博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解MySQL锁与事务隔离级别
阅读量:4170 次
发布时间:2019-05-26

本文共 4733 字,大约阅读时间需要 15 分钟。

1、锁定义

      锁是计算机协调多个进程或线程并发访问某一资源的机制。

      在数据库中,除了传统的计算资源(如CPU、RAM、I/O等)的争用以外, 数据也是一种供需要用户共享的资源。
      如何保证数据并发访问的一致性、有效性 是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个 重要因素。

2、锁分类

(1)、从性能上分为乐观锁(用版本对比来实现)和悲观锁

      乐观锁(用版本对比来实现)。比如说用数据库维护一个版本,表增加一个字段version
    比如存在一条数据,初始化的版本是V1,id字段也是1。
    查出这条数据,得到的版本号是V1,在当前事务中想要更新该字段的时候,需要先判断下更新的这条记录是否和当初从数据库查出的记录是一模一样的,一样才会更新。如果被其他线程更新过了,那当前更新就不能执行。
    简单说就是:其他线程更新完了,在此基础上不清楚是否更新过,直接更新的话很有可能导致其他线程的数据被覆盖掉。

      乐观锁相对于悲观锁来说,性能很好。乐观锁不能更新会直接返回,无需等待,而悲观锁会涉及到等待

(2)、从对数据库操作的类型分,分为读锁写锁(都属于悲观锁)

读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响(如果有线程过来想要写数据,是不可以的)
写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁

(3)、从对数据操作的粒度分,分为表锁行锁

3、表锁

每次操作锁住整张表

特点:

①开销小,加锁快; ps:一次性锁住整张表,不用定位到具体某一行。整张表就只会有一个表锁,不会像行锁会有那么多,开销小
②不会出现死锁; ps:对mysql来说,死锁其实就是几个线程分别操作不同的行,希望对其他线程正在操作的行数据加锁。而对于表锁来说,永远只会有一个线程在操作,能够拿到当前表的所就肯定不会出现死锁
③锁定粒度大,发生锁冲突的概率最高,并发度最低; ps:一个线程拿到表之后,必须操作完成,释放掉锁,其他线程才可继续操作,所以说并发度低,一次只能支持一个线程操作

3.1 基本操作

-- 建表SQL CREATE TABLE `mylock` (  `id` INT (11) NOT NULL AUTO_INCREMENT,  `NAME` VARCHAR (20) DEFAULT NULL,  PRIMARY KEY (`id`) ) ENGINE = MyISAM DEFAULT CHARSET = utf8;
-- 插入数据INSERT INTO`test`.`mylock` (`id`, `NAME`) VALUES ('1', 'a'); INSERT INTO`test`.`mylock` (`id`, `NAME`) VALUES ('2', 'b');INSERT INTO`test`.`mylock` (`id`, `NAME`) VALUES ('3', 'c'); INSERT INTO`test`.`mylock` (`id`, `NAME`) VALUES ('4', 'd');

手动增加表锁

lock table 表名称 read(write),表名称2 read(write);

查看表上加过的锁

show open tables;

删除表锁

unlock tables;

3.2 加表锁

在这里插入图片描述

现在我们已经对mylock表加了表锁,可以再打开一个mysql的窗口,来读取mylock的表数据,看一下结果如何?
在这里插入图片描述
可以看到,在一个session中加了表锁的读锁之后,其他session中的读数据是可以正常执行的。
那么可不可以在另一个session中对数据表进行写操作呢??
在这里插入图片描述
可以看到另一个session中的写操作被阻塞了,表锁在等待(在上面提到过,如果有表等待,则证明是悲观锁),也就说只有把表锁释放掉,写数据才会正常执行。

我们可以先看下数据库表加锁的状态

在这里插入图片描述
释放表锁,同时观察更新语句是否有变化
在这里插入图片描述
结论:
当前session和其他session都可以读该表
当前session中插入或者更新锁定的表都会报错,其他session插入或更新则会等待

到这里,小伙伴们可以思考下,表锁一般的使用场景是什么?一般哪种业务场景会使用到??

一般要对表做数据迁移或者全表做操作的话,,最好事先给表加一把锁,防止在迁移的过程中被其他的session或者线程修改数据。

3.3 结论

MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,

在执行增删改 操作前,会自动给涉及的表加写锁。

1、对MyISAM表读操作(加读锁) ,不会阻寒其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。

2、对MylSAM表写操作(加写锁) ,会阻塞其他进程对同一表的读和写操作,只有 当写锁释放后,才会执行其它进程的读写操作

总结:

简而言之,就是读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞。

4、行锁

每次操作锁住一行数据

特点:

①开销大,加锁慢; ps:需要定位到具体某一行,然后加锁,所以加锁慢。整张表会有很多行锁,所以开销大
②会出现死锁;
③锁定粒度最小,发生锁 冲突的概率最低,并发度最高; ps:一次可支持很多线程操作,只要是操作不同的行,加的是不同的行锁,所以并发度相对于表锁并发度高

InnoDB与MYISAM的最大不同有两点:

支持事务(TRANSACTION)
支持行级锁

4.1 基本操作

