为什么 mysql 分区对我的情况没有影响
Why mysql partitioning has no effect in my case
我尝试在 Mysql
中测试分区的好处
我创建了两个 table:一个分区,另一个不分区。
每个table里面有10M条记录。
我想通过 "user_to_id" 列进行快速查询。
分区table(1024个部分):
CREATE TABLE `neworder10M_part_byuser` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`site_from_id` int(11) NOT NULL,
`site_to_id` int(11) NOT NULL,
`user_from_id` int(11) NOT NULL,
`user_to_id` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`,`user_to_id`),
KEY `composite_cover` (`user_to_id`,`user_from_id`,`site_from_id`,`site_to_id`,`created`)
) ENGINE=InnoDB
/*!50100 PARTITION BY HASH (user_to_id)
PARTITIONS 1024 */ |
Table 带聚簇键(未分区):
CREATE TABLE `neworder_10M` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`site_from_id` int(11) NOT NULL,
`site_to_id` int(11) NOT NULL,
`user_from_id` int(11) NOT NULL,
`user_to_id` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`user_to_id`,`id`),
UNIQUE KEY `id_UQ` (`id`)
) ENGINE=InnoDB;
当我用 python 脚本对两个 table 进行基准测试时 1000 个请求:
for i in xrange(1,REQS):
user_id = random.randint(1,10000);
cursor.execute("select * from neworder10M_part_byuser where user_to_id=%s;" % (user_id))
已分区 table:22 rps
未分区:22.7 rps
为什么分区 table 没有速度优势?正如我所期望的那样,较小的数据 - 更快的查询。
并且解释还显示使用的分区:
mysql> explain select * from neworder10M_part_byuser where user_to_id=6867;
+----+-------------+-------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | neworder10M_part_byuser | p723 | ref | composite_cover | composite_cover | 4 | const | 1009 | 100.00 | Using index |
+----+-------------+-------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
但我在现实中并没有看到真正的速度提高....我做错了什么?
Tables 填充码:
def send_orders(cur,users=10000,orders=10000000):
for i in xrange(1,orders+1): //10000000 rows here
print i
from_user = random.randint(1,users)
to_user = random.randint(1,users)
from_site = random.randint(1,10000)
to_site = random.randint(1,10000)
cur.execute("INSERT INTO neworder (site_from_id, site_to_id,user_from_id, user_to_id,created) VALUES ('%d','%d','%d','%d',NOW());" % (from_user,to_user,from_site,to_site))
mysql 版本:Ver 14.14 Distrib 5.7.12,适用于 Linux (x86_64)。
硬盘是ssd.
我们预计 SELECT 语句的性能不会有太大差异,因为查询正在使用索引范围扫描,并且分区 table 的查询正在修剪分区。
如果不进行分区修剪,我们预计 较慢 的性能 SELECT 与分区 table 相比。因为那将是需要检查的 1024 个索引而不是一个索引。
分区提高查询性能的想法是一个谬论。
(这既是对问题的回答,也是对部分评论的反驳。)
如果您的 WHERE
子句可以导致分区修剪发生,那么它可以帮助提高复合索引的效率。因此,与非分区 table 相比没有优势,因为您可以选择更好的索引。
将分区修剪视为将 BTree 深度缩小 1 级。但是你必须做修剪。结果:几乎相同的成本。这是我对"range scan on 10M rows unpartitioned vs 10K rows in one partition"问题的回答。 (@spencer7593 的回答也很好。)
我发现只有 4 个用例 PARTITIONing
提高了性能。 my blog.
中有
BY RANGE
是唯一有用的分区方法。 BY HASH
,您正在使用的,似乎完全没有用。特别是,如果您对 'partition key' 进行范围扫描,它必然会扫描 所有 分区——不可能 'pruning'。
将分区键放在任何键的前面通常是低效的。
UNIQUE KEY id_UQ (id)
-- 为你的非分区测试做一个简单的 INDEX
;它会更有效率。它足以处理 AUTO_INCREMENT
.
(哎呀,@spencer7593 已经说了一些,并指向我的博客。谢谢。我写它是因为我厌倦了在论坛上重复自己的话。)
您的特定查询 (SELECT ... WHERE user_to_id = constant
) 是证明 PARTITIONing
(任何类型的)无用的好方法。那是你真正的查询吗?您实际上可能还有其他一些可以从分区中受益的查询;让我们看看他们。
“在更小的 table 上快 50 倍”——缓存?是否较小的 table 适合 buffer_pool 而较大的不适合? I/O 是影响性能的最大因素。
如果 WHERE user_to_id = constant
始终在您的查询中,则将 user_to_id
作为非分区 table 中每个索引(INDEX(id)
除外)的第一列。将其视为等同于 'pruning'.
我尝试在 Mysql
中测试分区的好处我创建了两个 table:一个分区,另一个不分区。
每个table里面有10M条记录。
我想通过 "user_to_id" 列进行快速查询。
分区table(1024个部分):
CREATE TABLE `neworder10M_part_byuser` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`site_from_id` int(11) NOT NULL,
`site_to_id` int(11) NOT NULL,
`user_from_id` int(11) NOT NULL,
`user_to_id` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`,`user_to_id`),
KEY `composite_cover` (`user_to_id`,`user_from_id`,`site_from_id`,`site_to_id`,`created`)
) ENGINE=InnoDB
/*!50100 PARTITION BY HASH (user_to_id)
PARTITIONS 1024 */ |
Table 带聚簇键(未分区):
CREATE TABLE `neworder_10M` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`site_from_id` int(11) NOT NULL,
`site_to_id` int(11) NOT NULL,
`user_from_id` int(11) NOT NULL,
`user_to_id` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`user_to_id`,`id`),
UNIQUE KEY `id_UQ` (`id`)
) ENGINE=InnoDB;
当我用 python 脚本对两个 table 进行基准测试时 1000 个请求:
for i in xrange(1,REQS):
user_id = random.randint(1,10000);
cursor.execute("select * from neworder10M_part_byuser where user_to_id=%s;" % (user_id))
已分区 table:22 rps 未分区:22.7 rps
为什么分区 table 没有速度优势?正如我所期望的那样,较小的数据 - 更快的查询。
并且解释还显示使用的分区:
mysql> explain select * from neworder10M_part_byuser where user_to_id=6867;
+----+-------------+-------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | neworder10M_part_byuser | p723 | ref | composite_cover | composite_cover | 4 | const | 1009 | 100.00 | Using index |
+----+-------------+-------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
但我在现实中并没有看到真正的速度提高....我做错了什么?
Tables 填充码:
def send_orders(cur,users=10000,orders=10000000):
for i in xrange(1,orders+1): //10000000 rows here
print i
from_user = random.randint(1,users)
to_user = random.randint(1,users)
from_site = random.randint(1,10000)
to_site = random.randint(1,10000)
cur.execute("INSERT INTO neworder (site_from_id, site_to_id,user_from_id, user_to_id,created) VALUES ('%d','%d','%d','%d',NOW());" % (from_user,to_user,from_site,to_site))
mysql 版本:Ver 14.14 Distrib 5.7.12,适用于 Linux (x86_64)。 硬盘是ssd.
我们预计 SELECT 语句的性能不会有太大差异,因为查询正在使用索引范围扫描,并且分区 table 的查询正在修剪分区。
如果不进行分区修剪,我们预计 较慢 的性能 SELECT 与分区 table 相比。因为那将是需要检查的 1024 个索引而不是一个索引。
分区提高查询性能的想法是一个谬论。
(这既是对问题的回答,也是对部分评论的反驳。)
如果您的 WHERE
子句可以导致分区修剪发生,那么它可以帮助提高复合索引的效率。因此,与非分区 table 相比没有优势,因为您可以选择更好的索引。
将分区修剪视为将 BTree 深度缩小 1 级。但是你必须做修剪。结果:几乎相同的成本。这是我对"range scan on 10M rows unpartitioned vs 10K rows in one partition"问题的回答。 (@spencer7593 的回答也很好。)
我发现只有 4 个用例 PARTITIONing
提高了性能。 my blog.
BY RANGE
是唯一有用的分区方法。 BY HASH
,您正在使用的,似乎完全没有用。特别是,如果您对 'partition key' 进行范围扫描,它必然会扫描 所有 分区——不可能 'pruning'。
将分区键放在任何键的前面通常是低效的。
UNIQUE KEY id_UQ (id)
-- 为你的非分区测试做一个简单的 INDEX
;它会更有效率。它足以处理 AUTO_INCREMENT
.
(哎呀,@spencer7593 已经说了一些,并指向我的博客。谢谢。我写它是因为我厌倦了在论坛上重复自己的话。)
您的特定查询 (SELECT ... WHERE user_to_id = constant
) 是证明 PARTITIONing
(任何类型的)无用的好方法。那是你真正的查询吗?您实际上可能还有其他一些可以从分区中受益的查询;让我们看看他们。
“在更小的 table 上快 50 倍”——缓存?是否较小的 table 适合 buffer_pool 而较大的不适合? I/O 是影响性能的最大因素。
如果 WHERE user_to_id = constant
始终在您的查询中,则将 user_to_id
作为非分区 table 中每个索引(INDEX(id)
除外)的第一列。将其视为等同于 'pruning'.