我有一个有活动供稿的网站,类似于像Facebook这样的社交网站。这是"最新的第一个"描述用户采取的操作的列表。在制作中,该表中约有200,000个条目。
因为无论如何都要问这个问题,我将首先分享完整的表格结构:
CREATE TABLE `karmalog` (
`id` int(11) NOT NULL auto_increment,
`guid` char(36) default NULL,
`user_id` int(11) default NULL,
`user_name` varchar(45) default NULL,
`user_avat_url` varchar(255) default NULL,
`user_sec_id` int(11) default NULL,
`user_sec_name` varchar(45) default NULL,
`user_sec_avat_url` varchar(255) default NULL,
`event` enum('EDIT_PROFILE','EDIT_AVATAR','EDIT_EMAIL','EDIT_PASSWORD','FAV_IMG_ADD','FAV_IMG_ADDED','FAV_IMG_REMOVE','FAV_IMG_REMOVED','FOLLOW','FOLLOWED','UNFOLLOW','UNFOLLOWED','COM_POSTED','COM_POST','COM_VOTE','COM_VOTED','IMG_VOTED','IMG_UPLOAD','LIST_CREATE','LIST_DELETE','LIST_ADMINDELETE','LIST_VOTE','LIST_VOTED','IMG_UPD','IMG_RESTORE','IMG_UPD_LIC','IMG_UPD_MOD','IMG_GEO','IMG_UPD_MODERATED','IMG_VOTE','IMG_VOTED','TAG_FAV_ADD','CLASS_DOWN','CLASS_UP','IMG_DELETE','IMG_ADMINDELETE','IMG_ADMINDELETEFAV','SET_PASSWORD','IMG_RESTORED','IMG_VIEW','FORUM_CREATE','FORUM_DELETE','FORUM_ADMINDELETE','FORUM_REPLY','FORUM_DELETEREPLY','FORUM_ADMINDELETEREPLY','FORUM_SUBSCRIBE','FORUM_UNSUBSCRIBE','TAG_INFO_EDITED','IMG_ADDSPECIE','IMG_REMOVESPECIE','SPECIE_ADDVIDEO','SPECIE_REMOVEVIDEO','EARN_MEDAL','JOIN') NOT NULL,
`event_type` enum('follow','tag','image','class','list','forum','specie','medal','user') NOT NULL,
`active` bit(1) NOT NULL,
`delete` bit(1) NOT NULL default '\0',
`object_id` int(11) default NULL,
`object_cache` text,
`object_sec_id` int(11) default NULL,
`object_sec_cache` text,
`karma_delta` int(11) NOT NULL,
`gold_delta` int(11) NOT NULL,
`newkarma` int(11) NOT NULL,
`newgold` int(11) NOT NULL,
`migrated` int(11) NOT NULL default '0',
`date_created` timestamp NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `user_sec_id` (`user_sec_id`),
KEY `image_id` (`object_id`),
KEY `date_event` (`date_created`,`event`),
KEY `event` (`event`),
KEY `date_created` (`date_created`),
CONSTRAINT `karmalog_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL,
CONSTRAINT `karmalog_ibfk_2` FOREIGN KEY (`user_sec_id`) REFERENCES `user` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
在优化此表之前,我的查询有5个连接,我遇到了慢查询时间。我已经对所有数据进行了非规范化,因此不再存在单个连接。因此表和查询是平的。
正如您在表格设计中所看到的那样,"事件"字段是一个枚举,持有几十个可能的值。在整个网站中,我会根据特定的事件类型显示活动供稿。通常,该查询如下所示:
SELECT * FROM karmalog as k
WHERE k.event IN ($events) AND k.delete=0
ORDER BY k.date_created DESC, k.id DESC
LIMIT 0,30
此查询的作用是查找总计集中与$ events中传递的任何事件匹配的最新30个条目,这些条目可以是多个。
由于删除了连接并且在大多数字段上都有索引,所以我期望它的表现非常好,但它并没有。在200k条目上,它仍然需要3秒钟,我不明白为什么。
关于解决方案,我知道我可以存档旧条目或按事件类型对表进行分区,但这会产生相当大的代码影响,我首先想了解为什么上面这么慢。
作为临时解决方案,我现在正在这样做:
SELECT * FROM
(SELECT * FROM karmalog ORDER BY date_created DESC, id DESC LIMIT 0,1000) as karma
WHERE karma.event IN ($events) AND karma.delete=0
LIMIT $page,$pagesize
这样做是为了限制baseset仅搜索最新的1000个条目,希望并猜测我传入的过滤器中有30个条目。它不是很虽然稳健。它不适用于更罕见的事件,它会带来分页问题。
因此,我首先想要了解我的初始查询为什么慢的根本原因,而不是我的期望。
编辑:我被要求分享执行计划。这是测试查询:
EXPLAIN SELECT * FROM karmalog
WHERE event IN ('FAV_IMG_ADD','FOLLOW','COM_POST','IMG_VOTE','LIST_VOTE','JOIN','CLASS_UP','LIST_CREATE','FORUM_REPLY','FORUM_CREATE','FORUM_SUBSCRIBE','IMG_GEO','IMG_ADDSPECIE','SPECIE_ADDVIDEO','EARN_MEDAL') AND karmalog.delete=0
ORDER BY date_created DESC, id DESC
LIMIT 0,36
执行计划:
id = 1
select_type = SIMPLE
table = karmalog
type = range
possible_keys = event
key = event
key_len = 1
red = NULL
rows = 80519
Extra = Using where; Using filesort
我不确定如何阅读上述内容,但我确实知道sort子句似乎真的会破坏这个查询。通过这种排序,它需要4.3秒,没有0.03秒。
答案 0 :(得分:1)
SELECT *
有时会大幅降低有序查询的速度,所以让我们首先重构您的查询,如下所示:
SELECT k.*
FROM karmalog AS k
JOIN (
SELECT id
FROM karmalog
WHERE event IN ($events)
AND delete=0
ORDER BY date_created DESC, id DESC
LIMIT 0,30
) AS m ON k.id = m.id
ORDER BY k.date_created DESC, k.id DESC
这将执行您的ORDER BY ... LIMIT
操作,而无需在排序阶段拖动整个表格。最后,它会从原始表中查找相应的三十行,然后再次排序。这可能会节省大量的I / O和内存数据混乱。
其次,如果在插入记录时按升序分配id
列值,则在date_created
操作中使用ORDER BY
是多余的。但是MySQL并不知道这一点,因此将其删除可能会有所帮助。如果您在插入时始终使用当前日期,并且从不更新日期,则会出现这种情况。
第三,您可以使用复合覆盖索引进行选择(内部)查询。这是一个包含您需要的所有字段的索引。当您使用覆盖索引时,可以从索引中满足整个查询,并且不需要退回到原始表。这样可以节省磁盘访问时间。
尝试使用此化合物覆盖索引:(delete, event, id)
。如果您决定在订购时无法使用date_created
,请尝试相应:(delete, event, date_created, id)
答案 1 :(得分:0)
在两个相关问题上添加复合索引。在您的表中,您可以通过指定例如
来实现KEY `date_created` (`date_created`, `event`)
此密钥仍可用于满足普通的date_created
范围搜索。但除此之外,还包括event
数据,因此DBS只能通过查看索引来检测相关行。
如果您愿意,您也可以尝试其他订单:第一个事件,然后是日期。如果有许多事件类型但是过滤器只包含很少的事件类型,这可能会允许一些优化。另一方面,我不确定系统是否能够在这种情况下使用LIMIT
条款,所以我不确定这个其他订单是否会提供任何帮助
编辑:我完全错过了您的date_event
索引已有此信息。但是,根据你的执行计划,没有使用过。看起来优化器出错了。您可以尝试删除event
索引,也可以删除date
索引,然后查看会发生什么。