mysql事务隔离

Wesley13
• 阅读 575

事务

MySQL 事务主要用于处理操作量大,复杂度高的数据。

比如:在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!

特性

一般来说,事务是必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

  1. 原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  2. 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  3. 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  4. 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

隔离级别

ANSI SQL标准定义的四个隔离级别为:

  1. READ UNCOMMITTED(未提交读),事务中的修改,即使没有提交,在其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。
  2. READ COMMITTED(提交读),一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读,因为两次执行相同的查询,可能会得到不一样的结果。因为在这2次读之间可能有其他事务更改这个数据,每次读到的数据都是已经提交的。
  3. REPEATABLE READ(可重复读),解决了脏读,也保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重读读隔离级别还是无法解决另外一个幻读的问题,指的是当某个事务在读取某个范围内的记录时,另外一个事务也在该范围内插入了新的记录,当之前的事务再次读取该范围内的记录时,会产生幻行。
  4. SERIALIZABLE(可串行化),它通过强制事务串行执行,避免了前面说的幻读的问题。

几个可能产生的错误:

  1. 脏读(dirty read):一个事务可以读取另一个尚未提交事务的修改数据。
  2. 不可重复读(nonrepeatable read):在同一个事务中,同一个查询在T1时间读取某一行,在T2时间重新读取这一行时候,这一行的数据已经发生修改,可能被更新了(update),也可能被删除了(delete)。
  3. 幻读(phantom read):在同一事务中,同一查询多次进行时候,由于其他插入操作(insert)的事务提交,导致每次返回不同的结果集。

InnoDB采用MVCC来支持高并发,并实现了四个标准的隔离级别。其默认级别是REPEATABLE READ(可重复读),并且通过间隙锁(next-key locking)策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影的插入。 隔离级别越低,事务请求的锁越少或保持锁的时间就越短。所以很多数据库系统默认的事务隔离级别是READ COMMITTED。质疑SERIALIZABLE隔离级别的性能,但是InnoDB存储引擎认为两者的开销是一样的,所以默认隔离级别使用REPEATABLE READ。

用命令设置当前会话或全局会话的事务隔离级别。

SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL 
{
    READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE
}
//配置文件
[mysqld]
transaction-isolation = READ-COMMITTED
//查看事务级别
select @@tx_ioslation;  //当前
select @@global.tx_isolation; //全局

MYSQL处理事务隔离(MVCC)

重点:主要是将表锁降低到了行锁。

Multi-Version Concurrency Control 多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。

