具有并发读写的Mysql查询性能

Posted

技术标签:

【中文标题】具有并发读写的Mysql查询性能【英文标题】:Mysql query performance with concurrent read,write 【发布时间】:2018-08-10 05:31:29 【问题描述】:

我有一个简单的表格,有 15 列:

CREATE TABLE MYTABLE(
ID int(11) NOT NULL AUTO_INCREMENT,
SYMBOL varchar(100) NOT NULL,
DATE varchar(100) NOT NULL,
TIME varchar(100) NOT NULL,
NUMBER decimal(38,0) NOT NULL,
A float DEFAULT NULL,
B float DEFAULT NULL,
C float DEFAULT NULL,
D float DEFAULT NULL,
E decimal(38,0) DEFAULT NULL,
F float DEFAULT NULL,
G decimal(38,0) DEFAULT NULL,
H decimal(38,0) DEFAULT NULL,
I decimal(38,0) DEFAULT NULL,
J float DEFAULT NULL,
K float DEFAULT NULL,
L decimal(38,0) DEFAULT NULL,
M decimal(38,0) DEFAULT NULL,
MILLIS decimal(38,0) DEFAULT NULL,
PRIMARY KEY (ID)
KEY SYM (SYMBOL) USING HASH
) ENGINE=InnoDB AUTO_INCREMENT=10250241 DEFAULT CHARSET=latin1

由符号(哈希索引)索引。这张表(6GB)的数据大约有 10,000,000 行。当我在工作台中查询这个表时,对于一个简单的查询,比如:

select  *  from MYTABLE WHERE symbol = 'A' and date>= '2018-08-01' and 
date<= '2018-08-09' and time>= '09:24:00' and time <= '15:24:00' order by 
millis desc ;'

需要 4-5 秒。 当对数据库的读取和写入同时发生时,性能会进一步下降。但这是一个实时数据库,要求数据从一个连接写入并从另一个连接读取。

有人可以提出一些优化性能的方法吗?随着时间的推移,我已经尝试了 BTREE 索引,但性能进一步下降。

按照建议,在对我的查询进行解释后,我得到了以下结果:

'Using index condition; Using where; Using filesort'

【问题讨论】:

运行EXPLAIN SELECT ... 命令,并在此处发布结果。您可能需要一个涵盖多个字段的索引。在这种情况下,符号和日期。 (确保它是 ONE 索引,按顺序包含两个字段) 我添加了解释的结果。我将在我的数据库中拥有的日期数将始终不超过 15-30。我还应该索引它们吗?我还需要我的写作过程要快(因为它是实时的,每分钟都在市场数据上运行)。创建另一个索引可能会影响这一点。 转储您的原始索引,并将其替换为包含日期,然后让我知道您的更新结果。虽然索引可能会变大,但仅对符号进行查找仍应使用此索引,并大大加快您正在运行的查询的读取时间。 MILLIS 字段包含哪些数据?是对应“DATE”和“TIME”字段的UTC时间戳吗? 查询现在需要 3.5 秒,但我的数据加载过程变得慢得多。之前一分钟的数据需要大约 10 秒,现在需要 30 多秒。 【参考方案1】:
DATE varchar(100) NOT NULL,
TIME varchar(100) NOT NULL,

我会从使用DATE()TIME() 类型开始,而不是varchar(或一个DATETIME()) - 或者将它们存储在integer 中作为Unix Time。在内部,它们将比字符串更有效。

例如,比较两个整数大约需要 1 个 CPU 周期。为了比较字符串,一般来说,每个字符都必须在一个循环中进行比较(直到有差异),除非使用了特殊的优化。如果数据是 unicode 格式,则必须对每个字符进行特殊查找。

整数也比日期/时间字符串表示占用更少的空间(Unix 时间为 4 个字节),并且长度不是可变的(即使日期都是相同的长度,在内部它们将被视为可变长度字符串,需要一个额外的“长度字段”)。

还按照其他地方的建议创建适当的索引。

select * from MYTABLE 
where symbol = 'A' and
      date >= '2018-08-01' and date <= '2018-08-09' and 
      time >= '09:24:00' and time <= '15:24:00'
