【问题标题】:Optimizing query in the MySQL slow-query log优化 MySQL 慢查询日志中的查询
【发布时间】:2011-11-07 17:04:24
【问题描述】:

我们的数据库设置为拥有一个credentials 表,其中包含多种不同类型的凭据(登录名等)。还有一个credential_pairs 表将其中一些类型关联在一起(例如,用户可能拥有密码和安全令牌)。

为了查看一对是否匹配,有以下查询:

SELECT DISTINCT cp.credential_id FROM credential_pairs AS cp
INNER JOIN credentials AS c1 ON (cp.primary_credential_id   = c1.credential_id)
INNER JOIN credentials AS c2 ON (cp.secondary_credential_id = c2.credential_id)
WHERE c1.data = AES_ENCRYPT('Some Value 1', 'encryption key')
AND   c2.data = AES_ENCRYPT('Some Value 2', 'encryption key');

此查询运行良好,并为我们提供了我们所需要的准确信息。但是,它不断显示在慢查询日志中(可能是由于缺少索引?)。当我要求 MySQL“解释”它给我的查询时:

+----+-------------+-------+------+--------------------------------------------------------+---------------------+---------+-------+-------+--------------------------------+
| id | select_type | table | type | possible_keys                                          | key                 | key_len | ref   | rows  | Extra                          |
+----+-------------+-------+------+--------------------------------------------------------+---------------------+---------+-------+-------+--------------------------------+
|  1 | SIMPLE      | c1    | ref  | credential_id_UNIQUE,credential_id,ix_credentials_data | ix_credentials_data | 22      | const |     1 | Using where; Using temporary   |
|  1 | SIMPLE      | c2    | ref  | credential_id_UNIQUE,credential_id,ix_credentials_data | ix_credentials_data | 22      | const |     1 | Using where                    |
|  1 | SIMPLE      | cp    | ALL  | NULL                                                   | NULL                | NULL    | NULL  | 69197 | Using where; Using join buffer |
+----+-------------+-------+------+--------------------------------------------------------+---------------------+---------+-------+-------+--------------------------------+

我感觉最后一个条目(显示 69197 行)可能是问题所在,但我与 DBA 相差甚远...帮助?


凭证表:

CREATE TABLE  `credentials` (
  `hidden_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `credential_id` varchar(255) NOT NULL,
  `data` blob NOT NULL,
  `credential_status` varchar(100) NOT NULL,
  `insert_date` datetime NOT NULL,
  `insert_user` int(10) unsigned NOT NULL,
  `update_date` datetime DEFAULT NULL,
  `update_user` int(10) unsigned DEFAULT NULL,
  `delete_date` datetime DEFAULT NULL,
  `delete_user` int(10) unsigned DEFAULT NULL,
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`hidden_id`,`credential_id`),
  UNIQUE KEY `credential_id_UNIQUE` (`credential_id`),
  KEY `credential_id` (`credential_id`),
  KEY `data` (`data`(10)),
  KEY `credential_status` (`credential_status`(10))
) ENGINE=InnoDB AUTO_INCREMENT=1572 DEFAULT CHARSET=utf8;

credential_pairs 表:

CREATE TABLE  `credential_pairs` (
  `hidden_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `credential_id` varchar(255) NOT NULL,
  `primary_credential_id` varchar(255) NOT NULL,
  `secondary_credential_id` varchar(255) NOT NULL,
  `is_deleted` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`hidden_id`,`credential_id`),
  KEY `primary_credential_id` (`primary_credential_id`(10)),
  KEY `secondary_credential_id` (`secondary_credential_id`(10))
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=latin1;

凭证索引:

+-------------+------------+----------------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+
| Table       | Non_unique | Key_name             | Seq_in_index | Column_name   | Collation | Cardinality | Sub_part | Packed | Null | Index_type     | Comment |
+-------------+------------+----------------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+
| credentials |          0 | PRIMARY              |            1 | hidden_id     | A         |      186235 |     NULL | NULL   |      | BTREE      |         |
| credentials |          0 | PRIMARY              |            2 | credential_id | A         |      186235 |     NULL | NULL   |      | BTREE      |         |
| credentials |          0 | credential_id_UNIQUE |            1 | credential_id | A         |      186235 |     NULL | NULL   |      | BTREE      |         |
| credentials |          1 | credential_id        |            1 | credential_id | A         |      186235 |     NULL | NULL   |      | BTREE      |         |
| credentials |          1 | ix_credentials_data  |            1 | data          | A         |      186235 |       20 | NULL   |      | BTREE      |         |
+-------------+------------+----------------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+

credential_pair 索引:

