一、锁

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 索引

    二维数组中的查找 替换空格 从尾到头打印链表 重建二叉树 用两个栈实现队列 旋转数组的最小数字 斐波那契数列 跳台阶 变态跳台阶 矩形覆盖 二进制中1的个数 数值的整数次方 调整数组顺序使奇数位于偶数 ...

  2. 数据流中的中位数 牛客网 剑指Offer

    数据流中的中位数 牛客网 剑指Offer 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就 ...

  3. 我的笔记本电脑瞬间扩大一个T的容量!

    前言 不知道有多少人在家里搭建中央存储设备的,也就是NAS.这个东西在我日常生活中,存储了大量的个人资料,家人们的照片,技术的资料,还有各种高清影视剧.搭配公网的IP,可以真正做到,任何时候任何地点的 ...

  4. Linux网卡bond模式

    Bond模式 交换机配置 mode=0 balance-rr 轮询均衡模式 LACP mode on 强制链路聚合 mode=1 active-backup 主备模式 无 mode=2 balance ...

  5. 跟着老猫来搞GO,基础进阶

    回顾一下上一篇博客,主要是和大家分享了GO语言的基础语法,其中包含变量定义,基本类型,条件语句,循环语句.那本篇呢就开始和大家同步一下GO语言基础的进阶. 函数的定义 上次其实在很多的DEMO中已经写 ...

  6. js实现一个小游戏(飞翔的jj)

    js实现一个小游戏(飞翔的jj) 源代码+素材图片在我的仓库 <!DOCTYPE html> <html lang="en"> <head> & ...

  7. node.js中模块和包

    node.js中模块和包 什么是模块 如何创建并加载模块 1. 创建模块 2. 单次加载 3. 覆盖 exports 如何创建一个包 1. 作为文件夹的模块 2. package.json 如何使用包 ...

  8. SSH服务器拒绝了密码。请再试一次。怎么改都不行

    使用Xshell连接服务器,之前还好好的,突然之间就报"SSH服务器拒绝了密码.请再试一次"的错误. 1.检查 检查了IP.连接端口.用户.密码.网络是否正确? 本机情况:能够pi ...

  9. Spring Ioc 容器初始化过程

    IOC 是如何工作的? 通过 ApplicationContext 创建 Spring 容器,容器读取配置文件 "/beans.xml" 并管理定义的 Bean 实例对象.   通 ...

  10. python openpyxl、RESTful、Webservice接口 基础知识

    最近 在做接口测试的时候,遇到如下问题:如何通过数据驱动去做批量接口测试呢,我们的测试数据放在哪里去维护?下面整理出相关点,供大家参考 1.如何维护接口测试数据:放在excel文件中,通过python ...