最近遇到并发太高操作同一张表,经常会报错911,导致事务回滚的状况。
查找报错的源码部分,发现一段代码的挨得比较近两条update操作导致的
1、根据主键更新记录
2、根据手机号更新记录
问题分析:
分析DB2的锁机制,由于DB2 默认是CS的隔离级别,它的原理是,游标每到一行就会锁住改行,对于一般应用来说是足够了,但是如果遇到全表扫描,那么CS模式会锁住表中大量的行,直到查询完毕。
所以步骤一的事务执行时候,由于主键有索引所以此时是行锁。步骤二执行没有索引此时会全表扫描锁住大量行。当需要锁住步骤一种的行时,如果被事务占用,就会等待,这种情况下一般不会导致问题。
当步骤2并发太多时就会碰到,由于没索引更新记录,每次都锁了一张表的大量行,导致下一个扫描需要锁的时候发现已经被锁了,这个时候就会出现问题。
DB2的锁注意事项:
Ø 优化查询 Sql,建立合适的索引,使得其能够走索引查询,由于索引的范围和排序,可以直接跳过许其他行,定位到符合我们需要的行。
Ø 采用合适的隔离级别。由于DB2 默认是CS的隔离级别,它的原理是,游标每到一行就会锁住改行,对于一般应用来说是足够了,但是如果遇到全表扫描,那么CS模式会锁住表中大量的行,直到查询完毕。所以可以根据业务需求,将其改为UR模式,它不会对表加任何行锁。或者在JDBC中设置隔离级别(Isolation Levels)
Ø 合理设置锁超时参数,它主要是用来避免事务长时间被占用,导致锁和连接无法释放,影响系统的并发。可以设置 DB参数
Ø 更新操作一定要走索引,否则很容易产生死锁。(针对边更新边查的操作)
Ø 避免出现锁升级现象,当锁等待达到一定程度时(行锁的个数超过loctList *percent of lock list),就会出现行锁升级为表锁,即锁升级。因为一旦出现锁升级,那么锁住的就不再是行,而是表,那么其他事务要想访问该表中的任意行,必须等待事务将锁释放。
修改Lock timeout (-1代表不检测锁超时),一般来说,该参数默认为10s足矣。
当系统存在严重的锁等待时,可以通过以下sql,定位到锁等待Sql
db2 "select AGENT_ID ,substr(STMT_TEXT,1,100) as statement,STMT_ELAPSED_TIME_MS from table(SNAPSHOT_STATEMENT('dbname',-1)) as B where AGENT_ID in (select AGENT_ID_HOLDING_LK from table(SNAPSHOT_LOCKWAIT(‘dbname’,-1)) as A order by LOCK_WAIT_START_TIME ASC FETCH FIRST 20 ROWS ONLY ) order by STMT_ELAPSED_TIME_MS DESC"
感谢此文作者