项目中有一个批处理任务,用来删除数据库中过期的数据(包括说话人的语音、模型、记录等),当程序被分布式部署后,就会有多个批处理线程同时进行删除,不过不同的线程,会根据元信息表得到不同的说话人信息,从而删除不同的数据,并不存在竞争的问题,但是,当项目使用oracle数据库在线上运行时,却频繁出现了ORA-00060: deadlock detected while waiting for resource的错误。在进行错误调试时,打印出了sql语句,发现程序在delete from smb.voice where id = ?时出现了deadlock,根据打印出的信息,发现并发的线程删除的voice id没有重叠的现象,id为主键,根据id来删除具体的行记录,获取到的应该为行锁,两个并发并发线程不会产生锁竞争,为什么会出现deadlock,非常郁闷。
于是登录oracle数据库去查看trace file,在trace file中发现了deadlock的相关信息,如下:
从上述信息可以看出,两个session都holds SX(行级排他锁),在waits SSX(共享行级排他锁),因为我对SX锁我知道,根据id来删除记录,会产生SX排它锁,正常,不过,它们等待的SSX锁到底是什么?通过查询,我知道SSX同一时间只允许一个事务持有和修改锁定的表,其他事务可以查询但不能修改,也就是说SSX是表级别的锁,这也就是此处deadlock的罪魁祸首,但是为什么会出现表锁喃?又是锁住哪张表的喃?我无法根据上面的信息,看出锁住的是哪张表?就用一些信息进行了google,发现了一个页面上描述的问题的trace file和我的很像,网站:https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:3932525800346405986,而下面的解决方案中有如下信息:
do you
1) delete from the parent table
2) update the parent table primary key (even if just setting it to itself, many applications do that for some reason)
3) merge into the parent table.
2)、3)的情况我都不存在,但是我要delete的voice表确实是主表(parent table),它被另一张记录表verification_record表所依赖(利用外键进行依赖),于是我开始研究外键与deadlock之间的关系。发现了一个现象:oracle中外键不加索引,当对主表进行操作时,会产生死锁。因为当表之间存在外键约束时,你对主表进行更新或删除操作时,都会去全表扫描子表,找到外键列的行记录,确保数据的一致性,这样就产生了SSX锁,解决的方案为在子表的外键列加索引,这样扫描索引即可,不会全表扫描,也就不会产生SSX锁,但是因为在我们的系统的主业务流程中,数据的插入操作很频繁,建立过多索引会严重影响主业务的效率,于是我们取消了数据库的外键约束,由程序确保数据表的一致性,问题解决。