一、锁

1、锁的定义

    锁即是一种用来协调多线程或进程并发使用同一共享资源的机制

2、锁的分类

  • 从性能上分类:乐观锁和悲观锁
  • 从数据库操作类型上分类:读锁和写锁
  • 从操作粒度上分类:表锁和行锁

2.1 从性能上分类

2.1.1 乐观锁
    乐观锁顾名思义就是操作的时候很乐观,认为操作不会产生并发问题(不会有其他线程对数据进行修改),因此不会上锁。但是会在更新时判断其他线程再这之前有没有对数据进行修改,一般会使用版本号机制或CAS算法实现。
2.1.1.1 版本号机制
 实现方式:
  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时,set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败
version可以自定义,也可以使用时间戳来作为版本编号
示例SQL:
update table set name = 'anna',version = version+1 where id = #{id} and version = #{version}
 
 
2.1.1.2 CAS算法
    乐观锁的另一种技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新表变量的值,而其他线程都失败,失败的线程并不会呗挂起,而是被告知在这次竞争中失败,并可以再次尝试。
CAS操作中包含三个操作数:
  • 需要读写的内存位置V
  • 进行比较的预期原值A
  • 拟写入的新值B
如果内存位置V的值与预期原值A匹配,则将B写入内存位置V中替代A,如果预期原值A不匹配则不做任何操作。无论那种情况,他都会在CAS指令之前返回该位置的值(在CAS的一些特殊情况下仅返回CAS是否成功,而不提取当前值)。这其实和乐观锁的冲突检查+数据更新的原理时一致的。
2.1.2 悲观锁
    总是假设最坏的情况,每次取数据都会认为其他线程会修改,所以都会加锁。一旦加锁,不同线程同时执行的时,只能有一个线程执行,其他线程在入口出等待,知道锁被释放。
悲观锁在Mysql和JAVA中被广泛应用
  • MYSQL的读锁、写锁、行锁等
  • Java的synchronized关键字

使用建议:
    读得多,冲突几率小,使用乐观锁
    写的锁,冲突几率大,使用悲观锁

2.2 从数据库操作类型上分类

2.2.1 读锁
    读锁在加锁时允许其他事务进行查询操作,但不允许进行修改操作,即共享锁。
2.2.2 写锁
    写锁在加锁时则不允许其他事务进行任何操作,即排他锁。

2.3 从操作粒度上分类

2.3.1 表锁
    顾名思义,会将整个表加锁,这种方式加锁粒度大,加锁块、开销小,不会产生死锁。但是并发执行情况下效率较低、容易发生锁冲突。
2.3.2 行锁
    行锁会对被操作的数据行进行加锁,这种方式加锁粒度小、加锁慢、开销大,可能会产生死锁。但是并发执行情况下效率高,不容易发生锁冲突。
    innodb中的行锁时针对索引加锁而非针对记录。如果索引失效,就会从行锁升级为表锁。

在Mysql中MyISAM的引擎仅支持表锁,每次进行查询操作都会对表加读锁,进行修改操作则加写锁。MyISAM并不支持事务和行锁,而innodb支持行级锁和事务,这也是MyISAM和Innodb最大的不同。

二、事务

1、事务的定义

    事务时由一组SQL语句组成的逻辑处理单元。

2、事务的特性

    事务具有四种特性:原子性、一致性、隔离性、持久性。
  • 原子性:事务中所有操作应具有原子性,即要么都执行要么都不执行
  • 一致性:指操作执行前后的数据具备一致性。这意味着,所有相关的数据规则都必须应用于事务的修改,以保证数据的完整性。事务结束时,所有的数据结构也都必须是正确的。
  • 隔离性:每个事务都有其独立的环境,事务的的中间状态不为外界可见,反之亦然。
  • 持久性:当事务提交之后,它对数据库数据的改变具有持久性,即便数据库宕机重启后数据库的数据状态应该依然是事务提交之后的结果。

3、并发事务处理带来的问题

  • 更新丢失:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生更新丢失的问题-最后的更新覆盖的有其他事务所做的更新。
  • 脏读:事务读取到了其他事务修改但未提交的的值,而之后那个事务进行了回滚,那么当前事务所读取到的数据就是脏数据,不符合事务的一致性。
  • 不可重复读:当前事务在一开始读取了一行数据,而在那之后另一个事务提交了一个对于该行的更新,那么当此事务再次去读取同一行数据时,读取到的结果和第一次的不同、或则这一条记录已被删除,这种现象就叫做不可重复读。
    也就是说事务读取到了其他事务已提交的数据,不符合隔离性。
  • 幻读:事务A读取到了事务B已提交的新增的数据,不符合隔离性

