为什么 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'.