+------------------+------------+---------------------------------------------+--------------+-------------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table            | Non_unique | Key_name                                    | Seq_in_index | Column_name             | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+------------------+------------+---------------------------------------------+--------------+-------------------------+-----------+-------------+----------+--------+------+------------+---------+
| credential_pairs |          0 | PRIMARY                                     |            1 | hidden_id               | A         |       69224 |     NULL | NULL   |      | BTREE      |         |
| credential_pairs |          0 | PRIMARY                                     |            2 | credential_id           | A         |       69224 |     NULL | NULL   |      | BTREE      |         |
| credential_pairs |          1 | ix_credential_pairs_credential_id           |            1 | credential_id           | A         |       69224 |       36 | NULL   |      | BTREE      |         |
| credential_pairs |          1 | ix_credential_pairs_primary_credential_id   |            1 | primary_credential_id   | A         |       69224 |       36 | NULL   |      | BTREE      |         |
| credential_pairs |          1 | ix_credential_pairs_secondary_credential_id |            1 | secondary_credential_id | A         |       69224 |       36 | NULL   |      | BTREE      |         |
+------------------+------------+---------------------------------------------+--------------+-------------------------+-----------+-------------+----------+--------+------+------------+---------+

更新说明:

AFAICT:DISTINCT 是多余的……没有什么真正需要它,所以我放弃了它。为了遵循 Fabrizio 的建议,在 credential_pairs 查找中找到位置,我随后将语句更改为:

SELECT credential_id 
FROM credential_pairs cp
WHERE cp.primary_credential_id = (SELECT credential_id FROM credentials WHERE data = AES_ENCRYPT('value 1','enc_key')) AND
    cp.secondary_credential_id = (SELECT credential_id FROM credentials WHERE data = AES_ENCRYPT('value 2','enc_key'))

而且....什么也没有。该语句需要同样长的时间,并且解释看起来几乎相同。因此,我在主列和辅助列中添加了一个索引:

ALTER TABLE credential_pairs ADD INDEX `idx_credential_pairs__primary_and_secondary`(`primary_credential_id`, `secondary_credential_id`);

而且……什么都没有。

+----+-------------+-------------+-------+---------------------+---------------------------------------------+---------+------+-------+--------------------------+
| id | select_type | table       | type  | possible_keys       | key                                         | key_len | ref  | rows  | Extra                    |
+----+-------------+-------------+-------+---------------------+---------------------------------------------+---------+------+-------+--------------------------+
|  1 | PRIMARY     | cp          | index | NULL                | idx_credential_pairs__primary_and_secondary | 514     | NULL | 69217 | Using where; Using index |
|  3 | SUBQUERY    | credentials | ref   | ix_credentials_data | ix_credentials_data                         | 22      |      |     1 | Using where              |
|  2 | SUBQUERY    | credentials | ref   | ix_credentials_data | ix_credentials_data                         | 22      |      |     1 | Using where              |
+----+-------------+-------------+-------+---------------------+---------------------------------------------+---------+------+-------+--------------------------+

它说它正在使用索引,但它看起来仍然像是在扫描表。因此,我添加了一个联合密钥(根据下面的 a'r 评论):

ALTER TABLE credential_pairs ADD KEY (primary_credential_id, secondary_credential_id);

而且...与索引相同的结果(这些功能是否相同?)。

【问题讨论】:

  • 尝试在 credential_pairs 上添加复合密钥,例如。 ALTER TABLE credential_pairs ADD KEY (primary_credential_id, secondary_credential_id);
  • 您最初是在尝试查找与给定值集(“某个值 1”和“某个值 2”)匹配的所有 PAIRS 吗?因为这就是查询正在做的事情。没有办法让这个查询更快,因为它总是需要进行全表扫描。这在设计中看起来是一个错误,因为通常您应该有一个用户并知道他的 credential_id。一旦他提供了令牌值,您就可以使用此 credential_id 来查找属于他的对(恰好 1 对和仅扫描的一行),然后根据输入的令牌值验证该对中的主要和次要凭据。

标签: mysql optimization mysql-slow-query-log


【解决方案1】:

DISTINCT 是生成“使用临时”的原因,您通常希望尽可能避免使用这些

另外,您正在扫描整个 credential_pair 表,因为您没有任何条件反对它,因此不使用索引,并且在应用 WHERE 之前返回整个表

希望有意义

编辑/添加

尝试从不同的表开始,如果我理解正确,您有表 A、表 B 和表 AB,并且您从 AB 开始选择,尝试从 A 开始

我没有测试过,但你可以试试:

SELECT cp.credential_id
FROM credentials AS c1
    LEFT JOIN credential_pairs AS cp ON (c1.credential_id = cp.primary_credential_id)
    LEFT JOIN credentials AS c2 ON (cp.secondary_credential_id = c2.credential_id)
WHERE
    c1.data = AES_ENCRYPT('Some Value 1', 'encryption key')
    AND c2.data = AES_ENCRYPT('Some Value 2', 'encryption key');

我过去曾通过移动选择表来获得好运

【讨论】:

  • 您实际添加的内容可能会减慢您的查询速度。我相信在 WHERE 中添加 (SELECT) 只会导致为每个值执行子查询
  • 可以发一些专栏内容吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-30
  • 2016-07-19
  • 1970-01-01
相关资源
最近更新 更多