我有一个简单的表,有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'
答案 0 :(得分:2)
DATE varchar(100) NOT NULL,
TIME varchar(100) NOT NULL,
我将从使用DATE()
和TIME()
类型而不是varchar
(或一个DATETIME()
)开始-或将它们存储为{{1 }}。在内部,它们将比字符串更有效。
例如,比较两个整数大约需要1个CPU周期。通常,要比较字符串,必须使用循环比较每个字符(直到有区别),除非使用特殊的优化。如果数据采用unicode,则必须对每个字符进行特殊查找。
整数还比日期/时间字符串表示占用更少的空间(Unix时间为4字节),并且长度也不可变(即使日期长度相同,在内部也将它们视为可变长度字符串,需要一个额外的“长度字段”)。
还要按照其他地方的建议创建适当的索引。
integer
您确定要(仅)按Unix Time
进行订购,还是仅作为测试?
对于上述查询,忽略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
。这样,要返回的记录将在磁盘上的块中紧密靠近。否则,它们可能会散布在整个表上,需要进行许多磁盘搜索和(块)读取才能获取所有记录。
答案 1 :(得分:0)
使用desc在符号和毫秒上创建索引。
答案 2 :(得分:0)
对于此查询:
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
之前需要进行过滤。
答案 3 :(得分:0)
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”)是好的。