【发布时间】:2020-02-01 20:29:34
【问题描述】:
我的应用程序出现问题,导致 MySQL 表由于插入需要很长时间而被锁定,在查看在线文章后,似乎与自动增量有关,信息如下 -
插入数据的 Python(不幸的是一次一行,因为我需要自动递增的 id 以供将来插入时参考)-
for i, flightobj in stats[ucid]['flight'].items():
flight_fk = None
# Insert flights
try:
with mysqlconnection.cursor() as cursor:
sql = "insert into cb_flights(ucid,takeoff_time,end_time,end_event,side,kills,type,map_fk,era_fk) values(%s,%s,%s,%s,%s,%s,%s,%s,%s);"
cursor.execute(sql, (
ucid, flightobj['start_time'], flightobj['end_time'], flightobj['end_event'], flightobj['side'],
flightobj['killnum'], flightobj['type'], map_fk, era_fk))
mysqlconnection.commit()
if cursor.lastrowid:
flight_fk = cursor.lastrowid
else:
flight_fk = 0
except pymysql.err.ProgrammingError as e:
logging.exception("Error: {}".format(e))
except pymysql.err.IntegrityError as e:
logging.exception("Error: {}".format(e))
except TypeError as e:
logging.exception("Error: {}".format(e))
except:
logging.exception("Unexpected error:", sys.exc_info()[0])
上面每 2 分钟对相同的数据运行一次,并且应该只插入非重复项,因为 MySQL 会因为唯一的 ucid_takeofftime 索引而拒绝重复项。
MYSQL 信息,cb_flights 表 -
`pk` int(11) NOT NULL AUTO_INCREMENT,
`ucid` varchar(50) NOT NULL,
`takeoff_time` datetime DEFAULT NULL,
`end_time` datetime DEFAULT NULL,
`end_event` varchar(45) DEFAULT NULL,
`side` varchar(45) DEFAULT NULL,
`kills` int(11) DEFAULT NULL,
`type` varchar(45) DEFAULT NULL,
`map_fk` int(11) DEFAULT NULL,
`era_fk` int(11) DEFAULT NULL,
`round_fk` int(11) DEFAULT NULL,
PRIMARY KEY (`pk`),
UNIQUE KEY `ucid_takeofftime` (`ucid`,`takeoff_time`),
KEY `ucid_idx` (`ucid`) /*!80000 INVISIBLE */,
KEY `end_event` (`end_event`) /*!80000 INVISIBLE */,
KEY `side` (`side`)
) ENGINE=InnoDB AUTO_INCREMENT=76023132 DEFAULT CHARSET=utf8;
现在从 Python 代码插入到表中,有时可能需要 60 多秒。 我相信这可能与在表上创建锁的自动增量有关,如果是这样,我正在寻找一种解决方法。
innodb 信息 -
innodb_autoinc_lock_mode 2
innodb_lock_wait_timeout 50
缓冲使用最多或减少 70%。
感谢任何帮助,无论是从应用程序端还是 MySQL 端。
编辑 为 cb_kills 表添加 create 语句,该表也与插入一起使用,但据我所知没有问题,这是对第一个答案的评论的回应。
CREATE TABLE `cb_kills` (
`pk` int(11) NOT NULL AUTO_INCREMENT,
`time` datetime DEFAULT NULL,
`killer_ucid` varchar(50) NOT NULL,
`killer_side` varchar(10) DEFAULT NULL,
`killer_unit` varchar(45) DEFAULT NULL,
`victim_ucid` varchar(50) DEFAULT NULL,
`victim_side` varchar(10) DEFAULT NULL,
`victim_unit` varchar(45) DEFAULT NULL,
`weapon` varchar(45) DEFAULT NULL,
`flight_fk` int(11) NOT NULL,
`kill_id` int(11) NOT NULL,
PRIMARY KEY (`pk`),
UNIQUE KEY `ucid_killid_flightfk_uniq` (`killer_ucid`,`flight_fk`,`kill_id`),
KEY `flight_kills_fk_idx` (`flight_fk`),
KEY `killer_ucid_fk_idx` (`killer_ucid`),
KEY `victim_ucid_fk_idx` (`victim_ucid`),
KEY `time_ucid_killid_uniq` (`time`,`killer_ucid`,`kill_id`),
CONSTRAINT `flight_kills_fk` FOREIGN KEY (`flight_fk`) REFERENCES `cb_flights` (`pk`)
) ENGINE=InnoDB AUTO_INCREMENT=52698582 DEFAULT CHARSET=utf8;
【问题讨论】:
-
不太可能解决您的问题,但为什么不在循环外创建一个光标,而不是在每次迭代时创建一个新光标?
-
"现在从 Python 代码插入到表中,有时可能需要 60 秒以上", - 这可能表明存在问题。无论表有多大,单个 INSERT 都不会花费那么长时间。我怀疑插入正在等待 X 锁,如果其他 trxs 持有 S 或 X 锁,则无法授予该锁。这是一个想法——你能检查慢日志中的查询并发布它的摘要吗?与总执行时间相比,锁定等待时间是多少?
-
谢谢@akuzminsky。基本上按照您的输入,我检查了慢日志,它只显示上面的插入需要很长时间,没有发现任何其他锁定等...但是这指示我检查事务,在那里我发现一个 SP它正在向不同的表运行插入,但从 cb_flights 中选择是由于插入选择语句而被锁定的表。这是使用事件调度程序每 10 分钟左右运行一次,我更新了 SP 以使用临时表,现在一切运行顺利。
-
记住您可以进行死锁检测,A) innodb_deadlock_detect=ON 和 B) innodb_print_all_deadlocks=ON 在错误日志中包含详细信息。始终同时打开并每天、每周或每月检查错误日志以了解新错误何时抬起丑陋的脑袋并不是一个坏主意。
-
"insert only non duplicates" -- 请注意,它只检查唯一键以确定行是否重复。