测试用表如下及下图1:
drop table ttt;
create table ttt (id1 int NOT NULLAUTO_INCREMENT , id2 int ,PRIMARYkey(id1));
INSERT into ttt VALUES (1,100);
show variables like '%iso%';
(图1)
使用python脚本 session_AA及session_BB(两个脚本完全一致,详细代码文章末尾session_AA.py)来做两个会话关于tidb事务的测试。
Session_AA 输出结果如下
Session_BB 输出结果如下
注意上面两图里两个会话的调度时间差(红色框框内),在python脚本里,使用了time sleep 10s,来保证session_bb在调度时,session_aa还没有正常提交
在两个会话执行完成后:表ttt结果为-100
且所连接的tidb服务器 tidb.log如下图以及如下文字
cd /u01/tidata/log
cat tidb.log
2018/03/24 16:23:03.203 server.go:118: [info][17] new connection 192.168.121.84:60111
2018/03/24 16:23:03.213 set.go:157: [info][17] set system variable autocommit = 0
2018/03/24 16:23:05.844 server.go:118:[info] [18] new connection 192.168.121.84:60113
2018/03/24 16:23:05.865 set.go:157: [info][18] set system variable autocommit = 0
2018/03/24 16:23:13.222 metrics.go:355:[warning] [EXPENSIVE_QUERY] select * from ttt
2018/03/24 16:23:13.251 server.go:278:[info] [17] close connection
2018/03/24 16:23:15.872 metrics.go:355:[warning] [EXPENSIVE_QUERY] select * from ttt
2018/03/24 16:23:15.880 session.go:335:[warning] [18] retryable error: [try again later]: tikv restarts txn:Txn(Mvcc(WriteConflict { start_ts: 398951654575570949, conflict_ts:398951656502591489, key: [116, 128, 0, 0, 0, 0, 0, 4, 255, 145, 95, 114, 128,0, 0, 0, 0, 255, 0, 0, 1, 0, 0, 0, 0, 0, 250], primary: [116, 128, 0, 0, 0, 0,0, 4, 145, 95, 114, 128, 0, 0, 0, 0, 0, 0, 1] })), txn: <nil>
2018/03/24 16:23:15.880 session.go:455:[warning] [18] Retry [0] query [0] update ttt a set a.id2 = id2-100 where a.id1= 1
2018/03/24 16:23:15.880 2pc.go:576: [info]2PC clean up done, tid: 398951654575570949
2018/03/24 16:23:15.884 session.go:455:[warning] [18] Retry [0] query [1] COMMIT
2018/03/24 16:23:15.904 server.go:278: [info][18] close connection
官方文档事务定义:
从tidb 服务器的 tidb.log日志来看,的确在session_BB commit了检测了冲突并做了重试。
但是根据实际业务场景:假设存在这种同一用户并发的情况,tidb的事务重试机制可能会产生一些错误数据
待解决问题:综合以上比如在并发情况下账户提现的测试,该如何规避以上错误数据问题。
队列?
关闭tidb重试?
业务层多app端,只能单点在线,或者能同时在线,pc端与app端只能有一端可以操作对数据正确性要求较高的业务模块?
欢迎感兴趣同仁讨论。
测试脚本session_AA.py:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
#####################################
## created by basededato ##
## 2018-03-24 ##
#####################################
import pymysql
import time
# 脚本执行时间,以保证session_BB 与 session_AA 的输出时间差小于 下面的sleep time
print(time.asctime( time.localtime(time.time()) ))
conn= pymysql.connect(
host = '192.168.121.217',
port = 5919,
user = 'root',
passwd = 'root',
db = 'uat_exue',
cursorclass = pymysql.cursors.DictCursor
)
cur = conn.cursor()
#cur.execute('update ttt a set a.id2 = id2-100 where a.id1 = 1')
cur.execute('insert into ttt values (1,100)')
# 休眠时间,以保证 session_BB 在调度时,session_AA 还未提交
time.sleep(10)
reslut=cur.execute('select * from ttt where id1 = 1')
msg = cur.fetchmany(reslut)
for output in msg:
print(output['id2'])
if output['id2'] < 0:
conn.rollback()
print('事务回退')
else:
conn.commit()
print('事务提交')
cur.close()
conn.close()