mysql:强制不同的访问类型,在(…)中使用索引

siotufzp  于 2021-06-21  发布在  Mysql
关注(0)|答案(1)|浏览(311)

我正在尝试调整一个非常简单的查询:

select * from log where user_id in (...) order by id desc limit 25

我只想显示一组不同用户id(大约40个id)的最后25个事件。这个查询大约需要50秒来运行(表中有超过8000万条记录)。
通过执行 EXPLAIN format=json 我看得出来 access_typerange . 经过一番探索,a了解到如果我将ID的数量更改为9,查询计划器将使用另一种访问方式: index .
所以我假设,对于大量的id,mysql将在组中较小的id和较大的id之间进行范围扫描,如果id是“close”,这可能是有意义的,但情况并非总是如此。也许在某种程度上,这个额外的数据量在进行排序时会成为一个问题(如下面的解释计划中所示)。
40身份证解释

{
  "query_block": {
    "select_id": 1,
    "ordering_operation": {
      "using_filesort": true,
      "table": {
        "table_name": "log",
        "access_type": "range",
        "possible_keys": [
          "app_log_user_id"
        ],
        "key": "log_user_id",
        "used_key_parts": [
          "user_id"
        ],
        "key_length": "4",
        "rows": 6150,
        "filtered": 100,
        "index_condition": "(`app`.`log`.`user_id` in (<43 different ids from 12000 to 330000>))"
      }
    }
  }
}

9 ID解释

{
  "query_block": {
    "select_id": 1,
    "ordering_operation": {
      "using_filesort": false,
      "table": {
        "table_name": "log",
        "access_type": "index",
        "possible_keys": [
          "app_log_user_id"
        ],
        "key": "PRIMARY",
        "used_key_parts": [
          "id"
        ],
        "key_length": "4",
        "rows": 6901,
        "filtered": 4552.8,
        "attached_condition": "(`app`.`log`.`user_id` in (< 9 ids from 12000 to 18000))"
      }
    }
  }
}

我做了一个实验:我将这个查询分成5个子查询,其中只有9个或更少的id,并应用了一个 UNION 对所有人来说,最后是命令和限制条款。这个查询的查询计划变得有点混乱,甚至有奇怪的值说其中一个子查询的搜索行数是86737713(我认为这是一个非常错误的估计,所有其他的都在10246左右)。你猜怎么着?查询只花了6秒,比50秒要好。
我不知道使用哪种策略来优化这种查询,但据我所知,如果我能告诉优化器使用 acess_typeindex 相反 range ,它会表现得更好。有可能吗?
额外细节 user_id 有外键和索引。
我们使用MySQL5.6(innodb)
表有大约80kk行。
显示创建表

CREATE TABLE `app_log` (
  `id`              int(11)          NOT NULL AUTO_INCREMENT,
  `timestamp`       datetime         NOT NULL,
  `user_id`         int(11)          NOT NULL,
  `content_type_id` int(11)          NOT NULL,
  `object_id`       int(10) unsigned NOT NULL,
  `status`          int(11)                   DEFAULT NULL,
  `type`            int(11)                   DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `app_log_content_type_id` (`content_type_id`),
  KEY `app_log_144dd2a9` (`timestamp`),
  KEY `app_log_user_id` (`user_id`, `id`)
)
  ENGINE = InnoDB
  AUTO_INCREMENT = 108628300
  DEFAULT CHARSET = latin1
relj7zay

relj7zay1#

可能的解释是:
您使用的是哪个版本的mysql/mariadb?我猜你用的是MySQL5.6(你使用 FORMAT=JSON 确认“至少5.6.5”。)
5.6.5介绍 eq_range_index_dive_limit ,默认值为10。
5.7.4 eq_range_index_dive_limit 默认值提高到200-影响 IN() 可能的解决方法:
这张便条可能解释了你的9对43项中 IN 列表。建议你和我一起玩 eq_range_index_dive_limit .
琐事
kk=千
m、 对会计师来说,千等于千
嗯,对会计师来说是百万,一个拉卡
10万,印第安人=10万
百万印度卢比=1000万(1000万)
十亿,对英国人来说曾经是百万;幸运的是,这种困惑似乎已经消失。
1000和1024之间的区别(以及kb和kib之间的区别)等等,出于所有实际目的,在本论坛中都可以忽略。

相关问题