在主键索引的顺序中放置分区键的位置是否有一般规则?
Is there a general rule for where in the order of a primary-key index to place the partition key?
假设我在每个查询中都正确查询了分区键。是否有合理的理由将分区键放在除第一行以外的任何位置?
我觉得我对索引的工作原理有些不了解。假设 MySQL 和 InnoDB.
我想我明白了,通常情况下,您先放置最有选择性的键,然后放置选择性较差的键。分区键通常是选择性较低的键之一。但是,如果分区键包含在每个查询中,那么首先包含分区键有什么区别呢?这对其他方面也有帮助吗?例如,如果分区键位于主键索引的前面,我就不必在每个索引中都包含分区键:使用其他索引的查询可以从与最左键约束一致的主键索引中借用主键。
而且我不知道索引本身是否曾经分区过,但如果它是覆盖索引,它似乎可能是分区的。 (我说得对吗?)如果是这样,分区键必须是第一个,不,分区才能工作?
例如:
CREATE TABLE `fee` (
`fi` INT ,
`fo` INT ,
PRIMARY KEY ( `fi` , `fo` ) ,
) ENGINE = INNODB
PARTITION BY RANGE ( `fi` ) (
. . .
);
或者。 . .
CREATE TABLE `fee` (
`fi` INT ,
`fo` INT ,
PRIMARY KEY ( `fo` , `fi` ) ,
) ENGINE = INNODB
PARTITION BY RANGE ( `fi` ) (
. . .
);
如果有的话,哪个本质上更好,为什么或为什么不?
感谢您的宝贵时间。
两列的选择性并不像有些人想的那么重要
如果您要查询 table 为:
SELECT ... FROM fee WHERE fi=? AND fo=?
那么按fi,fo
还是按fo,fi
查找B树有什么关系呢?它最终会找到相同的记录,并且它会花费大致相同的步骤来做到这一点。理论上存在差异,但在大多数情况下不会产生显着差异。
更重要的是,如果您的查询只搜索主键的一列或另一列。
您提到所有查询都在分区列上搜索,在此示例中为 fi
。您是否有搜索 fi
而不是 fo
的查询?
SELECT ... FROM fee WHERE fi=?
如果 fi
是主键的第一列,这将进行分区修剪,并且还会使用 PRIMARY KEY 索引,因为您的搜索词在第一列。
mysql> explain partitions select * from fee where fi = 175;
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | fee | p2 | ref | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
而如果fi
是主键的第二列,那么它可以进行分区修剪,但不使用索引。
mysql> explain partitions select * from fee where fi = 175;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | fee | p2 | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
索引也是分区的。将分区视为一系列完全独立的 table,具有相同的列和相同的索引,只是行的一个子集。一旦查询确定要读取哪个分区,它就会按照与未分区 table 相同的方式进行查询,根据查询条件选择索引。会用主键搜索吗?
mysql> explain partitions select * from fee where fi = 175 and created_at < now();
+----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | fee | p2 | range | created_at | created_at | 6 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
这里我们看到 fi
上的条件导致分区修剪,但优化器更喜欢 created_at
上的索引。它在相应的分区中搜索该索引。
“您先放置最有选择性的键,然后放置选择性较差的键”——不,那是一个老掉牙的故事。
将使用“=”测试的键放在最前面是一个简单且更重要的规则。
将复合 InnoDB BTree 索引视为以这种方式工作。将所有列连接在一起,然后将 BTree 想象成具有单个字符串作为键。
索引中把“分区键”放在最前面是最没用的地方!你已经在修剪它了;将它放在索引中实际上是多余的。但是,对于任何唯一键(包括 `PRIMARY KEY')来说, 是必需的。
是的,您正确地观察到 PK 列隐式包含在每个辅助键中,因此包含分区键。
请注意,如果分区键实际上不是所需 UNIQUE
键的一部分,则唯一性约束是不可能的(在 MySQL 中)。但是,附加的 PK 不是唯一性约束的一部分。由于 MySQL 只愿意检查一个分区的唯一性,因此您必须包括分区键以提供在整个 table 中声明“唯一”的语义。 (是的,这有点令人费解;接受它。)
在您的示例中,如果您执行 SELECT .. WHERE fi BETWEEN 1 and 2
AND fo=3,任何索引(PK 是一个索引)starting with fi
都可以比 fo
在索引中 第一个 更难。
因此,经验法则是将分区键移动到包含它的任何索引的 end。 (我只见过一个罕见的例外;我忘记了细节。)
假设我在每个查询中都正确查询了分区键。是否有合理的理由将分区键放在除第一行以外的任何位置?
我觉得我对索引的工作原理有些不了解。假设 MySQL 和 InnoDB.
我想我明白了,通常情况下,您先放置最有选择性的键,然后放置选择性较差的键。分区键通常是选择性较低的键之一。但是,如果分区键包含在每个查询中,那么首先包含分区键有什么区别呢?这对其他方面也有帮助吗?例如,如果分区键位于主键索引的前面,我就不必在每个索引中都包含分区键:使用其他索引的查询可以从与最左键约束一致的主键索引中借用主键。
而且我不知道索引本身是否曾经分区过,但如果它是覆盖索引,它似乎可能是分区的。 (我说得对吗?)如果是这样,分区键必须是第一个,不,分区才能工作?
例如:
CREATE TABLE `fee` (
`fi` INT ,
`fo` INT ,
PRIMARY KEY ( `fi` , `fo` ) ,
) ENGINE = INNODB
PARTITION BY RANGE ( `fi` ) (
. . .
);
或者。 . .
CREATE TABLE `fee` (
`fi` INT ,
`fo` INT ,
PRIMARY KEY ( `fo` , `fi` ) ,
) ENGINE = INNODB
PARTITION BY RANGE ( `fi` ) (
. . .
);
如果有的话,哪个本质上更好,为什么或为什么不?
感谢您的宝贵时间。
两列的选择性并不像有些人想的那么重要
如果您要查询 table 为:
SELECT ... FROM fee WHERE fi=? AND fo=?
那么按fi,fo
还是按fo,fi
查找B树有什么关系呢?它最终会找到相同的记录,并且它会花费大致相同的步骤来做到这一点。理论上存在差异,但在大多数情况下不会产生显着差异。
更重要的是,如果您的查询只搜索主键的一列或另一列。
您提到所有查询都在分区列上搜索,在此示例中为 fi
。您是否有搜索 fi
而不是 fo
的查询?
SELECT ... FROM fee WHERE fi=?
如果 fi
是主键的第一列,这将进行分区修剪,并且还会使用 PRIMARY KEY 索引,因为您的搜索词在第一列。
mysql> explain partitions select * from fee where fi = 175;
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | fee | p2 | ref | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
而如果fi
是主键的第二列,那么它可以进行分区修剪,但不使用索引。
mysql> explain partitions select * from fee where fi = 175;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | fee | p2 | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
索引也是分区的。将分区视为一系列完全独立的 table,具有相同的列和相同的索引,只是行的一个子集。一旦查询确定要读取哪个分区,它就会按照与未分区 table 相同的方式进行查询,根据查询条件选择索引。会用主键搜索吗?
mysql> explain partitions select * from fee where fi = 175 and created_at < now();
+----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | fee | p2 | range | created_at | created_at | 6 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
这里我们看到 fi
上的条件导致分区修剪,但优化器更喜欢 created_at
上的索引。它在相应的分区中搜索该索引。
“您先放置最有选择性的键,然后放置选择性较差的键”——不,那是一个老掉牙的故事。
将使用“=”测试的键放在最前面是一个简单且更重要的规则。
将复合 InnoDB BTree 索引视为以这种方式工作。将所有列连接在一起,然后将 BTree 想象成具有单个字符串作为键。
索引中把“分区键”放在最前面是最没用的地方!你已经在修剪它了;将它放在索引中实际上是多余的。但是,对于任何唯一键(包括 `PRIMARY KEY')来说, 是必需的。
是的,您正确地观察到 PK 列隐式包含在每个辅助键中,因此包含分区键。
请注意,如果分区键实际上不是所需 UNIQUE
键的一部分,则唯一性约束是不可能的(在 MySQL 中)。但是,附加的 PK 不是唯一性约束的一部分。由于 MySQL 只愿意检查一个分区的唯一性,因此您必须包括分区键以提供在整个 table 中声明“唯一”的语义。 (是的,这有点令人费解;接受它。)
在您的示例中,如果您执行 SELECT .. WHERE fi BETWEEN 1 and 2
AND fo=3,任何索引(PK 是一个索引)starting with fi
都可以比 fo
在索引中 第一个 更难。
因此,经验法则是将分区键移动到包含它的任何索引的 end。 (我只见过一个罕见的例外;我忘记了细节。)