4、事务的隔离级别

    为了解决并发事务处理所带来的问题,mysql提供了4种事务隔离级别以供我们选择。
  • 读未提交(read uncommit):可以读取到事务未提交的修改
  • 读已提交(read commit):可以读取到事务已提交的修改
  • 可重复读(repeatable read):事务进行中不会读取到其他事务已提交的修改,只会使用第一次查询时所获得的快照结果。这里使用到了MVCC(多版本并发控制)机制来达到这个效果。
  • 串行化(Serializable):将事务转化为串行的形式来执行,该级别会进行锁表,因此不会出现幻读的情况,这种隔离界别的并发性极低,开发种很少会用到。
    数据库的事务隔离级别越严格,那么并发所带来的副作用就越小,但是并发执行的效率也就越低,因为事务隔离级别就是在一定程度上使事务“串行化”进行,这显然与“并发”相矛盾。
    所以应该合理的去选择事务隔离级别,根据业务场景对于数据的敏感程度进行选择。
 
间隙锁:使用间隙锁可以在可重复读的隔离级别下避免幻读,使用下面SQL语句
update account set name = 'tufeu' where id>=5  and id < 7;
在事务开头执行这一语句,可以使得其他事务无法在当前事务未提交前在id为5到7之间插入数据,这就是间隙锁
当然如果你不想真的修改数据的话,请保证间隙锁范围内没有数据。
 
 
在seesion_1种使用间隙锁
在session_2种插入新数据因为id自增所以新id为6,在间隙锁范围内,被阻塞导致超时,插入失败。
不过由于大部分场景下不需要处理幻读的情况,所以平时要尽量避免间隙读,因为这样的确会降低并发效率
MVCC机制:
    mvcc机制底层由undo log实现,这里以一种较为简单的方式说明机制
首先要明确每个事务都有一个自己的id,事务的id只有在事务执行第一条sql语句时才会分配,mysql的事务id分配严格按照事务的创建顺序来进行。
我们可以看作每一条数据都有两个隐藏的字段创建事务id和删除事务id(可为空)
当我们修改一条数据的时候,不会直接修改,而是新增一条修改过后的数据id和原数据一致(这里是假设不是真实实现,不考虑id重复这个问题)
当我们删除的时候,会将当前做删除操作的事务id记录再”删除事务id“列上
当我们查询一条记录是,会创建一个查询快照,记录此刻的最大已提交事务id。
当我们再次查询的时候会在mysql底层带上过滤条件,创建事务id<=max(当前事务id,快照点已提交最大事务id) and 删除事务id>max(当前事务id,快照点已提交最大事务id)
查询结果不一定唯一,以创建事务id最大的那个为准。
id name balacne create_id del_id
1 a 100 11
2 b 200 12    12
3 c 300 12
1 a 120 14
 
比如上面这几行数据,假如我们事务id为11 和 12 新增前三条数据,然后新开一个事务id为13的事务,进行查询操作select * form account,的到一个快照,并获得当前的最大已提交事务id为12,不去提交13,新创建一个事务id14,修改id为1的语句,这个时候,mysql会新增一条数据为修改过后的样子,有其事务创建id为14,这个时候我们用事务13去查询,查询语句回事 select * from account where 创建事务id<=max(当前事务id(13),快照点最大提交id(12)) and 删除事务id>max(当前事务id(13),快照点最大提交id(12)),如果没有删除事务id则忽略这个条件,这个时候应该可以看出我们查询出来的数据只会是第一条,因为第四条的创建事务id为14。
我们再看id为2的数据,根据上面的sql查询语句,我们可以看出他的删除事务id为12小于max(当前事务id,快照点最大提交id)所以无法查询到该数据。
 
优化建议:
  • 尽可能的让数据检索通过索引完成,避免无索引行锁升级为表锁
  • 合理设计索引,尽量缩小锁的范围
  • 极可能减少检索条件范围,避免间隙锁
  • 尽量控制事务大小,减少锁定资源量和时间长度,设计事务加锁的sql尽量放在事务最后执行
  • 尽可能使用低级别的事务隔离以提升并发效率
 
 
 

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

 
 
 
 

