如果有人能够解释MySQL是如何在默认配置中使用大型表格进行搅拌的话,我将不胜感激。
注意:我不需要建议如何增加内存,提高性能或迁移等。我想了解它为什么运行良好并且表现良好。
我有下表:
CREATE TABLE `daily_reads` (
`a` varchar(32) NOT NULL DEFAULT '',
`b` varchar(50) NOT NULL DEFAULT '',
`c` varchar(20) NOT NULL DEFAULT '',
`d` varchar(20) NOT NULL DEFAULT '',
`e` varchar(20) NOT NULL DEFAULT '',
`f` varchar(10) NOT NULL DEFAULT 'Wh',
`g` datetime NOT NULL,
`PERIOD_START` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`i` decimal(16,3) NOT NULL,
`j` decimal(16,3) NOT NULL DEFAULT '0.000',
`k` decimal(16,2) NOT NULL DEFAULT '0.00',
`l` varchar(1) NOT NULL DEFAULT 'N',
`m` varchar(1) NOT NULL DEFAULT 'N',
PRIMARY KEY (`a`,`b`,`c`,`PERIOD_START`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
它运行在具有1个CPU核心,6GB RAM,CentOS 7的VM上(对该VM的访问权限非常有限)。
它运行在具有128MB缓冲池(SELECT @@innodb_buffer_pool_size/1024/1024
)
数据库大小约为96GB,“读取”表中约为560M行,其他表约为710M行。
select database_name, table_name, index_name, stat_value*@@innodb_page_size
from mysql.innodb_index_stats where stat_name='size';
主要:83,213,500,416(没有其他指数)
我得到〜/ 500K读/月,写入仅作为ETL过程的一部分直接从Informatica到DB(每月约75M写入)。
只能通过存储过程调用读取查询:
CALL sp_get_meter_data('678912345678', '1234567765432', '2017-01-13 00:00:00', '2017-05-20 00:00:00');
// striped out the not important bits:
...
SET daily_from_date = DATE_FORMAT(FROM_DATE_TIME, '%Y-%m-%d 00:00:00');
SET daily_to_date = DATE_FORMAT(TO_DATE_TIME, '%Y-%m-%d 23:59:59');
...
SELECT
*
FROM
daily_reads
WHERE
A = FRIST_NUMBER
AND
B = SECOND_NUMBER
AND
daily_from_date <= PERIOD_START
AND
daily_to_date >= PERIOD_START
ORDER BY
PERIOD_START ASC;
我对InnoDB的理解非常有限,但我认为我需要将所有索引都安装到内存中以进行快速查询。读取过程只需几毫秒。我认为在默认的MySQL配置上足够快地查询500M +表在技术上是不可能的......?
我缺少什么?
注意:我不需要建议如何增加内存,提高性能或迁移等。我想了解它为什么运行良好并且表现良好。
答案 0 :(得分:1)
答案很长:您的主键是由a
和b
开头的多个列的组合。
您的WHERE
条款说明了这一点。
WHERE a = FRIST_NUMBER
AND b = SECOND_NUMBER
AND etc etc.
这个WHERE
子句非常有效地利用与主键关联的索引。它随机地将索引访问到它所需的第一行,然后按顺序扫描它。因此,它实际上不必在您的索引或表格中进行分页以满足您的查询。
简短回答:当查询利用索引时,MySQL速度快且便宜。
如果您希望此查询的完美索引,它将是(a, b, daily_from_date)
上的复合索引。这将使用相等匹配来命中索引中的第一个匹配行,然后范围扫描所选日期范围的索引。但你现在的表现非常好。
您询问索引是否必须完全适合内存。不可以.DBMS软件的全部用途用于处理一次可能无法容纳在内存中的大量数据。良好的DBMS实现可以很好地维护内存缓存,并在需要时从大容量存储中刷新这些缓存。 innodb缓冲池就是这样一个缓存。请记住,对表的任何插入或更新都需要将表数据和索引数据最终写入大容量存储。
答案 1 :(得分:0)
一些指数可以改善表现。
在您的特定情况下,您要过滤3列:A,B和PERIOD_START。 要加快查询速度,可以在此列上使用索引。
在PERIOD_START上添加索引可能效率低下,因为此类型存储TIME信息,因此您在同一天有很多不同的值。
您可以添加一个新列,以正确的类型(DATE)(例如PERIOD_START_DATE)存储PERIOD_START的DATE部分,并在此列上添加索引。
这使索引更有效,这可以提高计算性能,因为您使用的是查找表(键 - &gt;值)。
如果您不想更改客户端代码,可以使用&#34;生成存储列&#34;。 See MySql manual
祝你好运
答案 2 :(得分:0)
可能你的索引被使用(可能没有给出前沿与查询中的列不匹配),但即使它不是,你也只读过一次因为查询没有任何连接,后续运行会选择缓存的结果。
由于您正在使用informatica来加载数据(它是数据加载的瑞士军刀),因此它可能比您意识到的要多得多。假设数据加载是所有插入,那么它可能会丢弃并重新创建索引并以批量模式运行以快速加载数据。它甚至可以预先运行查询以使用第一次后加载运行来填充缓存。
答案 3 :(得分:0)
索引是否必须适合内存?
不,整个索引不必适合内存。在查询执行期间,只需要检查的索引部分。
由于主键的最左侧列(您是聚簇索引)具有条件,因此查询仅检查与您搜索的值匹配的行。表的其余部分根本没有检查过。
您可以尝试在查询中使用EXPLAIN,并查看已检查行数的估算值。这只是优化器计算的粗略估计,但它应该表明您的查询只需要检查5.5亿行的一小部分。
InnoDB缓冲池将常用页面的副本保存在RAM中。页面使用频率越高,就越有可能留在缓冲池中而不会被踢出。随着时间的推移,当您运行查询时,缓冲池会随着最值得保留在RAM中的页面集逐渐稳定。
如果您的查询工作负载要经常扫描整个表,那么小缓冲池会流失更多。但是,您的查询可能会反复请求表的相同小部分。一种称为Pareto Principle的现象适用于许多现实世界的应用程序:大多数请求都是由少数数据满足。
当我们运行复杂的分析查询时,这个原则往往会失败,因为这些查询更有可能扫描整个表。