数据库的死锁: 和 JAVA的 死锁类似 ,条件 ,两个事务(线程) ,事务1 和事务2 ,事务1 要拿到 了 锁a ,等待 锁 b ,事务2 拿到了 锁b ,等待锁a。 这时候就死锁了。
备注: java 我们很明显的 知道什么时候加的锁什么时候释放锁,比如 synch 方法( 进入方法前获取锁,方法执行完毕释放锁 ), synch 语句块( 进入语句块 和退出 语句块 ) ,lock 接口( lock 和 unlock 的时候 )。
数据库加锁在什么时候呢?
MySQL 为例, mysql 的写是加的 写锁,也就是独占锁, mysql innodb 的读 因为有个非锁定读的机制,所以 读的时候不需要加锁,并且读的时候别的事务 也能改这份数据,但是 读线程读到的依旧是 事务启动的时候的数据( RR 事务隔离级别 描述的 可重复读)。
也就是 innodb 读不加锁。但是写加了锁,在什么时候加锁呢? 在我们执行一条 update 语句的时候。 在什么时候释放锁呢? 在事务提交的时候。
时间 事务a 开始 事务b 开始
t1 1 随便 读点啥..... 1 随便 搞点啥
t2 2 update 资源 1 锁 资源1,如果拿不到就等待 2 update 资源2 锁定资源2 ,锁不到就等待
t3 3 随便搞搞点 啥 3 随便搞点啥
t4 4 update 资源 2 锁资源2,如果拿不到就等待 4 update 资源1 锁定资源1 ,锁不到就等待
上面说过 锁会在事务提交的时候释放,所以 两个事务就锁死了。
做实验的时候可以 关闭mysql 的自动提交 ,然后2 个窗口敲 。或者 java代码里面通过 java的 CyclicBarrier 让两个事务 在 t3 的时间点对齐 。
然后说说数据库死锁的解决办法。
1 编码规范有计划的顺序更新数据避免出现 相互抱锁的 问题。
2 数据库线程有超时 机制,一个事务超时,锁被释放,另一个线程就会向下执行了。
3 数据库有死锁检查 机制,发现死锁的时候 会让 更新少的 一个 事务回滚。
innodb_deadlock_detect=on 之歌参数默认是打开的,也就是mysql 默认就会处理死锁问题。处理方式就是回滚 跟新少的事务。
4 mysql 如果关闭了 死锁回滚 可以配置 等锁超时 时间 innodb_lock_wait_timeout 。 默认是 5 0 秒。