002-MySQL中的锁

三、MySQL锁

  1. 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  2. 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
  3. 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

粒度越小并发度越高,锁冲突的机率越低,加锁速度越慢,开销越大。

3.1 Mysql中MyISAM和INNODB

共有5种类型的表:
MyISAM
Heap
Merge
INNODB
ISAM
简述在MySQL数据库中MyISAMInnoDB的区别

MyISAM:
  • 不支持事务,但是每次查询都是原子的;
  • 支持表级锁,即每次操作是对整个表加锁
  • 存储表的总行数(所以进行count()时更加 牛逼);
  • 一个MYISAM表有三个文件:索引文件、表结构文件、数据文件

采用非聚集索引索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性

InnoDb:

支持ACID的事务,支持事务的四种隔离级别;支持行级锁及外键约束:因此可以支持写并发;不存储总行数;

事务特性ACID:
(1)原子性:即不可分割性,事务要么全部被执行,要么就全部不被执行。
(2)一致性:事务的执行使得数据库从一种正确状态转换成另一种正确状态
(3)隔离性:在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务。
(4)持久性: 事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。

一个InnoDb引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受操作系统文件大小限制,一般为2G),受操作系统文件大小的限制;

主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值 (value);因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问主索引;最好使用自增主键,防止插入数据时,为维持B+树结构,文件的大调整。

3.2 乐观锁和悲观锁
(1)乐观锁

简单地说,就是从应用系统层面上做并发控制,去加锁。

实现乐观锁常见的方式:

版本号version。实现方式,在数据表中增加版本号字段,每次对一条数据做更新之前,先查出该条数据的版本号,每次更新数据都会对版本号进行更新。在更新时,把之前查出的版本号跟库中数据的版本号进行比对,如果相同,则说明该条数据没有被修改过,执行更新。如果比对的结果是不一致的,则说明该条数据已经被其他人修改过了,则不更新,客户端进行相应的操作提醒。

//1.查询出商品信息 
select status,version from t_goods where id=#{id} 
//2.根据商品信息生成订单 
//3.修改商品status为2 
update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};

注意第二个事务执行update时,第一个事务已经提交了,所以第二个事务能够读取到第一个事务修改的version。

(2)悲观锁

简单地说,就是从数据库层面上做并发控制,去加锁。

悲观锁的实现方式有两种:共享锁(读锁)和排它锁(写锁)

详细说明

  • 相信很多朋友在面试的时候,都会被问到乐观锁和悲观锁的问题,如果不清楚其概念和用法的情况下,相信很多朋友都会感觉很懵逼,那么面试的结果也就不言而喻了。

那么乐观锁和悲观锁到底是个什么东西,用它能来做什么呢?

相信大家都遇到这种场景,当很多人(一两个人估计不行)同时对同一条数据做修改的时候,那么数据的最终结果是怎样的呢?
这也就是我们说的并发情况,这样会导致以下两种结果:

  • 更新错误,你修改之后的数据可能被别人覆盖了,导致你很懵逼,甚至怀疑自己开发的功能是否有问题;

  • 脏读,数据更新错误,导致读数据也是错的,查询出一些默认奇妙的数据,看到的不是你自己修改的结果。

二、悲观锁

悲观锁,简单地说,就是从数据库层面上做并发控制,去加锁。
悲观锁的实现方式有两种:共享锁(读锁)和排它锁(写锁)

  1. 共享锁(IS锁),实现方式是在sql后加LOCK IN SHARE MODE,比如SELECT ... LOCK IN SHARE MODE,即在符合条件的rows上都加了共享锁,这样的话,其他session可以读取这些记录,也可以继续添加IS锁,但是无法修改这些记录直到你这个加锁的session执行完成(否则直接锁等待超时)。
  2. 排它锁(IX锁),实现方式是在sql后加FOR UPDATE,比如SELECT … FOR UPDATE ,即在符合条件的rows上都加了排它锁,其他session也就无法在这些记录上添加任何的S锁或X锁。如果不存在一致性非锁定读的话,那么其他session是无法读取和修改这些记录的,但是innodb有非锁定读(快照读并不需要加锁),for update之后并不会阻塞其他session的快照读取操作,除了select …lock in share mode和select … for update这种显示加锁的查询操作。

通过对比,发现for update的加锁方式无非是比lock in share mode的方式多阻塞了select…lock in share mode的查询方式,并不会阻塞快照读。

mysql InnoDB引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型。

在Java中,synchronized的思想也是悲观锁。

以排它锁为例

要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用auto commit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。set autocommit=0;

//0.开始事务 

begin;/begin work;/start transaction; (三者选一就可以) 

//1.查询出商品信息 

select status from t_goods where id=1 for update; 

//2.根据商品信息生成订单 

insert into t_orders (id,goods_id) values (null,1); 

//3.修改商品status为2 

update t_goods set status=2; 

//4.提交事务 

commit;/commit work;

上面的查询语句中,我们使用了select…for update的方式, 这样就通过开启排他锁的方式实现了悲观锁。此时在t_goods表中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。

补充:

  • 1.MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作 (UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁。
  • 2.MySQL InnoDB默认行级锁。 行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住。
  • 3.从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适

————————————————

原文链接:https://blog.csdn.net/u013521882/article/details/85275987

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值