如何优化MySQL中的大表,何时可以从分区中受益?

Nic*_*las 5 mysql indexing partitioning database-performance

总之,日期范围分区和内存配置实现了我的目标.

我需要增加分配给innodb_buffer_pool_size的内存,因为默认的8M太低了.Rick James推荐70%的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秒.

我还发现慢查询日志文件是识别问题的一个很好的工具,我在那里看到它表明我的innodb_buffer_pool_size很低并且突出了所有表现不佳的查询.这也确定了我需要索引其他表的区域.

编辑2016-11-12解决方案

我正在重构一个记录遥测数据的大型表,它已经运行了大约4-5个月并且已经产生了大约.平均行数约为5400万条记录.380个字节.

我已经开始看到我的一个原始数据查询的性能滞后,它会在24小时内返回设备的所有日志.

最初我认为它是索引,但我认为这是MySQL需要处理的I/O量.一个典型的24小时查询将包含2.2k 3k到9k记录,我实际上想支持大约7天的导出.

我没有数据库性能调优的经验,所以仍然只是学习绳索.我正在考虑一些策略.

  1. 根据查询原始数据调整复合索引,虽然我认为我的索引没问题,因为解释计划显示100%命中率.
  2. 考虑创建覆盖索引以包括所需的所有行
  3. 按日期实施远程分区: a)保留每月分区.例如,过去6个月 b) 将旧的任何东西移到归档表.
  4. 使用原始数据创建单独的表(垂直分区),并将其与主查询表的ID连接.我的索引正在运行,不确定这是我的问题.
  5. 更改我的查询以批量提取数据,然后按创建的日期限制X排序并继续,直到不再返回任何记录.
  6. 查看服务器配置

1,2(INDEXES): 我会用我的查询修改我的索引,但我认为我很好,因为Explain显示100%命中,除非我读错了.

我会在重建时尝试覆盖索引,但是如何确定设置错误的效果呢?EG插入速度受到影响.

如何在实时环境中最好地监控桌子的性能?

编辑:我刚刚开始使用慢速日志文件,它看起来像是一个很好的查找问题的工具,我想在performance_schema上查询可能是另一种选择?

3(PARTITIONING): 我已经阅读了一些关于分区的内容,并且不确定我的数据大小是否会产生很大的差异.

Rick James提出了大约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;
Run Code Online (Sandbox Code Playgroud)

DeviceID_AND_Created是主索引.我需要PK聚簇索引,因为我在摘要表中使用记录ID来跟踪给定设备的最后一条消息.创建将是分区列,所以我想这也会添加到PK群集?

查询:

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;
Run Code Online (Sandbox Code Playgroud)

这将返回给定24小时的所有日志,此时此时间约为.3k到9k行,平均行大小为381字节,一旦删除其中一个TEXT字段(原始),将减少

edl*_*erd 2

按日期实施范围分区: a) 保留每月分区。例如,过去 6 个月 b) 将任何较旧的内容移至存档表。

这是一个很好的主意。我猜想所有写入都将位于最新的分区中,并且您将仅查询最近的数据。您总是希望数据和索引适合内存。所以读取时没有磁盘 I/O。

根据您的使用情况,每周使用一个分区甚至可能是明智的。那么您只需在内存中保留最多两周的数据即可读取过去 7 天的数据。

如果您使用 innodb 作为引擎,您可能还需要调整缓冲区大小(即 innodb_buffer_pool_size),或者在使用 myisam 引擎时调整 myisam_key_cache。

另外,向数据库机器添加内存通常会有所帮助,因为操作系统可以将数据文件存储在内存中。

如果写入量很大,您还可以调整其他选项(即使用 innodb_log_buffer_size 将写入持久化到磁盘的频率)。这是为了让脏页在内存中保留更长时间,以避免过于频繁地将它们写回磁盘。