mysql - 我该怎么做才能使这个 SQL 更有效率? (表有 850K 行)

标签 mysql sql performance

由于生产问题,我一直在 mysql 上运行缓慢的日志,第一个查询是:

select * from feeditem feeditem0_ where feeditem0_.importance=0 and feeditem0_.company_id=N limit 21;

我将 select(N 是 FK 的 id)缩写为从休眠生成的,它只是选择该表中的所有字段。当我执行 mysql 解释时,我得到:

explain select * from feeditem feeditem0_ where feeditem0_.importance=0 and    feeditem0_.company_id=5045 limit 21 \G;;
*************************** 1. row ***************************
       id: 1
select_type: SIMPLE
    table: feeditem0_
     type: index_merge
possible_keys: FKF49961B13D5FD8EF,importance
      key: FKF49961B13D5FD8EF,importance
  key_len: 9,5
      ref: NULL
     rows: 2422
    Extra: Using intersect(FKF49961B13D5FD8EF,importance); Using where

该表中大约有 85 万行。

架构是:

CREATE TABLE `feeditem` (
`DTYPE` varchar(31) NOT NULL,
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`dateCreated` datetime DEFAULT NULL,
`endSentance` varchar(255) DEFAULT NULL,
`importance` int(11) DEFAULT NULL,
`startSentance` varchar(255) DEFAULT NULL,
`summary` varchar(255) DEFAULT NULL,
`summaryComplete` bit(1) NOT NULL,
`targetId` bigint(20) DEFAULT NULL,
`targetSentance` text,
`type` varchar(255) NOT NULL,
`hasRead` bit(1) DEFAULT NULL,
`teamProject_id` bigint(20) DEFAULT NULL,
`user_id` bigint(20) DEFAULT NULL,
`usertoread_id` bigint(20) DEFAULT NULL,
`contentType` varchar(255) DEFAULT NULL,
`company_id` bigint(20) DEFAULT NULL,
`updated` int(1) unsigned DEFAULT NULL,
`feedType` varchar(255) DEFAULT NULL,
`extraInfo` varchar(255) DEFAULT NULL,
`extraTargetId` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FKF49961B1B74A2DA5` (`user_id`),  
KEY `FKF49961B17CE9E5EF` (`teamProject_id`),
KEY `FKF49961B137B7D1B4` (`usertoread_id`),
KEY `FKF49961B13D5FD8EF` (`company_id`),
KEY `importance` (`importance`),
KEY `dateCreated` (`dateCreated`)
) ENGINE=InnoDB AUTO_INCREMENT=956498 DEFAULT CHARSET=utf8

有什么方法可以阻止扫描 2400 奇数行?这是慢日志的摘要(使用 mysqlsla):

Count         : 61  (53.98%)
Time          : 523 s total, 8.57377 s avg, 6 s to 19 s max  (54.03%)
95% of Time : 456 s total, 8 s avg, 6 s to 14 s max
Lock Time (s) : 0 total, 0 avg, 0 to 0 max  (0.00%)
95% of Lock : 0 total, 0 avg, 0 to 0 max
Rows sent     : 34 avg, 21 to 51 max  (38.69%)
Rows examined : 3.49k avg, 40 to 8.89k max  (0.00%)
Users         :100.00% (61) of query, 100.00% (113) of all users

谢谢

更新 1: 我添加了另一个 2 col 索引(称为 feedquery),但优化器似乎选择不使用该索引:

mysql> explain select id from feeditem feeditem0_ where feeditem0_.importance=0 and    feeditem0_.company_id=5045  \G;
*************************** 1. row ***************************
       id: 1
  select_type: SIMPLE
    table: feeditem0_
     type: index_merge
possible_keys: FKF49961B13D5FD8EF,importance,feedquery
      key: FKF49961B13D5FD8EF,feedquery
  key_len: 9,14
      ref: NULL
     rows: 2753
    Extra: Using intersect(FKF49961B13D5FD8EF,feedquery); Using where; Using index

如果我忽略索引:

 explain select id from feeditem feeditem0_ ignore index (FKF49961B13D5FD8EF) where feeditem0_.importance=0 and  feeditem0_.company_id=5045  \G;
 *************************** 1. row ***************************
       id: 1
 select_type: SIMPLE
    table: feeditem0_
     type: ref
 possible_keys: importance,feedquery
      key: feedquery
  key_len: 14
      ref: const,const
     rows: 8496
    Extra: Using where; Using index

表格:

CREATE TABLE `feeditem` (
.....
PRIMARY KEY  (`id`),
KEY `FKF49961B1B74A2DA5` (`user_id`),
 KEY `FKF49961B17CE9E5EF` (`teamProject_id`),
KEY `FKF49961B137B7D1B4` (`usertoread_id`),
KEY `FKF49961B13D5FD8EF` (`company_id`),
KEY `importance` (`importance`),
KEY `dateCreated` (`dateCreated`),
KEY `feedquery` (`importance`,`company_id`)
) ENGINE=InnoDB AUTO_INCREMENT=999359 DEFAULT CHARSET=utf8 

更新 2: @Salman A

SHOW profile;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000342 |
| checking permissions | 0.000024 |
| Opening tables       | 0.000053 |
| System lock          | 0.000027 |
| init                 | 0.000166 |
| optimizing           | 0.000068 |
| statistics           | 0.012869 |
| preparing            | 0.000202 |
| executing            | 0.000008 |
| Sending data         | 0.332767 |
| end                  | 0.000022 |
| query end            | 0.000009 |
| closing tables       | 0.000016 |
| freeing items        | 0.000040 |
| logging slow query   | 0.000005 |
| cleaning up          | 0.000014 |
+----------------------+----------+

ibdata1 大约 1.5 GB

最佳答案

一个通用的答案:

  1. 不要使用SELECT * 除非您绝对需要所有列。仅选择您需要的列。
  2. 添加 ORDER BY 子句或 LIMIT 意义不大。
  3. 创建一个复合(即多列)索引,涵盖
    • 重要性company_id
    • 您要按预期顺序ORDER BY 的字段
    • 您希望 SELECT 返回的任何其他字段(替换 *)

这样,数据库引擎可以通过单个索引查找操作查找与您的搜索直接匹配的内容,并直接从索引中覆盖排序和其他列。索引包含它涵盖的所有列的副本;如果所有请求的数据都驻留在索引中,则无需遍历实际表。这将提高查询效率。

请注意,这是速度换空间的交换。添加到索引中的每一列都会增加其物理大小,因此请明智地选择。

编辑 1:此外,索引对写入操作的速度有影响 - 由于索引维护,INSERT、UPDATE 和 DELETE 查询会慢一点 - 以换取更快的 SELECT。 (感谢评论,@Thor84no)

编辑 2:如果此查询是表的主要使用模式并且表变化不大(这一点非常重要!),您可以考虑创建聚簇索引。聚簇索引表示基表的物理排序,它不存在于基表之外, 与其他索引一样。每次更改聚集索引的定义或在现有记录“之间”添加/删除行时,实际数据都会在物理上重新排序,即在磁盘上,这是您希望避免的昂贵操作。

有时这可能是明智之举,但在您的情况下很可能并非如此。如果您的表是某种类型的日志表,请将聚簇索引保持在自动递增 ID 处。

关于mysql - 我该怎么做才能使这个 SQL 更有效率? (表有 850K 行),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7793393/

相关文章:

php - 由于动态加载的内容,Div 不会自动扩展

java - 数组按余数排序

mysql - ActiveRecord 范围查找今天创建的记录?

php - 如何在 PHP 中按单列对 SQL 连接的结果进行排序

java - 如何使用 Java 在 MySQL DB 中插入 HTML?

SQL 内部联接不返回具有空值的记录

php - 如何从不同的表中查找值?

mysql - SQL INNER JOIN 返回的行比我需要的多

java - 优化 SQLite 查询以使用一个查询

c# - 回归本源; for 循环、数组/向量/列表和优化