最近,使用sysbench测试时,尝试使用参数--skip_trx=on
,很快在实际的测试中就遇到了Duplicate entry
的报错,详细的报错如下:
[ 105s ] thds: 64 tps: 845.68 qps: 15226.20 (r/w/o: 11833.15/3393.04/0.00) lat (ms,95%): 82.96 err/s: 0.00 reconn/s: 0.00
[ 108s ] thds: 64 tps: 828.65 qps: 14846.32 (r/w/o: 11547.73/3298.59/0.00) lat (ms,95%): 92.42 err/s: 0.00 reconn/s: 0.00
[ 111s ] thds: 64 tps: 811.67 qps: 14705.36 (r/w/o: 11439.69/3265.67/0.00) lat (ms,95%): 92.42 err/s: 0.00 reconn/s: 0.00
FATAL: mysql_drv_query() returned error 1062 (Duplicate entry '172978' for key 'sbtest4.PRIMARY') for query 'INSERT INTO sbtest4 (id, k, c, pad) V
ALUES (172978, 743044, '85734897298-37760631172-31656179599-77290009462-94351507893-97022333300-02606364258-99231394161-86310536236-00514105136',
'50908340877-51595671823-98046322819-52667567569-56801127593')'
FATAL: `thread_run' function failed: /usr/share/sysbench/oltp_common.lua:488: SQL error, errno = 1062, state = '23000': Duplicate entry '172978' f
or key 'sbtest4.PRIMARY'
也注意到,这个错误的出现有一定的偶发性,但是高并发、长时间压测几乎一定会遇到了(在开启了--skip_trx=on
参数后)。因为Sysbench没有对”Duplicate entry”该错误进行处理,测试会退出,也就无法正常完成测试。
原因分析
当多个线程并发时,同时没有使用--skip_trx=on
,而是使用MySQL默认的auto commit模式,那么在oltp_read_write
模型下则一定的概率(小概率)会出现如下的场景:
时间线 | 线程A | 线程B |
1 | 生成随机ID X | |
2 | 生成随机ID X | |
3 | 删除 id 为 X 的记录 delete from sbtest where id = X | |
4 | 删除 id 为 X 的记录 delete from sbtest where id = X | |
5 | 写入 id 为 X 的记录 insert into sbtest (id…) values ( X …) | |
6 | 写入 id 为 X 的记录 insert into sbtest (id…) values ( X …) |
在上面的场景下,最后一步,线程B再次写入id 为 X 的记录时,则会出现冲突。
一般来说,即便发生如上情况,也不会出现Duplicate entry
的报错。但,组合一些情况,则会出现。例如,在这里,我们使用了--skip_trx=on
,那么线程A的如上行为不是在一个事务中,每个操作是一个独立的事务,那么就会出现Duplicate entry
报错。
如果没有使用--skip_trx=on
参数,那么在线程2尝试删除记录时,则会遇到锁等待,直到线程1的相关操作全部完成。也就不会出现报错。
避免该错误
在开启了–skip_trx=on之后,如果运行时间足够长,且是多线程并发,则几乎一定会遇到如上错误。可以考虑如下方案避免:
- 修改sysbench使用MySQL的
INSERT ... ON DUPLICATE KEY UPDATE
(参考)替换原始的INSERT
语句 - 尝试文档sysbench 1.0: teaching an old dog new tricks中使用hook尝试处理
一般来说,因为当表的记录数非常多时,遇到该类冲突的概率比较小,做如上处理并不会影响测试的“一致性”。
function sysbench.hooks.sql_error_ignorable(err)
if err.sql_errno == 1062 then -- ER_DUP_ENTRY
-- do nothing
-- con:reconnect()
return true
end
end
Leave a Reply