在不同列上执行SELECT WHERE的表的最佳索引技术

问题描述

我正在寻找一种高效的索引表索引技术,如下所示:

MariaDB [Webapp]> explain logs;
+----------------+--------------+------+-----+---------+----------------+
| Field          | Type         | Null | Key | Default | Extra          |
+----------------+--------------+------+-----+---------+----------------+
| id             | int(11)      | NO   | PRI | NULL    | auto_increment |
| user_id        | int(11)      | YES  | MUL | NULL    |                |
| activity_name  | varchar(20)  | NO   |     | NULL    |                |
| activity_key   | varchar(255) | NO   |     | NULL    |                |
| activity_value | varchar(255) | NO   |     | NULL    |                |
| activity_date  | datetime     | NO   | MUL | NULL    |                |
+----------------+--------------+------+-----+---------+----------------+

我确实这样搜索

SELECT *
FROM logs
WHERE user_id IN (1,3)
  AND activity_name IN ('login','logout')
  AND activity_date >= '2020-02-01'
  AND activity_date <= '2020-06-01'

涉及列user_idactivity_nameactivity_date的地方


有时是这样的:

SELECT *
FROM logs
WHERE user_id IN (1,'logout')

同时涉及user_idactivity_name,但没有日期。


也是这样的:
SELECT *
FROM logs
WHERE user_id IN (1,3)
  AND activity_date >= '2020-02-01'
  AND activity_date <= '2020-06-01'

SELECT *
FROM logs
WHERE activity_name IN ('login','logout')
  AND activity_date >= '2020-02-01'
  AND activity_date <= '2020-06-01'

我确实读过有关化合物索引的信息,如果我订购了搜索索引,它们会很好,但是如您所见,它并不适用,所以我认为它不合适。

我还读到单个索引可以一次只在一个列上使用,所以我认为这对我的情况不利。

请提出任何想法,我对MysqL不太熟悉。如何使查询最优化?

注意:我不使用通配(*),因为我阅读通配符会减慢速度,但只是为了缩短查询时间而把它简化了>

解决方法

对于每个查询,基本思想是拥有一个索引,其列覆盖where子句。对于您而言,这不能通过对四个查询使用单个索引来实现-我认为您需要3个索引。

首先,考虑以下索引:

logs(user_id,activity_name,activity_date)

它与第一个查询的where子句匹配:

WHERE 
    user_id IN (1,3) 
    AND activity_name IN ('login','logout') 
    AND activity_date >= '2020-02-01' 
    AND activity_date <= '2020-06-01'

在第二个查询中也是如此(此处将忽略第三个索引列):

WHERE 
    user_id IN (1,'logout') 

对于其他两个查询,您需要两个单独的索引:

WHERE 
    user_id IN (1,3) 
    AND activity_date >= '2020-02-01' 
    AND activity_date <= '2020-06-01'

需要:

logs(user_id,activity_date)

并且:

WHERE 
    activity_name IN ('login','logout') 
    AND activity_date >= '2020-02-01' 
    AND activity_date <= '2020-06-01'

需要:

logs(activity_name,activity_date)

旁注:通常,请勿盲目select *;相反,请枚举结果集中所需的列-特别是如果您不希望全部使用时。如果只需要两三列,请考虑将它们添加到索引的末尾,从而将其变为 covering 索引。