-- 建表语句CREATE TABLE `account` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `name` varchar(255) DEFAULT NULL,  `balance` int(11) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 插入数据INSERT INTO `test`.`account` (`name`, `balance`) VALUES ('lilei', '450'); INSERT INTO `test`.`account` (`name`, `balance`) VALUES ('hanmei', '16000');INSERT INTO `test`.`account` (`name`, `balance`) VALUES ('lucy', '2400');

查询现有数据

在这里插入图片描述
开启事务:(普通执行一条SQL语句,默认情况下是自动提交的,现在不想默认提交,可以使用begin操作)
在这里插入图片描述
现在更新数据:
在这里插入图片描述
显示执行成功,再次查询数据,也证明更新成功
在这里插入图片描述
但是其实该事务是没有提交的,现在不提交,在另外一个session中进行操作,先更新不同行的记录
在这里插入图片描述
可看到当前是修改成功的,那么接下来我们再去修改同一条记录,查看下效果
在这里插入图片描述
执行之后发现SQL阻塞了,也就是说id=3这条数据被第一个session中的事务加锁了。
这个时候我们再来提交事务看下效果
在这里插入图片描述

4.2 行锁支持事务

4.2.1 事务(Transaction)及其ACID属性

事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。

原子性(Atomicity)

事务是一个原子操作单元,其对数据的修改,要么全都执 行,要么全都不执行。

一致性(Consistent)

在事务开始和完成时,数据都必须保持一致状态。这意 味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束 时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。

隔离性(Isolation)

数据库系统提供一定的隔离机制,保证事务在不受外部并 发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是 不可见的,反之亦然。

持久性(Durable)

事务完成之后,它对于数据的修改是永久性的,即使出现系 统故障也能够保持。

4.2.2 并发事务处理带来的问题

更新丢失(Lost Update)

当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题–最后的更新覆盖了由其 他事务所做的更新。
eg:有两个线程在操作库存表,第一个线程查出的库存数量为10,减去库存5,剩余库存量应该是5。同一时刻,还有第二个线程也查出了库存是10,减去库存1,剩余库存量是9,这个时候如果第一个线程先更新,第二个线程后更新,那么就会把库存量为9更新回数据库,相当于第二个线程的结果把第一个线程的结果覆盖掉了

脏读(Dirty Reads)

一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此作进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫做“脏读”。
一句话:事务A读取到了事务B已经修改但尚未提交的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取的数据无效,不符合一致性要求。
eg:开启事务,有两个线程在操作库存表,第一个线程查出的库存数量为10,减去库存2,剩余库存量8,但是还未提交事务。同一时刻,该事务中第二个线程再去读的时候,查出了库存为8,又减去库存5,剩余库存量3。这个时候第一个线程发生异常,进行了rollback回滚操作,最终会把库存量3更新到数据库。

不可重读(Non-Repeatable Reads)

一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现 其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。
一句话:事务A读取到了事务B已经提交的修改数据,不符合隔离性

幻读(Phantom Reads)

一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插 入了满足其查询条件的新数据,这种现象就称为“幻读”。
一句话:事务A读取到了事务B提交的新增数据,不符合隔离性。

4.2.3 事务隔离级别

脏读、不可重复读和幻读,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

隔离级别 脏读(Dirty Reads) 不可重读(Non-Repeatable Reads) 幻读(Phantom Reads)
读未提交(Read uncommitted) 可能 可能 可能
读已提交(Read committed) 不可能 可能 可能
可重复读(Repeatable red) 不可能 不可能 可能
可串行化(serializable) 不可能 不可能 不可能

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔 离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的

同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用 对“不可重复读"和“幻读”并不敏感,可能更关心数据并发访问的能力。

查看当前数据库的事务隔离级别:

show variables like 'tx_isolation';

设置事务隔离级别:

set tx_isolation='REPEATABLE-READ';

MySQL默认的事务级别为:REPEATABLE-READ

更多内容,可前往查看

转载地址:http://xoyai.baihongyu.com/

你可能感兴趣的文章
VMware6辅助启动.bat
查看>>
升级linux内核到2.6.24
查看>>
vbs脚本大全,配有实例
查看>>
WIN32汇编基础
查看>>
Win32汇编基础教程
查看>>
“VM6辅助启动.bat”生成器.hta
查看>>
windows脚本调试howto
查看>>
五笔86版字根图程序
查看>>
Oracle EBS R12 - Use Rman to Clone Oracle EBS R12.1.1 without shutting down source Database and MT
查看>>
Oracle EBS - What happening when executing adpreclone.pl in DB and Apps Tier?
查看>>
Oracle EBS - What happening when executing adcfgclone.pl in DB Tier as well as Apps Tier?
查看>>
Oracle EBS - Details of Adpreclone and Adcfgclone
查看>>
两个对Oracle性能影响很大的io参数
查看>>
Win32ASM备忘之搭建UltraEdit实验环境
查看>>
The Best Linux Distribution of them all
查看>>
Oracle Apps DBA Interview Questions
查看>>
简单屏幕锁(Simple Screen Locker) 1.1.6.16
查看>>
Bash String Manipulation Examples – Length, Substring, Find and Replace
查看>>
String Operations in Shell
查看>>
烦请解释一下“驱动表”的概念
查看>>