Pessimistic write conflict, retry statement

请问下出现这种问题,我怎么定位是哪里发生冲突了呢?

原因:

因为输出的报错不全,不过通过字面意思可以确认是悲观事务的写入冲突既 8005 报错。

解决办法:

通过 tidb.log 可以定位到具体冲突操作的 SQL,并根据 SQL 来分析原因。

[2019/08/15 15:38:05.624 +08:00] [INFO] [set.go:190] [“set session var”] [conn=46411] [name=autocommit] [val=1] [2019/08/15 15:38:05.625 +08:00] [INFO] [adapter.go:464] [“pessimistic write conflict, retry statement”] [conn=39878] [txn=410479410387353605] [forUpdateTS=410479410675712001] [conflictCommitTS=410479410701926401] [2019/08/15 15:38:05.625 +08:00] [INFO] [session.go:1847] [GENERAL_LOG] [conn=46411] [user=root@.] [schemaVersion=1152] [txnStartTS=0] [current_db=tonder_test] [sql=“select @@session.tx_read_only”] [2019/08/15 15:38:05.626 +08:00] [INFO] [adapter.go:464] [“pessimistic write conflict, retry statement”] [conn=46396] [txn=410479410413568034] [forUpdateTS=410479410675712001] [conflictCommitTS=410479410701926401] [2019/08/15 15:38:05.626 +08:00] [INFO] [session.go:1847] [GENERAL_LOG] [conn=46411] [user=root@.] [schemaVersion=1152] [txnStartTS=0] [current_db=tonder_test] [sql=“SET autocommit=0”] [2019/08/15 15:38:05.626 +08:00] [INFO] [set.go:190] [“set session var”] [conn=46411] [name=autocommit] [val=0] [2019/08/15 15:38:05.627 +08:00] [INFO] [adapter.go:464] [“pessimistic write conflict, retry statement”] [conn=46395] [txn=410479410426675241] [forUpdateTS=410479410675712001] [conflictCommitTS=410479410701926401] [2019/08/15 15:38:05.628 +08:00] [INFO] [session.go:1847] [GENERAL_LOG] [conn=46411] [user=root@.] [schemaVersion=1152] [txnStartTS=0] [current_db=tonder_test] [sql=“select * from tc_cle_record where payment_order_id=‘820190815152110034168256681012’”] [2019/08/15 15:38:05.628 +08:00] [INFO] [adapter.go:464] [“pessimistic write conflict, retry statement”] [conn=39883] [txn=410479410439782431] [forUpdateTS=410479410675712001] [conflictCommitTS=410479410701926401] [2019/08/15 15:38:05.628 +08:00] [INFO] [session.go:1847] [GENERAL_LOG] [conn=39878] [user=root@.] [schemaVersion=1152] [txnStartTS=410479410387353605] [current_db=tonder_test] [sql=“select * from tc_payment_agents_payment_conf where agent_id=‘1000000408’ and payment_type_id=1”] [2019/08/15 15:38:05.629 +08:00] [INFO] [adapter.go:464] [“pessimistic write conflict, retry statement”] [conn=39884] [txn=410479410452889624] [forUpdateTS=410479410675712001] [conflictCommitTS=410479410701926401]

是要根据Timestamp这些来判断吗?

需要通过时间戳和事务开启时间来判断

大佬,能说个具体的方法吗?:grinning:现在日志里面有general log和[“pessimistic write conflict, retry statement”] [conn=46395] [txn=410479707317862417] [forUpdateTS=410479707357184011] [conflictCommitTS=410479707370291217]感觉不知道从哪里看起

方法

  1. 通过 pd-ctl 工具,将 tso 可以转译到指定时间 https://pingcap.com/docs-cn/v3.0/reference/tools/pd-control/#tso
  2. 根据 "[conn=39883] [txn=410479410439782431] [forUpdateTS=410479410675712001] [conflictCommitTS=410479410701926401] "可以定位到具体是哪个是时间段事务,通过解决报错上线问题相关语句,在业务 log 和业务监控中找到冲突的报错。
  3. 另外如果 TiDB 侧已经出现 “pessimistic write conflict, retry statement” ,业务侧应该有报错反馈的。

日志介绍

因为已经开启悲观锁事务模式,“pessimistic write conflict, retry statement” 属于悲观锁事务冲突的输出,属于常规的日志报错。如果需要确认具体的冲突事务,需要确认看一下具体时间段的业务相关操作。

如果觉得别人的回答有帮助,可以将回答标记为解决方案:white_check_mark:,这样可以帮助其他有同样问题的人快速找到答案

好的,我在看:grinning:

1.多个事务同时去更新同一条数据,提交时都会去pd拿一个startTs,然后开始prewrite,给primary row上锁,再给其他的row上secondary key(primary row key + startTs),上锁成功会会进行primary commit,

如果 commit primary 成功,则可以异步的 commit secondaries,流程和 commit primary 一致, 失败了也无所谓。Primary row 提交的成功与否标志着整个事务是否提交成功。

为什么commit primary 成功了事务就算成功了, commit secondaries如果失败了,那secondary rows的数据不是没有变吗?这样怎么保证事务的原子性?

2.假如有a,b,c,d,e…等100事务(开启了悲观锁,设置了重试策略),同时更新去更新一条数据,每次只有一个能获得锁,其他的等锁的事务重试机制是什么样的呢?重试的时候就会报[“pessimistic write conflict, retry statement”] [conn=46395] [txn=410479707317862417] [forUpdateTS=410479707357184011] [conflictCommitTS=410479707370291217]?

3.[“pessimistic write conflict, retry statement”] [conn=46395] [txn=410479707317862417] [forUpdateTS=410479707357184011] [conflictCommitTS=410479707370291217]这条日志中的conflictCommitTS具体是指谁的commitTs?是获得并且提交成功的那个事务的commitTs?

https://github.com/pingcap/tidb/pull/11758 提了个 PR 修改 log 打印,后面的版本应该会把冲突的 key 打印出来 这样就更方便定位是哪里冲突

如果 commit primary 成功了,commit secondaries 是不会失败的。因为两阶段提交中, prewrite 其实已经把数据写成功了,持久化了… 而 commit 要做的只不过让数据对外可见。 事务的结果的[可见 / 不可见] 这个结果是原子的,因此事务原子性会得到保证

等前面的事务释放锁了之后,被锁 block 住的事务会再次重新读取新的数据然后尝试加锁,成功后再继续往下执行…

  1. [txn=410479707317862417 这个是当前的事务,然后 conflictCommitTS 是它跟哪个事务冲突(冲突事务的提交时间)了