MySQL——事务

一、事务

1、事务的四个特性ACID

① 原子性(atomicity):一个事务必须被视为一个不可分的最小工作单元,整个事务中所有操作要么全部提交成功,要么全部失败回滚。

② 一致性(consistency):数据库总是从一个一致性状态转化到另一个一致性状态。

③ 隔离性(isolation):一个事务所做的修改在最终提交前,对其他事务是不可见的。

④ 持久性(durability):一旦事务提交,所做的修改就会永久保存到数据库(磁盘)中,即使系统奔溃,也不会丢失。

2、事务的隔离级别

① 未提交读(READ UNCOMMITTED):事务中的修改,即便没有提交,对其他事务也都是可见的。

② 提交读(READ COMMITTED):一个事务从开始直到提交前,所做的任何修改对其他事务是不可见的。这是大多数数据库系统默认的隔离级别,但MySQL不是。这个级别有时候也叫不可重复读(nonrepeatable read),因为两次执行同样的查询,可能会得到不一样的结果。

③ 可重复读(REPEATABLE READ):同一事务中多次读取同样的结果是一致的。(Mysql默认的事务隔离级别)

④ 可串行化(SERIALIZABLE):最高的隔离级别。强制事务串行执行。

脏读:事务可以读取未提交的数据

幻读:当事务A在读取某个范围的记录时,事务B又在该范围内插入了新的记录,事务A再次读取该范围的记录时,会产生幻行。

3、死锁

死锁的四个条件:资源互斥,请求并保持,不可剥夺,循环等待

事务可能会导致死锁:两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。

为了解决这种问题,数据库系统实现了各种死锁检测死锁超时机制。越复杂的系统,越能检测到死锁的循环依赖,并立即返回一个错误,比如InnoDB存储系统。

还有一种解决方式,就是当查询的时间达到锁等待超时的设定后放弃锁请求,这种方式通常来说不太好。

InnoDB目前处理死锁的方法是,将持有最少行级排它锁的事务进行回归。死锁的产生于存储引擎有关

4、事务日志

事务日志可以帮助提高事务的效率。

使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录持久化到硬盘的事务日志中,而不用每次都将修改的数据本身持久化到磁盘。

事务日志采用的是追加的方式(跟kafka消息持久化一样),写日志的操作是磁盘上一小块区域内的顺序I/O,不是随机I/0,基本不需要多个地方移动磁头。

事务日志持久化以后,内存中被修改的数据会由后台异步刷回磁盘,目前大多数存储引擎都是这样实现的,我们通常称之为预写式日志(Write-Ahead Logging),修改数据需要写两次磁盘。

如果数据修改已经记录到事务日志并持久化,数据本身还没有写回磁盘,此时宕机了,存储引擎重启时会根据事务日志自动恢复这部分数据。

二、MySQL中的事务

 MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster。另外还有一些第三方引擎也支持事务。

1、自动提交(AUTOCOMMIT)

MySQL默认采用自动提交模式。也就是说,如果不是显示地开始一个事务,则每个查询都被当作一个事务执行提交操作。即进行update-query操作,update可视为事务开启,执行query时commit

在当前连接中,可以通过设置AUTOCOMMIT变量来启用或者禁用自动提交模式  show variables like ‘autocommit’;

 当自动提交关闭(设置为OFF)时,,所有的查询都是在一个事务中,直到显示地执行COMMIT提交或者ROLLBACK回滚,该事务结束,同时又开始了另一个新事务,

修改AUTOMMIT对非事务型的表,比如MyISAM或者内存表,不会有任何影响,对于这类表来说,没有commit和rollback的概念,相当于autocommit启用模式。

另有一些命令,在执行之前会强制执行commit提交当前的活动事务。典型的例子,在数据定义语言(DDL)中,如果是会导致大量数据改变的操作,比如alter table。

MySQL可以通过执行set transaction isolation level命令来设置隔离级别。新的隔离级别会在下一个事务开始时生效。

 2、在事务中混合使用存储引擎

MySQL服务器层是不管理事务的,事务是由下层的存储引擎实现的。所以在同一个事务中,使用多种存储引擎是不可靠的。

 例如:在事务中混合使用事务型和非事务型的表(例如InnoDB和MyISAM表),正常提交情况下不会有什么问题。

但当事务需要回滚时,非事务型的表上的变更就无法撤销,会导致数据库处于不一致状态,这种情况很难修复。事务的最终结果将无法确定。所以,为每张表选择合适的存储引擎非常重要。

在非事务的表上执行事务操作时,MySQL不会有任何提醒,也不会报错,仅当需要回滚时才会发出警告“某些非事务型的表上的变更不能被回滚”。

3、隐式和显示锁定

InnoDB采用的是两阶段锁定协议。