order by millis desc ;

您确定要(仅)通过millis 订购,还是只是为了测试?

对于上述查询,忽略对millis 的单独排序,理想情况下,记录将按以下顺序存储在磁盘上:symbol, datetime (millis?)。这样,要返回的记录将在磁盘上的块中紧密相连。否则它们可能会散布在整个表中,需要多次磁盘查找和(块)读取来检索所有记录。

【讨论】:

从比较两个 char(100) 字段切换到单个日期时间将是一个相当大的改进,但您必须意识到查询和更新 10M 行表不会立即进行,除非您有它背后的快速硬件。您可能希望了解如何获得更快的磁盘阵列和更多的内存。 @TerryCarmen 好吧,1000 万条记录并不多。至于选择记录:使用适当的索引应该是即时的(最多几毫秒)。至于检索(完整)记录:这完全取决于数据在磁盘上的组织方式。例如 6GB / 10M = 每条记录 600 字节。如果这些记录在磁盘上是连续的,则只需从磁盘读取 600*527=316kB + 一些开销,即使是便宜的磁盘也可以读取 100MB/s。但是,如果它们散布在整个桌子上,在最坏的情况下,每个 10 毫秒的磁盘寻道次数最多为 527 次...... @TerryCarmen ...如果花在查询数据上的时间更少并且锁被更快地释放,更新数据也会更快(或者至少会有更多的系统时间可用)。 【参考方案2】:

使用 desc 在符号和毫秒上创建索引。

【讨论】:

【参考方案3】:

对于这个查询:

select * 
from MYTABLE 
where symbol = 'A' and
      date >= '2018-08-01' and date <= '2018-08-09' and 
      time >= '09:24:00' and time <= '15:24:00'
order by millis desc ;

您希望在mytable(symbol, date, time) 上建立索引。实际上 time 只是作为副本存在,因此索引涵盖了 WHERE 子句。

包含millis 没有帮助,因为order by 之前需要过滤。

【讨论】:

【参考方案4】:

DECIMAL(38,0) 占用 17 个字节。你真的需要那种数据类型吗? (FLOAT 需要 4,DOUBLE 需要 8,BIGINT 需要 8。)(缩小 6GB 将有助于提高性能,尤其是在 innodb_buffer_pool_size 很小的情况下。)

如果millis 是毫秒,为什么它是 38 位而不是 3?无论如何,DATETIME(3) 提供了一个数据 + 时间 + 毫秒,全部包装成大约 7 个字节。此外,你可以这样做

ORDER BY datetime

从而允许高效的INDEX(symbol, datetime) 帮助处理WHERE。 (您当前的代码无法做到这一点。)

  date >= '2018-08-01' and date <= '2018-08-09' and 
  time >= '09:24:00' and time <= '15:24:00'

过滤到这 9 天中的每一天的白天。如果这确实是您想要的,那么没有索引可以正常工作。检查&lt;= 的使用——我理解日期的包容性,但我质疑它的时间。

INDEX(symbol, date)INDEX(symbol, time) 都有用。没有什么比这更好的了(除非你可以结合日期+时间)。添加这两个。

HASH InnoDB 中不存在索引;该请求被默默地转换为BTREE,这对于“点查询”差不多好,而对于“范围查询”则要好得多。在您的查询中,“复合”BTree 查询(参见上一段)要好得多。

请提供EXPLAIN SELECT ...,以便我们进一步推断发生了什么。

除了ID 之外的所有内容真的是可选的吗?考虑使用NOT NULL

'Using index condition; Using where; Using filesort' -- “文件排序”是不可避免的;忍受它。 “使用索引条件”(又名“ICP”)很好。

【讨论】:

以上是关于具有并发读写的Mysql查询性能的主要内容,如果未能解决你的问题,请参考以下文章

高并发与分布式系统的基石--数据库读写分离实战

比特 | MySQL 读写分离介绍及搭建

mysql性能优化之索引优化(转)

高性能MySQL卷一之架构分析

实现MySQL读写分离,MySQL性能调优

第183期1小时学会MySQL读写分离