具有并发读写的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 天中的每一天的白天。如果这确实是您想要的,那么没有索引可以正常工作。检查<=
的使用——我理解日期的包容性,但我质疑它的时间。
INDEX(symbol, date)
或 INDEX(symbol, time)
都有用。没有什么比这更好的了(除非你可以结合日期+时间)。添加这两个。
HASH
InnoDB 中不存在索引;该请求被默默地转换为BTREE
,这对于“点查询”差不多好,而对于“范围查询”则要好得多。在您的查询中,“复合”BTree 查询(参见上一段)要好得多。
请提供EXPLAIN SELECT ...
,以便我们进一步推断发生了什么。
除了ID
之外的所有内容真的是可选的吗?考虑使用NOT NULL
。
'Using index condition; Using where; Using filesort'
-- “文件排序”是不可避免的;忍受它。 “使用索引条件”(又名“ICP”)很好。
【讨论】:
以上是关于具有并发读写的Mysql查询性能的主要内容,如果未能解决你的问题,请参考以下文章