深入理解mysql锁与事务隔离级别的更多相关文章

  1. 【MySQL】深入理解MySQL锁和事务隔离级别

    先看个小案例: 话不多说,上案例,先创建一个表 mysql> CREATE TABLE IF NOT EXISTS `account`( `id` INT UNSIGNED AUTO_INCRE ...

  2. 面试必问的MySQL锁与事务隔离级别

    之前多篇文章从mysql的底层结构分析.sql语句的分析器以及sql从优化底层分析, 还有工作中常用的sql优化小知识点.面试各大互联网公司必问的mysql锁和事务隔离级别,这篇文章给你打神助攻,一飞 ...

  3. MySql锁和事务隔离级别

    在讲mysql事物隔离级别之前,我们先简单说说mysql的锁和事务. 一:数据库锁 因为数据库要解决并发控制问题.在同一时刻,可能会有多个客户端对同一张表进行操作,比如有的在读取该行数据,其他的尝试去 ...

  4. 深入理解Mysql索与事务隔离级别

    1. 概述 1.1 定义 锁是计算机协调多个进程或线程并发访问某一资源的机制. 在数据库中,除了传统的计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供需要用户共享的资源.如何保证数据并 ...

  5. MySQL锁与事务隔离级别

    一.概述 1.锁的定义 锁是计算机协调多个进程或线程并发访问某一资源的机制. 在数据库中,除了传统的计算资源(如CPU.RAM.IO等)的争用以外,数据也是一种供需要用户共享的资源.如何保证数据并发访 ...

  6. 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

    在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...

  7. 第36讲 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景

    在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...

  8. mysql中不同事务隔离级别下数据的显示效果--转载

    事务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查询语句因为崩溃或其他原因而无法执行,那么所有的语句就都 ...

  9. 浅谈mysql中不同事务隔离级别下数据的显示效果

    事务的概念 事 务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查 询语句因为崩溃或其他原因而无法执行,那 ...

随机推荐

  1. 机器人的运动范围 牛客网 剑指Offer

    机器人的运动范围 牛客网 剑指Offer 题目描述 地上有一个m行和n列的方格.一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大 ...

  2. Ubuntu virtualenv 创建 python3 虚拟环境 激活 退出

    首先默认安装了virtualenv 创建python3虚拟环境 your-name@node-name:~/virtual_env$ virtualenv -p /usr/bin/python3 py ...

  3. 记一次 Java 导出大批量 Excel 优化

    常用的excel导出方案,详情见Spring Boot 入门(十二):报表导出,对比poi.jxl和esayExcel的效率,其中jxl.esayEscel 底层都是基于 poi,它们仅仅是对 poi ...

  4. 【解决方案】Pyinstaller打包exe策略(简单实用)

    场景说明 在业务场景中, 经常需要Python开发一些小程序/脚本/GUI界面,进行简单的项目测试或未安装Python 的小伙伴们使用. 使用Pyinstaller将Python脚本或者项目打包,生成 ...

  5. Java开发介绍之JDK JRE JVM 和 环境变量配置

    一.JDK>JRE>JVM JDK(Java Development Kit):Java开发工具包 JDK中包含JRE,在JDK的安装目录下有一个名为jre的目录,里面有两个文件夹bin和 ...

  6. 记一次性能优化的心酸历程【Flask+Gunicorn+pytorch+多进程+线程池,一顿操作猛如虎】

    您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 本文只是记录我优化的心酸历程.无他,唯记录尔.....小伙伴们可围观,可打call,可以私信与我交流. 干货满满,建议收藏,需要用到时常看看. 小 ...

  7. (一)初识MySQL

    JavaEE:企业级Java开发  Web 前端(页面:展示,数据) 后台(连接点,连接数据库JDBC,链接前端(控制,控制视图跳转和给前端传递数据)) 数据库(存数据,Txt,Excel,word) ...

  8. CF264BGood Sequences

    CF264BGood Sequences 题面 大意 寻找最长递增字串,使得相邻两个数不互质. 思路 动态规划思想,ans记录当前的数以下标i为约数答案,使得需要填进去的数肯定与前一个数不互质.在开始 ...

  9. Python 爬取 房天下

    ... import requests from requests import ConnectionError from bs4 import BeautifulSoup import pymong ...

  10. 3、使用ListOperations操作redis(List列表)

    文章来源:https://www.cnblogs.com/shiguotao-com/p/10560354.html 方法 c参数 s说明   List<V> range(K key, l ...