创建 20,000 个表是个坏主意。不久之后,您将需要 40,000 张桌子,甚至更多。
我在我的书 SQL Antipatterns 中称这种综合症为 Metadata 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;
这为您提供了定义一个逻辑表的好处,同时还将表划分为许多物理表,以便在您查询分区键的特定值时更快地访问。
例如,当您像示例一样运行查询时,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
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;
这必须重组表(就像大多数 ALTER TABLE 更改一样),所以预计需要一段时间。
您可能想要监控分区中数据和索引的大小:
SELECT table_schema, table_name, table_rows, data_length, index_length
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE partition_method IS NOT NULL;
与任何表一样,您希望活动索引的总大小适合您的缓冲池,因为如果 MySQL 在 SELECT 查询期间必须将部分索引交换进出缓冲池,则性能会受到影响。
如果您使用 RANGE 或 LIST 分区,则添加、删除、合并和拆分分区更为常见。见http://dev.mysql.com/doc/refman/5.6/en/partitioning-management-range-list.html
我鼓励您阅读manual section on partitioning,并查看这个精彩的演示文稿:Boost Performance With MySQL 5.1 Partitions。