前言:
眼前咱们对“mysql不支持check”大概比较珍视,小伙伴们都想要知道一些“mysql不支持check”的相关内容。那么小编同时在网摘上汇集了一些有关“mysql不支持check””的相关文章,希望姐妹们能喜欢,大家一起来学习一下吧!Mysql事务1. ACID
在关系型数据库管理系统中,一个逻辑工作单元要成为事务,必须满足这 4 个特性,即所谓的 ACID: 原子性(Atomicity)、一致性( Consistency)、隔离性( Isolation )和持久性( Durability)。
1.1 原子性
原子性:事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
修改---》 Buffer Pool修改---》刷盘。可能会有下面两种情况:
事务提交了,如果此时Buffer Pool的脏页没有刷盘,这时候宕机了,如何保证修改的数据生效? Redo
如果事务没提交,但是Buffer Pool的脏页刷盘了,如何保证不该存在的数据撤销? Undo
每一个写事务,都会修改BufferPool ,从而产生相应的Redo/Undo日志,在Buffer Pool 中的页被刷到 磁盘之前,这些日志信息都会先写入到日志文件中,如果 Buffer Pool 中的脏页没有刷成功,此时数据 库挂了,那在数据库再次启动之后,可以通过 Redo 日志将其恢复出来,以保证脏页写的数据不会丢失。如果脏页刷新成功,此时数据库挂了,就需要通过Undo来实现了。
1.2 持久性
持久性:指的是一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,后续的操作或故障不 应该对其有任何影响,不会丢失。
如下图所示,一个“提交”动作触发的操作有: binlog落地、发送binlog、存储引擎提交、 flush_logs, check_point、事务提交标记等。这些都是数据库保证其数据完整性、持久性的手段。
MySQL的持久性也与WAL技术相关, redo log在系统Crash重启之类的情况时,可以修复数据,从而保 障事务的持久性。通过原子性可以保证逻辑上的持久性,通过存储引擎的数据刷盘可以保证物理上的持 久性。
1.3 隔离性
隔离性:指的是一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其他的并 发事务是隔离的。
InnoDB 支持的隔离性有 4 种,隔离性从低到高分别为:读未提交、读提交、可重复读、串行化。锁和多版本并发控制( MVCC )技术就是用于保障隔离性的(后面课程详解)。
1.4 一致性
一致性:指的是事务开始之前和事务结束之后,数据库的完整性限制未被破坏。一致性包括两方面的内 容,分别是约束一致性和数据一致性。
约束一致性:创建表结构时所指定的外键、 Check、唯一索引等约束,可惜在 MySQL 中不支持 Check 。
数据一致性:是一个综合性的规定,因为它是由原子性、持久性、隔离性共同保证的结果,而不是 单单依赖于某一种技术。
一致性也可以理解为数据的完整性。数据的完整性是通过原子性、隔离性、持久性来保证的,而这3个 特性又是通过 Redo/Undo 来保证的。逻辑上的一致性,包括唯一索引、外键约束、 check 约束,这属 于业务逻辑范畴。
ACID 及它们之间的关系如下图所示, 4个特性中有3个与 WAL 有关系,都需要通过 Redo、 Undo 日志 来保证等。
WAL的全称为Write-Ahead Logging ,先写日志,再写磁盘。
2.并发事务控制
2.1 并发事务
事务并发处理可能会带来一些问题,比如:更新丢失、脏读、不可重复读、幻读等。
更新丢失
当两个或多个事务更新同一行记录,会产生更新丢失现象。可以分为回滚覆盖和提交覆盖。
回滚覆盖:一个事务回滚操作,把其他事务已提交的数据给覆盖了。
提交覆盖:一个事务提交操作,把其他事务已提交的数据给覆盖了。
脏读
一个事务读取到了另一个事务修改但未提交的数据。
不可重复读
一个事务中多次读取同一行记录不一致,后面读取的跟前面读取的不一致。
幻读
一个事务中多次按相同条件查询,结果数量不一致。后续查询的结果和面前查询结果不同,多了或少了几行记录。
2.3 排队
最简单的方法,就是完全顺序执行所有事务的数据库操作,不需要加锁,简单的说就是全局排队。序列 化执行所有的事务单元,数据库某个时刻只处理一个事务操作,特点是强一致性,处理性能低。
2.2 排他锁
引入锁之后就可以支持并发处理事务,如果事务之间涉及到相同的数据项时,会使用排他锁,或叫互斥锁,先进入的事务独占数据项以后,其他事务被阻塞,等待前面的事务释放锁。
注意,在整个事务1结束之前,锁是不会被释放的,所以,事务2必须等到事务1结束之后开始。
2.3 读写锁
读和写操作:读读、写写、读写、写读。
读写锁就是进一步细化锁的颗粒度,区分读操作和写操作,让读和读之间不加锁,这样下面的两个事务 就可以同时被执行了。
读写锁,可以让读和读并行,而读和写、写和读、写和写这几种之间还是要加排他锁。
3.隔离级别
3.1 隔离级别类型
前面提到的“更新丢失”、”脏读”、“不可重复读”和“幻读”等并发事务问题,其实都是数据库一致性问题, 为了解决这些问题, MySQL数据库是通过事务隔离级别来解决的,数据库系统提供了以下 4 种事务隔 离级别供用户选择。
读未提交
Read Uncommitted 读未提交:解决了回滚覆盖类型的更新丢失,但可能发生脏读现象,也就是 可能读取到其他会话中未提交事务修改的数据。
已提交读
Read Committed 读已提交:只能读取到其他会话中已经提交的数据,解决了脏读。但可能发生 不可重复读现象,也就是可能在一个事务中两次查询结果不一致。
可重复度
Repeatable Read 可重复读:解决了不可重复读,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上会出现幻读,简单的说幻读指的的当用户读取某一范围的数 据行时,另一个事务又在该范围插入了新行,当用户在读取该范围的数据时会发现有新的幻影行。
可串行化
Serializable 串行化:所有的增删改查串行执行。它通过强制事务排序,解决相互冲突,从而解决 幻度的问题。这个级别可能导致大量的超时现象的和锁竞争,效率低下。
数据库的事务隔离级别越高,并发问题就越小,但是并发处理能力越差(代价)。读未提交隔离级别最 低,并发问题多,但是并发处理能力好。以后使用时,可以根据系统特点来选择一个合适的隔离级别, 比如对不可重复读和幻读并不敏感,更多关心数据库并发处理能力,此时可以使用Read Commited隔 离级别。
事务隔离级别,针对Innodb引擎,支持事务的功能。像MyISAM引擎没有关系。
事务隔离级别和锁的关系
1 )事务隔离级别是SQL92定制的标准,相当于事务并发控制的整体解决方案,本质上是对锁和MVCC使 用的封装,隐藏了底层细节。
2)锁是数据库实现并发控制的基础,事务隔离性是采用锁来实现,对相应操作加不同的锁,就可以防 止其他事务同时对数据进行读写操作。
3 )对用户来讲,首先选择使用隔离级别,当选用的隔离级别不能解决并发问题或需求时,才有必要在 开发中手动的设置锁。
MySQL默认隔离级别:可重复读
Oracle、SQLServer默认隔离级别:读已提交
一般使用时,建议采用默认隔离级别,然后存在的一些并发问题,可以通过悲观锁、乐观锁等实现处 理。
3.2 MySQL隔离级别控制
MySQL默认的事务隔离级别是Repeatable Read ,查看MySQL当前数据库的事务隔离级别命令如下:
show variables like 'tx_isolation';
select @@tx_isolation;
设置事务隔离级别可以如下命令:
set tx_isolation='READ-UNCOMMITTED';
set tx_isolation='READ-COMMITTED';
set tx_isolation='REPEATABLE-READ';
set tx_isolation='SERIALIZABLE';
4.MVCC(重点)简介
什么是 MVCC ?
MVCC(Multi-Version Concurrency Control)即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问。MVCC使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能。
MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读
什么是当前读和快照读?
在学习 MVCC 多版本并发控制之前,我们必须先了解一下,什么是 MySQL InnoDB 下的当前读和快照读?
当前读像 select lock in share mode (共享锁), select for update; update; insert; delete (排他锁)这些操作都是一种当前读。
为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
快照读像不加锁的 select 操作就是快照读,即不加锁的非阻塞读;快照读的实现是基于多版本并发控制,而有可能是之前的历史版本。
MVCC 就是为了实现读-写冲突不加锁,而这个读指的就是 快照读 , 而非当前读。
当前读实际上是一种加锁的操作,是悲观锁的实现
当前读,快照读和MVCC的关系
MVCC 多版本并发控制是 「维持一个数据的多个版本,使得读写操作没有冲突」 的概念,只是一个抽象概念,并非实现因为 MVCC 只是一个抽象概念,要实现这么一个概念,MySQL 就需要提供具体的功能去实现它,「快照读就是 MySQL 实现 MVCC 理想模型的其中一个非阻塞读功能」。而相对而言,当前读就是悲观锁的具体功能实现要说的再细致一些,快照读本身也是一个抽象概念,再深入研究。MVCC 模型在 MySQL 中的具体实现则是由 3 个隐式字段,undo 日志 ,Read View 等去完成的,具体可以看下面的 MVCC 实现原理
MVCC 能解决什么问题,好处是?
数据库并发场景有三种,分别为:
读-读:不存在任何问题,也不需要并发控制读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失
MVCC 带来的好处是?多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 所以 MVCC 可以为数据库解决以下问题
在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题
如何使用MVCC解决并发问题?有了 MVCC,所以我们可以形成两个组合:
MVCC + 悲观锁MVCC解决读写冲突,悲观锁解决写写冲突MVCC + 乐观锁MVCC 解决读写冲突,乐观锁解决写写冲突
这种组合的方式就可以最大程度的提高数据库并发性能,并解决读写冲突,和写写冲突导致的问题
MVCC 的实现原理
MVCC 的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View 来实现的。所以我们先来看看这个三个 point 的概念
隐式字段
每行记录除了我们自定义的字段外,还有数据库隐式定义的 DB_TRX_ID, DB_ROLL_PTR, DB_ROW_ID等字段
DB_TRX_ID6 byte,最近修改(修改/插入)事务 ID:记录创建这条记录/最后一次修改该记录的事务 IDDB_ROLL_PTR7 byte,回滚指针,指向这条记录的上一个版本的undo logDB_ROW_ID6 byte,隐含的自增 ID(隐藏主键),如果数据表没有主键,InnoDB 会自动以DB_ROW_ID产生一个聚簇索引
undo日志
undo log 主要分为两种:
insert undo log代表事务在 insert 新记录时产生的 undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃update undo log事务在进行 update 或 delete 时产生的 undo log ; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被 purge 线程统一清除
对 MVCC 有帮助的实质是 update undo log ,undo log 实际上就是存在 rollback segment 中旧记录链,
执行流程:
一、 比如一个有个事务插入 persion 表插入了一条新记录
记录如下, name 为 Jerry , age 为 24 岁, 隐式主键 是 1, 事务 ID 和 回滚指针 ,我们假设为 NULL
二、 现在来了一个 事务 1 对该记录的 name 做出了修改,改为 Tom
在事务 1修改该行(记录)数据时,数据库会先对该行加排他锁然后把该行数据拷贝到 undo log 中,作为旧记录,既在 undo log 中有当前行的拷贝副本拷贝完毕后,修改该行name为Tom,并且修改隐藏字段的事务 ID 为当前事务 1的 ID, 我们默认从 1 开始,之后递增,回滚指针指向拷贝到 undo log 的副本记录,既表示我的上一个版本就是它事务提交后,释放锁
三、 又来了个事务 2 修改person 表 的同一个记录,将age 修改为 30 岁
在事务2修改该行数据时,数据库也先为该行加锁然后把该行数据拷贝到 undo log 中,作为旧记录,发现该行记录已经有 undo log 了,那么最新的旧数据作为链表的表头,插在该行记录的 undo log 最前面修改该行 age 为 30 岁,并且修改隐藏字段的事务 ID 为当前事务 2的 ID, 那就是 2 ,回滚指针指向刚刚拷贝到 undo log 的副本记录事务提交,释放锁
不同事务或者相同事务的对同一记录的修改,会导致该记录的undo log成为一条记录版本线链表,undo log 的链首就是最新的旧记录,链尾就是最早的旧记录
Read View 读视图
什么是 Read View?
**Read View 就是事务进行快照读操作的时候生产的读视图 (Read View)**,在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的 ID
我们可以把 Read View 简单的理解成有三个全局属性
活跃事务列表
一个数值列表用于维护 Read View 生成时刻系统 正活跃的事务 ID 列表
up_limit_id
lower water mark是 trx_list 列表中事务 ID 最小的 ID
low_limit_id
hight water markReadView 生成时刻系统尚未分配的下一个事务 ID ,也就是 目前已出现过的事务 ID 的最大值 + 1为什么是 low_limit ? 因为它也是系统此刻可分配的事务 ID 的最小值
这两个 ID 其实就可以从当前执行的事务的视角,将所有的事务分为三个部分
小于低水位的部分一定是当前事务开始前就提交了的部分
大于等于高水位的则一定是还未提交的事务,我们一定不可见
处于中间的部分就要分类讨论了:
如果在视图数组中,说明当前事务开始时,这些事务仍在活跃,所以应该是不可见的;如果不在数组中,说明在仍活跃着的事务范围内,但其中有一些事务虽然不是开始最早的,但是结束的却比活跃数组中的事务早,以至于当前事务开始时,这些事务已经结束,所以就应该是可见的。
可见事务id总结
要么比低水位更早,要么比高水位的 id 小但是不能出现在活跃事物数组中。
整体流程
流程模拟
当事务 2对某行数据执行了快照读,此时还有事务1和事务3在活跃中,事务 4在事务 2快照读前一刻提交更新了。
事务 1事务 2事务 3事务 4事务开始事务开始事务开始事务开始………修改且已提交进行中快照读进行中………
up_limit_id 就是1,low_limit_id 就是 4 + 1 = 5,活跃事务列表值是 1, 3,Read View 如下图
我们的例子中,只有事务 4 修改过该行记录,并在事务 2 执行快照读前,就提交了事务。所以当前DB_TRX_ID 为4
拿该记录 DB_TRX_ID 字段记录的事务 ID 4 去跟 Read View 的 up_limit_id 比较
DB_TRX_ID(4)大于 up_limit_id(1),所以不符合可见条件继续判断 DB_TRX_ID(4) 是否大于等于 low_limit_id(5),不符和不可见条件最后判断 DB_TRX_ID(4) 是否处于 trx_list 中的活跃事务, 最后发现事务 ID 为 4 的事务不在当前活跃事务列表中, 符合可见性条件如果上述三点判断后不符合可见性,则去undolog链中找上一条记录的DB_TRX_ID,重复123步查找
所以事务 4修改后提交的最新结果对事务 2 快照读时是可见的,获取字段的值为'A'
MVCC 相关问题
RR 是如何在 RC 级的基础上解决不可重复读的?
Read View 生成时机的不同,从而造成 RC , RR 级别下快照读的结果的不同
当前读和快照读在 RR 级别下的区别:
表1:
事务A事务B开启事务开启事务快照读(无影响)查询金额为500快照读查询金额为500更新金额为400提交事务select 快照读金额为500select lock in share mode当前读金额为400
在上表的顺序下,事务 B 的在事务 A 提交修改后的快照读是旧版本数据,而当前读是实时新数据 400
表2:
事务A事务B开启事务开启事务快照读(无影响)查询金额为500更新金额为400提交事务select 快照读金额为400select lock in share mode当前读金额为400
而在表 2这里的顺序中,事务 B 在事务 A 提交后的快照读和当前读都是实时的新数据 400,这是为什么呢?
表2的第一次快照读在事务A更新金额为400而且commit之后发生
RC , RR 级别下的 InnoDB 快照读有什么不同?
正是 Read View 生成时机的不同,从而造成 RC , RR 级别下快照读的结果的不同
在 RC 隔离级别下,是每个快照读都会生成并获取最新的 Read View;
而在 RR 隔离级别下,则是同一个事务中的第一个快照读才会创建 Read View, 之后的快照读获取的都是同一个 Read View。
标签: #mysql不支持check