隐式锁定:在事务执行过程中,随时都可以执行锁定,锁只有在执行commit或rollback时才会被释放,并且所有锁时在同一时刻被释放。InnoDB会根据隔离级别在需要时候自动加锁。

显示锁定:InnoDB支持通过特定的语句显示锁定,这些语句不属于SQL规范

select ... lock in share mode
select ... for update

MySQL也支持lock tables和unlock tables语句,但这是在服务层实现的,和存储引擎无关。它们有自己的用途,并不能代替事务处理。如果应用需要用到事务时,还是应该选择事务型存储引擎。

经常可以发现,应用已经将表从MyISAM转换到InnoDB,还是显示使用lock tables语句,这不但没有必要,还会严重影响性能,实际InnoDB的行级锁工作的更好。

建议在任何时候不要执行lock tables。

4、多版本并发控制(MVCC)

MySQL的大多数事务型存储引擎实现都不是简单的行级锁。基于提升并发性能的考虑,他们一般都同时实现了多版本并发控制(MVCC)。

不仅是MySQL,也包括Oracle、PostgreSQL等其他数据库系统也都实现了MVCC。但具体实现机制不尽相同,因为MVCC没有一个统一的实现标准。

MVCC是行级锁的一个变种,但是它再很多情况下避免了加锁操作,因此开销更低。大都实现了非阻塞的读操作,写操作也只锁定必要的行。

MVCC的实现,通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事物看到的数据都是一致的。根据事务开始的时间不同,每个书屋对同一张表,同一时刻看到的数据可能不一样。

InnoDB的MVCC,是通过每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存行的创建时的系统版本号,一个保存行的删除时的系统版本号。每开始一个新的事务,系统版本号就会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。repeatable read隔离级别下的MVCC

select :InnoDB会根据以下两个条件检查每行记录,只有符合下述两个条件的记录,才会返回作为查询结果

a、InnoDB只查找版本早于当前事务版本的数据行(行的版本号<=事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。

b、行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始前未被删除。

insert:InnoDB为新插入的每一行保存当前系统版本号作为行版本号

delete:InnoDB为删除的每一行保存当前系统版本号作为行删除标识

update:InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号的原来的行作为行删除标识。

举个栗子:

-- 现在数据库有三条记录
1 - - - 88 -
2 - - - 88 -
3 - - - 88 -
-- 此时开启一个select事务,系统版本号+1=89,事务开始时刻版本号作为事务版本号=88
-- 情况一、没有其他事务并行执行
-- select 查询这三条记录发现 88=88 88=88 88=88 所以查询结果
1 - - - 88 -
2 - - - 88 -
3 - - - 88 -
-- 情况二、有其他事务并行执行
-- 若在select事务开启同时开启了一个update事务,系统版本号=90,update事务=89
-- update 1 - - - 89 -则
1 - - - 89 -
2 - - - 88 -
3 - - - 88 -
-- 此时select查询这三条记录 89>88 88=88 88=88,查询结果
2 - - - 88 -
3 - - - 88 -

保存两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计是的读数据操作很简单,性能很好,也能保证只会读取到符合标准的行。不足之处每行记录需要额外存储空间,需要更多行检查工作,以及额外的维护工作。

注意:MVCC只能在repeatable read和 read committed两个隔离级别下工作。其他隔离级别不兼容,因为read uncommitted总是读取最新的数据行,而不是符合当前事务版本的行,serializable则会对读取行加锁

5、InnoDB与MyISAM存储引擎

 InnoDB是MySQL默认的存储引擎,也是最重要、使用最广泛的存储引擎。特点是:

① 能处理大量的短期事务。

② 自动崩溃恢复特性,

③ 采用MVCC支持高并发,

④ 实现四个隔离级别,默认repeatable read,并通过间隙锁策略防止幻读的出现。间隙锁是的InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,防止幻影行的插入。

⑤ InnoDB基于聚簇索引建立的

⑥ 支持热备份

⑦ 读取磁盘数据时采用可预测性预读,能够自动在内存中创建hash索引以加速读操作的自适应哈希索引,能够加速插入操作的插入缓冲区。

MyISAM不支持事务和行级锁,而且有一个毫无疑问的缺陷就是崩溃后无法安全恢复,适用于表小,可以忍受修复操作的表。特点是:

① 加锁与并发:MyISAM对整张表加锁,而不是针对行。

② 修复:对于MyISAM表,MySQL可以手工或者自动执行检查和修复操作。

③ 索引特性:支持索引,是一种基于分词创建的索引。

④ 延迟更新索引键 ,异步写入磁盘,但数据库奔溃或宕机会导致索引损坏,需要执行修复操作。

抄自《高性能mysql》

原文地址:https://www.cnblogs.com/wqff-biubiu/p/12347838.html