背景mysql
考虑下面两个并发带来的问题:sql
一、丢失更新:一个事务的更新结果覆盖了其它事务的更新结果,即所谓的更新丢失。数据库
二、脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。bash
例如:并发
两个用户同时修改商品库存表,A、B同时进入,看到的库存都是100,A购买一件把库存修改成99(100-1)。此时B购买两件把库存修改成98(100-2),由于A、B同时读到的库存都是100,B并不能看到A作的库存更新,因此形成B脏读,形成A丢失更新。性能
因此为了解决这些并发带来的问题。 咱们须要引入并发控制机制--锁。ui
锁分类spa
悲观锁3d
悲观锁就是用户修改数据时看起来很悲观,保守态度,担忧别的用户会同时修改这条数据,因此每次修改时会提早把这条数据锁定起来,只有本身可修改(但别的用户能够读),等本身修改完了再释放锁。code
乐观锁
乐观锁就是用户修改数据时心态很乐观,无论别人修改不修改数据,我都不上锁,我修改的时候判断下数据有没有发生变化,没发生变化我就会更新成功,发生变化了就不会更新成功我再去重试以前的动做直到更新成功。
锁应用
悲观锁
使用悲观锁的时候咱们首先必须关闭mysql数据库的自动提交属性,由于MySQL默认使用autocommit模式,也就是说,当你执行一个更新操做后,MySQL会马上将结果进行提交。
关闭命令为:set autocommit=0;
悲观锁通常使用select…for update实现,在执行的时候会锁定数据,虽然会锁定数据,可是不影响其余事务的普通查询使用。
在咱们使用悲观锁的时候事务中的语句例如:
//开始事务
begin;/begin work;/start transaction; (三选一)
//查询信息
select * from order where id=1 for update;
//修改信息
update order set name='names';
//提交事务
commit;/commit work;(二选一)
复制代码
此处的查询语句for update关键字,在事务中只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一条数据时会等待其它事务结束后才执行,通常的SELECT查询则不受影响。
注意事项
执行事务时关键字select…for update会锁定数据,防止其余事务更改数据。可是锁定数据也是有规则的。
查询条件与锁定范围:
好比查询条件为主键ID=1等等,若是此条数据存在,则锁定当前行数据,若是不存在,则不锁定。
好比查询条件为主键ID>1等等,此时会锁定整张数据表。
会锁定整张数据表。
明确指定索引而且查到,则锁定整条数据。若是找不到指定索引数据,则不加锁。
乐观锁
例如表
student(id,name,version)
1 a 1
复制代码
当事务一进行更新操做:
update student set name='txt' where id = #{id} and version = #{version};
复制代码
此时操做完后数据会变为id = 1,name = txt,version = 2,当另一个事务二一样执行更新操做的时候,却发现version != 1,此时事务二就会操做失败,从而保证了数据的正确性。
二、使用时间戳来实现,原理同上。
三、使用其余数据库字段,如:金额,更新时添加条件判断金额是否变化,原理同上。
乐观锁图示
结论
两种锁各有优缺点,不能单纯的定义哪一个好于哪一个。乐观锁比较适合数据修改比较少,读取比较频繁的场景,即便出现了少许的冲突,这样也省去了大量的锁的开销,故而提升了系统的吞吐量。可是若是常常发生冲突(写数据比较多的状况下),上层应用不不断的retry,这样反而下降了性能,对于这种状况使用悲观锁就更合适。