mysql - OR运算符不使用索引

rob*_*mag 4 mysql sql explain

我有一个简单的邀请表:

CREATE TABLE `invitation` (
  `invitation_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `inviter_id` int(10) unsigned NOT NULL,
  `invitee_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`invitation_id`),
  UNIQUE KEY `invitee_inviter_idx` (`invitee_id`,`inviter_id`)
)
Run Code Online (Sandbox Code Playgroud)

我想通过邀请者70选择被邀请者62的邀请,反之亦然:

EXPLAIN SELECT * FROM `invitation` WHERE 
(invitee_id = 70 AND inviter_id = 62) OR (invitee_id = 62 AND inviter_id = 70)
Run Code Online (Sandbox Code Playgroud)

但是此查询的类型为ALL,并且不使用invitee_inviter_idx.请告诉我这里有什么问题?

谢谢!

==编辑==对不起,我错了架构,还有一个字段:request_ts.这次查询计划是ALL.

    CREATE TABLE `invitation` (
      `invitation_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `inviter_id` int(10) unsigned NOT NULL,
      `invitee_id` int(10) unsigned NOT NULL,
      `request_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
      PRIMARY KEY (`invitation_id`),
      UNIQUE KEY `invitee_inviter_idx` (`invitee_id`,`inviter_id`)
    )
Run Code Online (Sandbox Code Playgroud)

这是我的exlain结果:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  invitation  ALL invitee_inviter_idx \N  \N      \N  1   Using where
Run Code Online (Sandbox Code Playgroud)

Ton*_*Lee 8

您的选择未使用索引的原因至少有3个

1)您使用了a select *,其中包括不在索引中的项目(即invitation_id).这意味着如果它使用了索引,那么它必须查找数据库中的行以获取invitation_id值.如果你添加invitation_id到索引,它将使用索引.如果你做了一个select公正的invitee_id, inviter_id,它会使用索引.

2)查询优化器决定只扫描表而不是扫描索引的范围会更好.当优化器尝试决定全表扫描或部分索引扫描时,它不会针对您的确切查询执行此操作 - 它需要一个通常运行良好的计划.一个可能会重复运行.扫描invitee_id,inviter_id (62,70)(70,62)的可能只有8个索引条目,但如果从50k项目中随机选取,则平均距离约为17k项目.因此,平均而言,单个查询将访问索引的1/3(即,将其拉入内存),然后访问该行所在的页面(请参阅#1)将其拉入内存.你的行很小,访问一个项目可能会拉入680行(3个32位#的8k页乘12个字节),这是表的1/70 - 做100个查询,可能你已经将整个索引拉入内存和整个表 - 通过扫描表并使用少40%的内存来保存其他表的位来更长一段时间.在某些时候(似乎是65k行)它停止有意义.

3)你的问题是什么:你使用了OR.OR表达式不能用于在索引中查找内容 - 也就是说,您无法查找62或70.相反,它会生成查找范围(62,70),然后扫描到达(70,62)(请参阅#2为什么这样可以坏).

你问"这里有什么不对" - 这就是你使用了OR,它不会扩展.您不仅需要避免使用ALL类型,还需要避免使用大型RANGES.

我已经看到了与其他SQL引擎相同的问题,我使用的解决方案是UNION ALL.

就像是

SELECT * FROM `invitation` WHERE 
    (invitee_id = 70 AND inviter_id = 62)
UNION ALL
SELECT  * FROM `invitation` WHERE
    (invitee_id = 62 AND inviter_id = 70)
Run Code Online (Sandbox Code Playgroud)

这将使它作为两个查询完成并合并结果而不检查重复.

这在内存使用上要轻得多,速度要快得多 - 只需要索引的几页和表中的两页,每次查找都需要O(log(N)).这是因为它现在是const类型 - 你的目标是消除ALL,但切换到RANGE几乎与获取两行一样糟糕.扫描整个表是O(N)并且扫描索引的RANGE也是O(N),因为O(1/3*N)是O(N).换句话说,它不会扩展.