0

这是使用 MySQL 5.5。我似乎无法说服 MySQL 为这些查询使用索引,并且它们在 110 万行的表上运行需要 2-10 秒。

桌子:

CREATE TABLE `notifiable_events` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(255) NOT NULL,
`trigger_profile_id` int(10) unsigned DEFAULT NULL,
`model1` varchar(25) NOT NULL,
`model1_id` varchar(36) NOT NULL,
`model2` varchar(25) NOT NULL DEFAULT '',
`model2_id` varchar(36) NOT NULL DEFAULT '',
`event_data` text,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
`deleted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `key` (`key`),
KEY `notifiable_events__trigger_profile` (`trigger_profile_id`),
KEY `deleted` (`deleted`),
KEY `noti_evnts__m2` (`model2`),
KEY `noti_evnts__m1` (`model1`),
CONSTRAINT `notifiable_events__trigger_profile` FOREIGN KEY (`trigger_profile_id`) REFERENCES `profiles` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1177918 DEFAULT CHARSET=utf8

询问:

  SELECT * 
    FROM notifiable_events 
    WHERE (`model1` = 'page' AND `model1_id` = '54321') 
       OR (`model2` = 'page' AND `model2_id` = '12345');

解释:

mysql> EXPLAIN EXTENDED SELECT * FROM notifiable_events WHERE (`model1` = 'page' AND `model1_id` = '922645') OR (`model2` = 'page' AND `model2_id` = '922645')\G

    ****************************** 1. 行 ************************ *******
               编号:1
      选择类型:简单
            表:notifiable_events
             类型:全部
    可能键:noti_evnts__m2,noti_evnts__m1,noti_evnts__m1_m2
              键:空
          key_len:空
              参考:空
             行数:1033088
         过滤:100.00
            额外:使用 where
    1 行,1 个警告(0.00 秒)

mysql> EXPLAIN EXTENDED SELECT * FROM notifiable_events WHERE (`model1` = 'page' AND `model1_id` = '922645') OR (`model1` = 'page' AND `model1_id` = '922645')\G
****************************** 1. 行 ************************ *******
           编号:1
  选择类型:简单
        表:notifiable_events
         类型:参考
可能键:noti_evnts__m1,noti_evnts__m1_m2
          键:noti_evnts__m1
      关键长度:77
          参考:常量
         行数:1
     过滤:100.00
        额外:使用 where
1 行,1 个警告(0.00 秒)

mysql> EXPLAIN EXTENDED SELECT * FROM notifiable_events WHERE (`model2` = 'page' AND `model2_id` = '922645') OR (`model2` = 'page' AND `model2_id` = '922645')\G
****************************** 1. 行 ************************ *******
           编号:1
  选择类型:简单
        表:notifiable_events
         类型:参考
可能的键:noti_evnts__m2
          键:noti_evnts__m2
      关键长度:77
          参考:常量
         行数:428920
     过滤:100.00
        额外:使用 where
1 行,1 个警告(0.00 秒)

您可以看到,如果我只使用 model1 或只使用 model2,那么它将使用索引,但是,一旦我尝试将它们一起使用,它就会完全放弃索引并进行全表扫描。我已经尝试过 FORCE INDEX 并且我尝试了多键索引的组合(我为这个表留下了一个作为示例)。我也尝试过重新排列查询中元素的顺序,但这似乎也没有任何效果。

更新:我忘了提到我已经尝试过 ANALYZE 和 OPTIMIZE (每次多次,没有变化)。我还已经尝试了 *_id 上的索引(基数非常糟糕,这些列大多是唯一条目)和一个多索引,查询中使用了所有 4 列。在这两种情况下也没有改进或使用索引。

似乎使用索引来限制这里检查的行应该很容易,所以我希望我只是遗漏了一些东西。

4

1 回答 1

4

or子句有时会搞砸查询优化。

你可以试试工会,像这样的。它可能会重新激活索引。

SELECT * 
  FROM notifiable_events 
 WHERE  `model1` = 'page' AND `model1_id` = '54321'
 UNION
SELECT * 
  FROM notifiable_events 
 WHERE `model2` = 'page' AND `model2_id` = '12345'

编辑,回答评论中的问题

如果您尝试使用这种选择方案更新记录,您可以尝试以下操作:

UPDATE notifiable_events
   SET what=ever,
       what=ever,
       what=else 
 WHERE id IN (
   SELECT id 
     FROM notifiable_events 
    WHERE  `model1` = 'page' AND `model1_id` = '54321'
    UNION
   SELECT id 
     FROM notifiable_events 
    WHERE `model2` = 'page' AND `model2_id` = '12345'
 )

(请注意,当您使用 Stackoverflow 来解释您实际尝试做的事情时,这很有帮助。如果可以的话,当然可以将一个复杂的问题简化为一个更简单的问题,但是当您说您正在执行 SELECT 时'实际上是在进行更新,嗯,过于简单化了。)

于 2012-07-12T01:52:23.573 回答