mysql 数据库组合键问题
mysql database composite key issues
我们有一个数据库,其中的数据 table 定义如下:
CREATE TABLE data
(
msts BIGINT,
variable_id INT,
p_id INT,
value DOUBLE,
PRIMARY KEY(msts,variable_id,p_id)
);
ALTER TABLE data
ADD FOREIGN KEY (p_id)
REFERENCES p(id);
ALTER TABLE data
ADD FOREIGN KEY (variable_id)
REFERENCES variables(id);
这个table可以包含数十亿条记录。
进行简单查询时:
SELECT COUNT(msts) from data FORCE INDEX(PRIMARY) where (
msts<1535886000000000000 AND msts>1535796060000000000 AND
variable_id=107 AND p_id=661 );
产量:
+-------------+
| COUNT(msts) |
+-------------+
| 89873 |
+-------------+
1 row in set (42.51 sec)
需要 42.51 秒来计算 89873。
为什么要花这么长时间才能看到主键应该作为复合索引?
这是解释:
EXPLAIN SELECT COUNT(msts) from data FORCE INDEX(PRIMARY) where
( msts<1535886000000000000 AND msts>1535796060000000000 AND
variable_id=107 AND plant_id=661 );
给出:
rows = 190996998
filtered=0
ref=NULL
type=range
如有任何帮助,我们将不胜感激!
你的查询,重写以改变 where 子句的顺序,是这样的。
SELECT COUNT(msts)
from data
where variable_id=107
and p_id=661
and msts>1535796060000000000
and msts<1535886000000000000;
它包含两个等式匹配,分别在 variable_id 和 p_id 上。然后它在 msts 上包含一个范围过滤器。因此,您需要 (variable_id, p_id, msts)
列 的索引 以帮助快速满足您的查询。
为什么?您可以认为 MySQL 索引是按顺序排序的。为了满足您的查询,MySQL 随机访问第一个符合条件的项目的索引。然后按顺序扫描它直到最后一个项目。这称为索引范围扫描。
您预先存在的索引首先列出 msts。这意味着您的索引无法按顺序扫描,因为索引中的每个 msts 值都可能包含其他两列的许多值。
专业提示 1: 尽可能使用 COUNT(*)
而不是 COUNT(column)
。第二个较慢,因为它必须忽略任何为 NULL 的列值。第一个就把他们都数了。
专业提示 2: 额外的单列索引没有用,除非它们有助于加速特定查询。
专业提示 3: 强制使用索引几乎总是一个糟糕的选择。
专业提示 4: 阅读 https://use-the-index-luke.com/
编辑:您问的是如何进行转换。
如果您的 table 还没有包含数百万行,只需像这样更改主键定义。
ALTER TABLE data
DROP PRIMARY KEY,
ADD PRIMARY KEY (variable_id, p_id, msts);
如果它已经包含数十亿行,您可能应该使用正确的定义创建一个新的 table,将现有的 table 复制到其中。然后复制你的数据。然后把旧的table改名为data_old
什么的,把新的table改名为data
。这可能是一项涉及批量数据的复杂任务;如果你想不通,再问一个问题。
我们有一个数据库,其中的数据 table 定义如下:
CREATE TABLE data
(
msts BIGINT,
variable_id INT,
p_id INT,
value DOUBLE,
PRIMARY KEY(msts,variable_id,p_id)
);
ALTER TABLE data
ADD FOREIGN KEY (p_id)
REFERENCES p(id);
ALTER TABLE data
ADD FOREIGN KEY (variable_id)
REFERENCES variables(id);
这个table可以包含数十亿条记录。
进行简单查询时:
SELECT COUNT(msts) from data FORCE INDEX(PRIMARY) where (
msts<1535886000000000000 AND msts>1535796060000000000 AND
variable_id=107 AND p_id=661 );
产量:
+-------------+
| COUNT(msts) |
+-------------+
| 89873 |
+-------------+
1 row in set (42.51 sec)
需要 42.51 秒来计算 89873。
为什么要花这么长时间才能看到主键应该作为复合索引?
这是解释:
EXPLAIN SELECT COUNT(msts) from data FORCE INDEX(PRIMARY) where
( msts<1535886000000000000 AND msts>1535796060000000000 AND
variable_id=107 AND plant_id=661 );
给出:
rows = 190996998
filtered=0
ref=NULL
type=range
如有任何帮助,我们将不胜感激!
你的查询,重写以改变 where 子句的顺序,是这样的。
SELECT COUNT(msts)
from data
where variable_id=107
and p_id=661
and msts>1535796060000000000
and msts<1535886000000000000;
它包含两个等式匹配,分别在 variable_id 和 p_id 上。然后它在 msts 上包含一个范围过滤器。因此,您需要 (variable_id, p_id, msts)
列 的索引 以帮助快速满足您的查询。
为什么?您可以认为 MySQL 索引是按顺序排序的。为了满足您的查询,MySQL 随机访问第一个符合条件的项目的索引。然后按顺序扫描它直到最后一个项目。这称为索引范围扫描。
您预先存在的索引首先列出 msts。这意味着您的索引无法按顺序扫描,因为索引中的每个 msts 值都可能包含其他两列的许多值。
专业提示 1: 尽可能使用 COUNT(*)
而不是 COUNT(column)
。第二个较慢,因为它必须忽略任何为 NULL 的列值。第一个就把他们都数了。
专业提示 2: 额外的单列索引没有用,除非它们有助于加速特定查询。
专业提示 3: 强制使用索引几乎总是一个糟糕的选择。
专业提示 4: 阅读 https://use-the-index-luke.com/
编辑:您问的是如何进行转换。
如果您的 table 还没有包含数百万行,只需像这样更改主键定义。
ALTER TABLE data
DROP PRIMARY KEY,
ADD PRIMARY KEY (variable_id, p_id, msts);
如果它已经包含数十亿行,您可能应该使用正确的定义创建一个新的 table,将现有的 table 复制到其中。然后复制你的数据。然后把旧的table改名为data_old
什么的,把新的table改名为data
。这可能是一项涉及批量数据的复杂任务;如果你想不通,再问一个问题。