大家好,今天咱们来聊聊数据库锁机制,这可是并发控制里的大杀器。数据库锁就像一把钥匙,谁拿到了谁就拥有数据,别人只能等着。如果没有它,多个人一起改同一个数据,那肯定会乱套。 先来看看锁的粒度吧,这关系到能锁多少东西。 第一种是表级锁,一把锁把整张表都给锁死了。这招速度快、开销小,不会死锁,但并发性太差,大家抢着要的时候冲突特别多。主要是给MyISAM引擎和做DDL操作的时候用。 第二种是行级锁,只锁一行或者几行。并发性高、冲突少,但操作起来慢、开销大,有时候还会搞出死锁来。这可是InnoDB引擎在高并发环境下的大绝招。 还有一种是页级锁,锁一整页数据。它的开销和并发性介于前面两者之间,现在用得不多了,以前BDB引擎常用这个。 接下来是锁的模式,也就是你拿到钥匙后能干啥。 共享锁(S锁)就是让你读数据用的。好多人可以同时拿着这个读锁不打架。不过它会拦着别人给这块数据加写锁。你要是想读个数据的时候顺便把读锁给加上了,SQL里写上 LOCK IN SHARE MODE 就行了。 排他锁(X锁)权限最大,不光能读还能改。这时候谁都别想动这块数据了。UPDATE、DELETE、INSERT 这些操作,或者是 SELECT ... FOR UPDATE 语句都会自动给数据加上这种写锁。 再说说 InnoDB 的行级锁是咋实现的,特别是“可重复读”模式下这几招很管用。 记录锁(Record Lock)最基础,就是锁索引里的单条记录。 间隙锁(Gap Lock)比较玄乎,它锁住的不是具体的数据记录,而是两个记录之间的空隙。这样能防止别人在中间插数据,避免那种叫“幻读”的问题出现。 临键锁(Next-Key Lock)就厉害了,它把记录锁和间隙锁合二为一,锁定了一个范围并且还包括最后那个记录本身。这是 InnoDB 在“可重复读”模式下的默认算法。 为什么还要搞意向锁呢?意向锁其实是一种“我要锁行”的预告信。它有两种类型:意向共享锁(IS)和意向排他锁(IX)。比如你打算在某几行上加个共享锁或者排他锁,就先把意向锁给加上。 这样做主要是为了省事。要是直接给整张表加个大锁,数据库还得一行一行去查有没有其他的行锁存在,那得查到猴年马月去。有了意向锁以后,数据库只要看看有没有 IS 或者 IX 锁就行啦。 死锁这事儿挺烦人的。就是两个或者多个事务互相拿着对方想要的钥匙不放,双方都在等着对方先松手。 典型的例子是这样的:事务 A 锁住了订单1001,然后想拿订单1002。与此同时事务 B 锁住了订单1002,也想拿订单1001。结果 A 在等 B 放1002,B 在等 A 放1001,谁也别想走。 这时候数据库系统比如 InnoDB 就会自动出来管事了。它会挑个损失最小的(一般就是代价小的那个)把它给回滚掉并报错,这样就把僵局给打破了。 最后说说两种大策略:悲观锁和乐观锁。 悲观锁就是往最坏的地方想,觉得打架的事儿肯定有。所以在动手之前就先把门锁上(加锁),整个过程中一直拿着钥匙不让人碰。 这种策略适合那种写操作特别多、大家抢得厉害的地方,比如银行转账、扣库存什么的。 乐观锁就比较想得开了。觉得大家都很客气,很少打架。所以干活的时候不加锁(不用钥匙),等到最后要提交的时候再看看数据是不是被别人动过了。 这招适合那种读的人多写的人少、吵架概率低的地方。现代数据库系统(比如 MySQL 的 InnoDB)一般会把这两种策略结合起来用。 再配合上多版本并发控制(MVCC),就能让读的人不耽误写的人干活了。这样就能把系统的并发能力提得更高。