Hor*_*ren 29 mysql indexing performance
我想知道什么是更高效和更快的性能:
在一个大表或多个没有索引的小表上有索引?
由于这是一个相当抽象的问题,让我让它变得更实用:
我有一个包含用户统计信息的表(20,000个用户和大约3000万行).该表有10列,包括user_id,actions,timestamps等
最常见的应用是:通过插入数据user_id和user_ID的检索数据(SELECT报表从不包含多个user_id's).
到目前为止,我有一个INDEXon user_id和查询看起来像这样
SELECT * FROM statistics WHERE user_id = 1
Run Code Online (Sandbox Code Playgroud)
现在,随着越来越多的行,表变得越来越慢.INSERT陈述减慢因为INDEX变得越来越大; SELECT语句慢下来,因为有更多行要搜索.
现在我想知道为什么没有为每个用户提供一个统计表,而是将查询语法更改为:
SELECT * FROM statistics_1
Run Code Online (Sandbox Code Playgroud)
哪里显然1代表user_id.
这样一来,没有INDEX需要,有远在每个表中的数据较少,所以INSERT并SELECT声明要快很多.
现在我再次提出问题:
处理这么多表(在我的情况下是20,000)而不是使用一个带有一个表的表是否有任何现实世界的缺点INDEX?
我的方法是否真的可以加快速度,或者查找表最终会减慢速度而不是一切?
Bil*_*win 82
创建20,000个表是个坏主意.不久之后你需要40,000个表,然后更多.
我在我的书SQL Antipatterns中称这种综合症为元数据Tribbles.每当您计划创建"每X表"或"每X列"时,就会发生这种情况.
当您拥有数万个表时,这确实会导致真正的性能问题.每个表都需要MySQL来维护内部数据结构,文件描述符,数据字典等.
还有实际的操作后果.您真的想要创建一个系统,要求您在每次新用户注册时创建新表吗?
相反,我建议你使用MySQL Partitioning.
这是分区表的一个例子:
CREATE TABLE statistics (
id INT AUTO_INCREMENT NOT NULL,
user_id INT NOT NULL,
PRIMARY KEY (id, user_id)
) PARTITION BY HASH(user_id) PARTITIONS 101;
Run Code Online (Sandbox Code Playgroud)
这为您提供了定义一个逻辑表的好处,同时还将表划分为多个物理表,以便在查询分区键的特定值时更快地访问.
例如,当您运行类似示例的查询时,MySQL只访问包含特定user_id的正确分区:
mysql> EXPLAIN PARTITIONS SELECT * FROM statistics WHERE user_id = 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: statistics
partitions: p1 <--- this shows it touches only one partition
type: index
possible_keys: NULL
key: PRIMARY
key_len: 8
ref: NULL
rows: 2
Extra: Using where; Using index
Run Code Online (Sandbox Code Playgroud)
分区的HASH方法意味着行通过整数分区键的模数放置在分区中.这意味着许多user_id映射到同一个分区,但每个分区的平均行数只有1/N(其中N是分区数).并且您使用恒定数量的分区定义表,因此您不必在每次获得新用户时对其进行扩展.
您可以选择任意数量的分区,最多1024个(或MySQL 5.6中的8192个分区),但有些人在报告这些分区时报告了性能问题.
建议使用素数分区.如果您的user_id值遵循模式(例如仅使用偶数),则使用素数分区有助于更均匀地分布数据.
在评论中回答你的问题:
我怎样才能确定合理数量的分区?
对于HASH分区,如果您使用101个分区,就像我在上面的示例中所示,那么任何给定的分区平均有大约1%的行.您说您的统计信息表有3000万行,因此如果您使用此分区,则每个分区只有300k行.MySQL更容易阅读.您也可以(也应该)使用索引 - 每个分区都有自己的索引,并且它只有整个未分区表上的索引的1%.
因此,如何确定合理数量的分区的答案是:您的整个表有多大,以及您希望分区平均有多大?
分区数量不应该随着时间的推移而增长吗?如果是这样:我该如何自动化?
如果使用HASH分区,则分区数不一定需要增长.最终你可能总共有300亿行,但我发现当你的数据量增长了几个数量级时,无论如何都需要一个新的架构.如果您的数据增长很大,则可能需要对多个服务器进行分片以及分区为多个表.
也就是说,您可以使用ALTER TABLE重新分区表:
ALTER TABLE statistics PARTITION BY HASH(user_id) PARTITIONS 401;
Run Code Online (Sandbox Code Playgroud)
这必须重组表(就像大多数ALTER TABLE更改一样),所以期望它需要一段时间.
您可能希望监视分区中的数据和索引的大小:
SELECT table_schema, table_name, table_rows, data_length, index_length
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE partition_method IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)
与任何表一样,您希望活动索引的总大小适合您的缓冲池,因为如果MySQL在SELECT查询期间必须将部分索引交换进缓冲池,性能会受到影响.
如果使用RANGE或LIST分区,则添加,删除,合并和拆分分区更为常见.请参阅http://dev.mysql.com/doc/refman/5.6/en/partitioning-management-range-list.html
我鼓励您阅读有关分区的手册部分,并查看这个漂亮的演示文稿:使用MySQL 5.1分区提升性能.
这可能取决于您计划经常进行的查询类型,确定的最佳方法是实现两者的原型并进行一些性能测试。
话虽如此,我希望带有索引的单个(大)表总体上会做得更好,因为大多数 DBMS 系统都经过了大量优化,可以处理查找数据并将数据插入到大表中的确切情况。如果您尝试制作许多小表以希望提高性能,那么您就在与优化器(通常更好)作斗争。
另外,请记住,一张桌子在未来可能更实用。如果您想获得所有用户的汇总统计信息怎么办?拥有 20 000 个表会使执行起来非常困难且效率低下。也值得考虑这些模式的灵活性。如果你这样划分你的桌子,你可能会把自己设计成未来的一个角落。