英文好难: Concurrency [kən'kʌrəns] Con curren cy

MVCC会保存某个时间点上的数据快照。这意味着事务可以看到一个一致的数据视图,不管他们需要跑多久。这同时也意味着不同的事务在同一个时间点看到的同一个表的数据可能是不同的。

通过为每一行记录添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。但是InnoDB并不存储这些事件发生时的实际时间,相反它只存储这些事件发生时的系统版本号。这是一个随着事务的创建而不断增长的数字。每个事务在事务开始时会记录它自己的系统版本号。每个查询必须去检查每行数据的版本号与事务的版本号是否相同。让我们来看看当隔离级别是REPEATABLE READ时这种策略是如何应用到特定的操作的:

SELECT InnoDB必须每行数据来保证它符合两个条件:

  1. InnoDB必须找到一个行的版本,它至少要和事务的版本一样老(也即它的版本号不大于事务的版本号)。这保证了不管是事务开始之前,或者事务创建时,或者修改了这行数据的时候,这行数据是存在的。
  2. 这行数据的删除版本必须是未定义的或者比事务版本要大。这可以保证在事务开始之前这行数据没有被删除。这里的不是真正的删除数据,而是标志出来的删除。真正意义的删除是在commit的时候。

符合这两个条件的行可能会被当作查询结果而返回。

  • INSERT:InnoDB为这个新行记录当前的系统版本号。
  • DELETE:InnoDB将当前的系统版本号设置为这一行的删除ID。
  • UPDATE:InnoDB会写一个这行数据的新拷贝,这个拷贝的版本为当前的系统版本号。它同时也会将这个版本号写到旧行的删除版本里。

这种额外的记录所带来的结果就是对于大多数查询来说根本就不需要获得一个锁。他们只是简单地以最快的速度来读取数据,确保只选择符合条件的行。这个方案的缺点在于存储引擎必须为每一行存储更多的数据,做更多的检查工作,处理更多的善后操作。

MVCC只工作在REPEATABLE READ和READ COMMITED隔离级别下。READ UNCOMMITED不是MVCC兼容的,因为查询不能找到适合他们事务版本的行版本;它们每次都只能读到最新的版本。SERIABLABLE也不与MVCC兼容,因为读操作会锁定他们返回的每一行数据 [1] 。

个人理解: 当锁发生时,创建数据副本,副本的版本 = 事务的版本。

模拟一个数据结构

struct data {
lock Lock,
     value {
             line data_value,
             version int,
            next *data,
       },
     lockversion map[int]int
}

当事务开启时,data1 = clone data; data.next = data1; data.version += 1; data.locked;

当事务结束时, data.next != null data = data.next; (要释放之前的data); data.unlocked;

lockversionmap 对应的是一个map 事务ID对应数据版本ID。

当锁为其他级别时,

  • RUC级别 读原始数据 不产生副本。
  • RC级别 读到可能已经修改的数据,产生副本。
  • RR级别 如果多次读的话,并行有其他事务,可能结果不一致。
  • 当锁为串行锁的时候,一旦发现locked,就必须等待锁结束,才会读取数据。

补充

  1. SQL规范所规定的标准,不同的数据库具体的实现可能会有些差异
  2. mysql中默认事务隔离级别是可重复读时并不会锁住读取到的行
  3. 事务隔离级别为读提交时,写数据只会锁住相应的行
  4. 事务隔离级别为可重复读时,如果有索引(包括主键索引)的时候,以索引列为条件更新数据,会存在间隙锁间隙锁、行锁、下一键锁的问题,从而锁住一些行;如果没有索引,更新数据时会锁住整张表。
  5. 事务隔离级别为串行化时,读写数据都会锁住整张表
  6. 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大,鱼和熊掌不可兼得啊。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
OMG!Java高级开发岗必问知识点
目录1.Mysql2.CHAR与VARCHAR的区别?3.能说下myisam和innodb的区别吗?4.你能说下事务的基本特性和隔离级别吗?5.并发问题脏读、不可重复读、幻读?6.事务的隔离级别?7.说说自增主键、UUID?8.mysql的约束分类?9.drop、delete与tru
LosAngel LosAngel
3年前
golang实现MySQL数据库事物的提交与回滚
MySQL事务主要用于处理操作量大,复杂度高的数据。在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务。事务用来管理insert,update,delete语句,事务处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行。一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicit
Stella981 Stella981
3年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
MySQL数据库InnoDB存储引擎Log漫游(1)
作者:宋利兵来源:MySQL代码研究(mysqlcode)0、导读本文介绍了InnoDB引擎如何利用UndoLog和RedoLog来保证事务的原子性、持久性原理,以及InnoDB引擎实现UndoLog和RedoLog的基本思路。00–UndoLogUndoLog是为了实现事务的原子性,
Wesley13 Wesley13
3年前
Mysql事务处理
事务处理在各种管理系统中都有着广泛的应用,比如人员管理系统,很多同步数据库操作大都需要用到事务处理。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!   删除的SQL语句deletefromuserinfowhere~~~d
Wesley13 Wesley13
3年前
Mysql系列第十五讲 事务详解
Mysql系列第十五讲什么是事务?事务的几个特性(ACID)Mysql中事务操作savepoint关键字只读事务事务中的一些问题事务的隔离级别关于隔离级别的选择什么是事务?数据库中的事务是指对数据库执行一批操作,这些操作最终要么
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这