我需要增加分配给 innodb_buffer_pool_size 的内存,因为默认的8M太低了。 Rick James建议70% of RAM这个设置,他有很多很好的信息。
Edlerd的建议是正确的: - )
我将数据拆分为月分区,然后运行6,000行响应查询,最初需要6到12秒。它现在在不到一秒钟内完成(.984 / .031)。我使用默认的innodb缓冲区大小(innodb_buffer_pool_size = 8M)来运行它,以确保它不仅仅是内存增加。
然后我设置innodb_buffer_pool_size = 4G并以更好的响应运行查询.062 / .032。
我还想提一下,增加内存也提高了我的Web应用程序和服务的整体速度,它接收和写入消息到这个表,我很惊讶这个配置设置有多大区别。我的网络服务器的第一个字节时间(TTFB)现在几乎与MySQL Workbench相当,有时会达到20秒。
我还发现slow query log file是识别问题的一个很好的工具,我在那里看到它表明我的innodb_buffer_pool_size很低并且突出了所有表现不佳的查询。这也确定了我需要索引其他表的区域。
我正在重构一个记录遥测数据的大型表,它已经运行了大约4-5个月并且已经产生了大约。平均行数约为5400万条记录。 380字节。
我已经开始在我的一个原始数据查询中看到一些性能延迟,这些查询会在24小时内返回设备的所有日志。
最初我认为它是索引,但我认为这是MySQL需要处理的I / O量。一个典型的24小时查询将包含 2.2k 3k到9k记录,我实际上想支持大约7天的导出。
我对数据库性能调优没有经验,所以仍然只是学习绳索。我正在考虑一些策略。
1,2(INDEXES): 我会用我的查询重写我的索引,但我认为我很好,因为Explain显示100%命中,除非我读错了。
我会在重建时尝试覆盖索引,但是如何确定设置错误的效果呢?例如。插入速度受到影响。
如何在实时环境中最好地监控桌面的性能?
编辑:我刚刚开始使用slow log file,它看起来像是查找问题的好工具,我认为performance_schema上的查询可能是另一个问题选项?
3(PARTITIONING): 我已经阅读了一些关于分区的内容,并且不确定我的数据大小是否会产生很大影响。
Rick James suggests> 1M记录,我是54M,并希望在归档之前保留大约300M,我的桌子是否足够复杂?
我必须自己测试一下,因为我没有任何这方面的经验,这对我来说都是理论上的。如果它不适合我的需要,我只是不想走这条路。
4(通过'加入'细节表进行垂直分区):我不认为我有表扫描问题而且我需要所有行,所以我不确定这种技术会有什么好处
5(使用限制并再次获取):如果我在单个请求中使用较少的时间,是否可以释放服务器?我是否会在同一连接上以更多命令为代价看到更好的I / O吞吐量?
6(评论配置):另一部分是审核安装MySQL时使用的默认非开发人员配置,也许有一些设置可以调整? : - )
感谢阅读,热衷于听取任何和所有建议。
以下FYI:
表格
CREATE TABLE `message_log` (
`db_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`db_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created` datetime DEFAULT NULL,
`device_id` int(10) unsigned NOT NULL,
`display_name` varchar(50) DEFAULT NULL,
`ignition` binary(1) DEFAULT NULL COMMENT 'This is actually IO8 from the falcom device',
`sensor_a` float DEFAULT NULL,
`sensor_b` float DEFAULT NULL,
`lat` double DEFAULT NULL COMMENT 'default GPRMC format ddmm.mmmm \n',
`lon` double DEFAULT NULL COMMENT 'default GPRMC longitude format dddmm.mmmm ',
`heading` float DEFAULT NULL,
`speed` float DEFAULT NULL,
`pos_validity` char(1) DEFAULT NULL,
`device_temp` float DEFAULT NULL,
`device_volts` float DEFAULT NULL,
`satellites` smallint(6) DEFAULT NULL, /* TINYINT will suffice */
`navdist` double DEFAULT NULL,
`navdist2` double DEFAULT NULL,
`IO0` binary(1) DEFAULT NULL COMMENT 'Duress',
`IO1` binary(1) DEFAULT NULL COMMENT 'Fridge On/Off',
`IO2` binary(1) DEFAULT NULL COMMENT 'Not mapped',
`msg_name` varchar(20) DEFAULT NULL, /* Will be removed */
`msg_type` varchar(16) DEFAULT NULL, /* Will be removed */
`msg_id` smallint(6) DEFAULT NULL,
`raw` text, /* Not needed in primary query, considering adding to single table mapped to this ID or a UUID correlation ID to save on @ROWID query */
PRIMARY KEY (`db_id`),
KEY `Name` (`display_name`),
KEY `Created` (`created`),
KEY `DeviceID_AND_Created` (`device_id`,`created`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DeviceID_AND_Created是主要索引。我需要PK聚簇索引,因为我在摘要表中使用记录ID来跟踪给定设备的最后一条消息。创建的将是分区列,所以我想这也会被添加到PK集群中?
QUERY:
SELECT
ml.db_id, ml.db_created, ml.created, ml.device_id, ml.display_name, bin(ml.ignition) as `ignition`,
bin(ml.IO0) as `duress`, bin(ml.IO1) as `fridge`,ml.sensor_a, ml.sensor_b, ml.lat, ml.lon, ml.heading,
ml.speed,ml.pos_validity, ml.satellites, ml.navdist2, ml.navdist,ml.device_temp, ml.device_volts,ml.msg_id
FROM message_log ml
WHERE ml.device_id = @IMEI
AND ml.created BETWEEN @STARTDATE AND DATE_ADD(@STARTDATE,INTERVAL 24 hour)
ORDER BY ml.db_id;
这将返回给定24小时内的所有日志,此时此时间约为。 3k到9k行,平均行大小为381个字节,一旦我删除其中一个TEXT字段(原始)将减少
答案 0 :(得分:2)
按日期实施远程分区:a)保留每月分区。例如。最近6个月b)将旧的东西移到归档表。
这是非常好主意。我猜所有的写入都将在最新的分区中,您将只查询最近的数据。您总是希望数据和索引适合内存。因此读取时没有磁盘i / o。
根据您的使用情况,每周一个分区甚至可能是明智之举。然后,您只需要在内存中保留最多两周的数据,以便在过去7天内阅读。
如果您在使用myisam引擎时使用innodb作为引擎或myisam_key_cache,您可能还想调整缓冲区大小(即innodb_buffer_pool_size)。
同时将ram添加到数据库计算机通常会有所帮助,因为操作系统可以将数据文件存储在内存中。
如果您有大量写入,您还可以调整其他选项(即使用innodb_log_buffer_size将写入持久保存到磁盘的频率)。这是为了让脏页在内存中的时间更长,以避免过于频繁地将它们写回磁盘。
答案 1 :(得分:1)
对于那些好奇的人,以下是我用来创建分区和配置内存的内容。
更新了PK以包含分区
中使用的范围列ALTER TABLE message_log
CHANGE COLUMN created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
DROP PRIMARY KEY,
ADD PRIMARY KEY (db_id, created);
使用ALTER TABLE添加分区。
事后看来,我应该将每个分区创建为单个ALTER语句,并在后续分区上使用Reorganize Partition(和here),因为在一次命中中执行它会占用大量资源和时间。 / p>
ALTER TABLE message_log
PARTITION BY RANGE(to_days(created)) (
partition invalid VALUES LESS THAN (0),
partition from201607 VALUES LESS THAN (to_days('2016-08-01')),
partition from201608 VALUES LESS THAN (to_days('2016-09-01')),
partition from201609 VALUES LESS THAN (to_days('2016-10-01')),
partition from201610 VALUES LESS THAN (to_days('2016-11-01')),
partition from201611 VALUES LESS THAN (to_days('2016-12-01')),
partition from201612 VALUES LESS THAN (to_days('2017-01-01')),
partition from201701 VALUES LESS THAN (to_days('2017-02-01')),
partition from201702 VALUES LESS THAN (to_days('2017-03-01')),
partition from201703 VALUES LESS THAN (to_days('2017-04-01')),
partition from201704 VALUES LESS THAN (to_days('2017-05-01')),
partition future values less than (MAXVALUE)
);
注意:我不确定使用to_days()或原始列是否会产生很大的不同,但我已经看到它在大多数示例中使用过,所以我把它作为假设最好的实践。
要更改 innodb_db_buffer_pool_size 的值,您可以找到以下信息: MySQL InnoDB Buffer Pool Resize和Rick Jame's page on memory
您也可以在 选项文件 菜单中的MySQL Workbench中执行此操作,然后使用innoDB选项卡。您在此处所做的任何更改都将写入配置文件中,但您需要停止并启动MySQL以读取配置,否则您还可以设置全局值以进行实时更新。
答案 2 :(得分:1)
这样的交易!即使没有写评论或回答,我也会得到4次提及。我正在写一个答案,因为我可能会有一些进一步的改进......
是的,PARTITION BY RANGE(TO_DAYS(...))
是正确的方法。 (可能有小个替代品。)
4GB内存中有70%是紧张的。确保没有交换。
您提到了一个查询。如果它是主要的问题,那么这会稍微好一些:
PRIMARY KEY(device_id, created, db_id), -- desired rows will be clustered
INDEX(db_id) -- to keep AUTO_INCREMENT happy
如果您没有清除旧数据,那么即使没有分区,上述关键建议也能提供相同的效率。
lat/lon representation说DOUBLE
有点矫枉过正。
小心inefficiency of UUID,特别是对于大桌子。