(1)前序:
在多个事务并发更新数据的时候,都是要在行级别加独占锁的,这就是行锁,独占锁都是互斥的,所以不可能发生脏写问题,一个事务提交了才会释放自己的独占锁,唤醒下一个事务执行。
如果此时读取别的事务在更新的数据,有两种可能:
第一种是基于mvcc机制进行事务隔离,读取快照版本,这是比较常见的。
第二种可能是查询的同时基于特殊语法去加独占锁或者共享锁。
如果查询的时候加独占锁,那么跟其他更新数据的事务加的独占锁是互斥的;如果查询的时候加共享锁,那么跟其他查询加的共享锁是不互斥的,但是跟其他事务更新数据加的独占锁是互斥的,跟其他查询加的独占锁也是互斥的。
(2)经验:
不太建议在数据库粒度去通过行锁实现复杂的业务锁机制,而建议通过redis,zookeeper来用分布式锁实现复杂业务下的锁机制,更为合适。
原因:
因为如果分布式系统里的复杂业务的一些锁机制依托数据库查询的时候,在SQL语句里加共享锁或者独占锁,会导致这个加锁逻辑隐藏在SQL语句里,在Java业务系统层面不好维护,所以一般不建议这么做。
比较正常的情况,还是多个事务并发运行更新一条数据,默认加独占锁互斥,同时其他事务读取基于mvcc机制进行快照版本读,实现事务隔离。
(3)表锁:
在数据库里,不光可以通过查询中的特殊语法加行锁,比如 lock in share mode,for update等,还可以通过一些方式在表级别加锁。
有些人可能会以为当你执行增删改的时候默认加行锁,然后执行DDL语句时,比如alter table之类的语句,会默认加表锁。这么说也不太正确,但也有一定的道理,因为确实执行DDL的时候,会阻塞所有增删改操作。执行增删改操作会阻塞DDL操作。
但这是通过MySQL通用的元数据锁实现的,也就是MetaData locks,但这还不是表锁的概念。因为表锁其实是InnoDB存储引擎的概念,InnoDB存储引擎提供了自己的表级别锁,跟这里的DDL语句用的元数据锁还不是一个概念。
只不过DDL语句和增删改操作,确实是互斥的。
知识点:还有什么情况会锁表?需要结合**插入字段是否有索引和隔离级别**来看是否锁表。