我有一个名为“ roomlogs”的大表,其中有近100万个条目。 表的结构:
id-> PK
roomId-> varchar FK到Rooms表
userId-> varchar FK到用户表
enterTime->日期和时间
exitTime->日期和时间
状态->布尔
我以前在roomID上建立过索引,最近我在userId列上添加了一个索引。
因此,当我使用以下代码运行存储过程时,它将花费更多的时间,例如平均50秒。这不应该。
DELIMITER ;;
CREATE DEFINER=`root`@`%` PROCEDURE `enter_room`(IN pRoomId varchar(200), IN puserId varchar(50), IN ptime datetime, IN phidden int, pcheckid int, pexit datetime)
begin
update roomlogs set
roomlogs.exitTime = ptime,
roomlogs.`status` = 1
where
roomlogs.userId = puserId
and roomlogs.`status` = 0
and DATEDIFF(ptime,roomlogs.enterTime) = 0;
INSERT into roomlogs
( roomlogs.roomId,
roomlogs.userId,
roomlogs.enterTime,
roomlogs.exitTime,
roomlogs.hidden,
roomlogs.checkinId )
value
( pRoomId,
userId,
ptime,
pexit,
phidden,
pcheckid);
select *
from
roomlogs
where
roomlogs.id= LAST_INSERT_ID();
end ;;
DELIMITER ;
花费这么长时间的原因可能是什么?
编辑:这是显示表:
CREATE TABLE `roomlogs` (
`roomId` varchar(200) CHARACTER SET latin1 DEFAULT NULL,
`userID` varchar(50) CHARACTER SET latin1 DEFAULT NULL,
`enterTime` datetime DEFAULT NULL,
`exitTime` datetime DEFAULT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
`status` int(11) DEFAULT '0',
`hidden` int(11) DEFAULT '0',
`checkinId` int(11) DEFAULT '-1',
PRIMARY KEY (`id`),
KEY `RoomLogIndex` (`roomId`),
KEY `RoomLogIDIndex` (`id`),
KEY `USERID` (`userID`)
) ENGINE=InnoDB AUTO_INCREMENT=1064216 DEFAULT CHARSET=utf8
我还可以看到该查询每天运行的次数更多,例如每天100000次(几乎连续)。
SELECT count(*) from roomlogs where roomId=proomId and status='0';
由于此查询从同一张表中读取数据,因此InnoDB会阻塞或在更新查询上创建锁定,因为我可以看到,当以上存储的过程运行的次数更多时,此查询将花费更多的时间。
以下是MySQL变量的链接:https://docs.google.com/document/d/17_MVaU4yvpQfVDT83yhSjkLHsgYd-z2mg6X7GwvYZGE/edit?usp=sharing
答案 0 :(得分:1)
roomlogs
需要以下“复合”索引:
INDEX(userId, `status`, enterTime)
我最近添加了一个索引,因此以前的行没有被索引。
不是。添加INDEX
会索引整个表。
默认索引类型为BTree
;无需显式指定它。
过程中的更新查询是否锁定?
它执行某种形式的锁定。 autocommit
的值是什么?您是否明确使用BEGIN
和COMMIT
?表是ENGINE=InnoDB
吗?请提供SHOW CREATE TABLE
。
每个查询的MySQL.slow_logs表_time锁定显示0。
您显示的INSERT
似乎与UPDATE
插入了同一行。也许您需要INSERT ... ON DUPLICATE KEY UPDATE ...
?
不要“隐藏函数中的索引列”;代替DATEDIFF(roomlogs.enterTime,NOW()) = 0
,
AND enterTime >= CURDATE()
AND enterTime < CURDATE() + INTERVAL 1 DAY
这可以使索引得到更充分的利用。
KEY `RoomLogIndex` (`roomId`), Change to (roomId, status)
KEY `RoomLogIDIndex` (`id`), Remove, redundant with the PK
仅缓冲97,517,568的缓冲池-使它更像9G。