乐观锁和悲观锁
悲观锁:就是假设每次操作数据时都会认为会产生冲突,所以每次操作都都会通过获取锁来进行操作数据
乐观锁:乐观锁认为一般情况下数据不会造成冲突,所以在数据进行提交更新时才会对数据的冲突与否进行检测。其特点就是先进行业务操作,不到最后式不会去拿锁的,乐观的认为拿锁多半是成功的。
共享锁和排他锁
共享锁和排他锁都是悲观锁的实现。其中共享锁又称为读锁,其他用户可以并发读取数据,但任何事物都不能对其数据进行修改,是在读取的时候创建的语法为:
start transaction;
select * from table where id =1 lock in share mode;
commit;
排他锁又称为写锁:加了排他锁,只能对其对这个事务进行读写,在此结束前,其他事务不能对其加任何锁,其他进程可以读取,不能写操作,语法为:
begin/ begin work/ start transaction;
select * from table where id =1 for update;
commit;
乐观锁的实现
乐观锁在数据库上的实现完全式逻辑的 数据库本身并不支持需要自己开发。
实现办法:
版本号控制原理:
1.为表加一个version字段
2.当读取数据时,连同这个version字段一起读出来
3.数据每更新一次就给该值加1
4.当提交更新时,判断数据库表中对应的记录的当前版本号是否与之前取出来的版本号一致,如果一致则可以直接更新。如果不一致则表示表示过期数据需要重试
版本号机制
一般是在数据表中加上版本号字段 version,表示数据被修改的次数。当数据被修改时,这个字段值会加1。
举个简单的例子:假设帐户信息表中有一个 version 字段,当前值为 1 ,而当前帐户的余额( balance )为 100 。
操作员 A 此时准备将其读出( version=1 ),并从其帐户余额中扣除 50( 100-50 );
操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 20 ( 100-20 );
操作员 A 完成修改工作,将数据版本号加1( version=2 ),连同帐户扣除后余额( balance=50 ),提交到数据库完成更新;
操作员 B 完成了操作,也将版本号加1( version=2 )试图向数据库提交数据( balance=80 ),但此时比对数据库记录版本发现,操作员 B 提交的数据版本号为 2 ,数据库记录的当前版本也为 2 ,不满足 “提交版本必须大于记录当前版本才能执行更新“ 的乐观锁策略。
因此,操作员 B 的提交被驳回。这样,就避免了操作员 B 用基于 version=1 的旧数据修改,最终造成覆盖操作员 A 操作结果的可能。
CAS
行锁和表锁
行锁和表锁也是属于悲观锁的一种试按照锁的对象来划分的
行锁的特点:锁的粒度小,发生锁冲突的概率低、处理并发的能力强;开销大、加锁慢、会出现死锁
加锁的方式:自动加锁。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任何锁。
表级锁特点:开销小、加锁快、无死锁;锁粒度大,发生锁冲突的概率高,高并发下性能低
加锁的方式:自动加锁。查询操作(SELECT),会自动给涉及的所有表加读锁,更新操作(UPDATE、DELETE、INSERT),会自动给涉及的表加写锁。
脏读:当一个事务执行过程中修改了某个数据,这时,另一个事务读到这个数据叫做脏读,因为如果第一个事务进行回滚的话,那么数据本身没有改变,其他事务读到的是错误的数据。
虚读:当事务读取某个数据后,另一个数据对该数据进行了修改,而第一个事务再次读取该数据就会发现两次读取结果不一致。
幻读:一个事务读取几行数据后,另一个事务插入了几行数据,则第一个事务再次读取时就会发现多了几行数据,和虚读的区别在于,虚读是针对一个数据的重复读不一致问题,幻读是对一组数据重复读的不一致。虚读的重点是修改,幻读的重点在于新增或者删除。