悲观锁介绍(百科):
悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作都某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。
悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。
Mysql InnoDB 排他锁实现悲观锁
用法:select…for update;
与普通查询不一样的是,我们使用了select…for update的方式,这样就通过数据库实现了悲观锁。此时在表X中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。
行级锁 (Row Lock)或 表级锁(Table Lock):
使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住);
当然Mysql也对for update做了一些优化,并不是没有指定主键,一定会触发表级锁;比如使用索引也会影响数据库的锁定级别。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,注意这里只是影响。具体可以参考:mysql事务和锁InnoDB
Mysql InnoDB悲观锁的使用:
要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。
set autocommit=0;
我们的电商设计中是下单扣库存,所以用户下单的时候,会调用商品系统的库存扣减服务,此时商品系统是根据库存行记录进行加悲观锁做库存扣减的,虽然这种方式比较安全,可以一定程度上减少超卖的风险,但是在下单并发非常高的时候,Mysql的悲观锁就会显得效率很低。
应对这种场景有几种解决方案:
①阿里修改了Mysql底层的源码,提高了悲观锁的效率;
②使用Redis进行库存计算,DB作为最终一致性的数据存储;
③请求端对库存扣减进行合并统一处理;
其它参考:
http://www.cnblogs.com/zhaoyl/p/4121010.html
http://hedengcheng.com/?p=771