昆明网站多端小程序设计,芍药居网站建设公司,网络运营需要学什么专业,百度景安空间网站文章目录 一、事务1.1 事务概述1.2 事务控制语句1.3 ACID特性 二、隔离级别2.1 隔离级别的分类2.1.1 读未提交#xff08;RU#xff09;2.1.2 读已提交#xff08;RC#xff09;2.1.3 可重复读#xff08;RR#xff09;2.1.4 串行化 2.2 命令2.3 并发读异常2.3.1 脏读2.3… 文章目录 一、事务1.1 事务概述1.2 事务控制语句1.3 ACID特性 二、隔离级别2.1 隔离级别的分类2.1.1 读未提交RU2.1.2 读已提交RC2.1.3 可重复读RR2.1.4 串行化 2.2 命令2.3 并发读异常2.3.1 脏读2.3.2 不可重复读2.3.3 幻读2.3.4 丢失更新 2.4 区别2.5 测试代码 三、MVCC3.1 read view3.2 当前读 快照读3.3 聚集索引隐藏列3.4 事务的可见性问题 四、redo log五、undo log 一、事务
1.1 事务概述
事务并发连接场景下用户定义的一个操作序列。这些操作要么都做要么都不做是一个不可分割的工作单位。
MySQL的事务就是将多条SQL语句作为整体进行执行。
1事务的目的事务将数据库从一种一致性状态转换为另一种一致性状态保证系统始终处于一个完整且正确的状态。
2事务的组成事务可由一条非常简单的 SQL 语句组成也可以由一组复杂的SQL 语句组成。
3事务的特征在数据库提交事务时可以确保要么所有修改都已经保存要么所有修改都不保存。事务是访问并更新数据库各种数据项的一个程序执行单元。在 MySQL innodb 下单条语句都具备事务。
1.2 事务控制语句
-- 显示开启事务
START TRANSACTION | BEGIN
-- 提交事务并使得已对数据库做的所有修改持久化
COMMIT
-- 回滚事务结束用户的事务并撤销正在进行的所有未提交的修改
ROLLBACK
-- 创建一个保存点一个事务可以有多个保存点
SAVEPOINT identifier
-- 删除一个保存点
RELEASE SAVEPOINT identifier
-- 事务回滚到保存点
ROLLBACK TO [SAVEPOINT] identifier1.3 ACID特性
1原子性A 事务是访问并更新数据库各种数据项的一个程序执行单元是不可分割的工作单位。因此事务操作要么全部执行要么全部不执行不存在中间状态。若事务执行过程发生错误通过 undolog 来回滚到事务初始状态。undolog 记录的是事务每步具体操作当回滚时回放事务具体操作的逆运算。
undo log 存放在共享表空间内用于存储旧版本的数据。主要有两个作用 ∘ \circ ∘ 事务回滚记录事务 DML 操作步骤通过逆运算逻辑取反实现事务回滚。 ∘ \circ ∘ MVCC记录事务 DML 操作提交后产生的行数据版本信息。
2一致性C 事务的前后所有的数据都保持一个一致的状态不能违反数据的一致性检测完整性约束检查
一致性指事务将数据库从一种一致性状态转变为下一种一致性的状态在事务执行前后数据库完整性约束没有被破坏。一个事务单元需要提交之后才会被其他事务可见。一致性由原子性、隔离性以及持久性共同来维护的。
一致性的种类 ∘ \circ ∘ 数据一致性数据库完整性约束 – 五大约束必须遵守。 ∘ \circ ∘ 预期一致性逻辑一致性可以适当破坏。例查询是否存在不存在写入可能出现数据查询不存在插入时却存在此时会报错。
3隔离性I 事务的隔离性要求每个读写事务的对象对其他事务的操作对象能相互分离并发事务之间不会相互影响防止多个并发事务交叉执行导致数据不一致。
不同程度的隔离级别应对不同的现象如脏读、不可重复读、幻读。可以通过适度破环一致性得以提高性能。
通过 MVCC和 锁来实现 ∘ \circ ∘ MVCC 多版本并发控制主要解决数据库中多个事务并发执行时可能出现的读写冲突和数据不一致问题。通过记录和获取行版本而不是使用锁来限制读操作从而实现高效并发读性能。 ∘ \circ ∘ 锁用来处理并发 DML 操作数据库中提供粒度锁的策略针对表聚集索引 B 树、页聚集索引 B 树叶子节点、行叶子节点当中某一段记录行三种粒度加锁
4持久性D 事务一旦完成要将数据所做的变更记录下来包括数据存储和多副本的网络备份。
事务提交后事务 DML 操作将会持久化写入 redolog 磁盘文件 哪一个页 页偏移值 具体数据即使发生宕机等故障数据库也能将数据恢复。
redolog 记录的是物理日志。事务提交后记录事务 DML 操作对应物理页修改的内容写入 redo log 磁盘文件。发生宕机等故障时恢复数据库数据。
二、隔离级别
ISO 和 ANIS SQL 标准制定了四种事务隔离级别目的在于提升数据库并发性能。MySQL innodb默认支持的隔离级别是REPEATABLE READ。
2.1 隔离级别的分类
读未提交Read Uncommitted、读已提交Read Committed、可重复读Repeatable Read、串行化Serializable。
2.1.1 读未提交RU
最低级别的隔离级别事务可以读取到其他事务未提交的数据。即事务还未提交其变更就能被其他事务看到
问题可能导致脏读、不可重复读和幻读问题。
加锁情况该级别下读不加锁写自动加排他锁写锁在事务提交或回滚后释放锁。
2.1.2 读已提交RC
事务只能读取到其他事务已提交的数据。即事务提交后其变更才能被其他事务看到
问题避免了脏读问题。但仍然可能出现不可重复读和幻读问题。
加锁情况该级别后支持 MVCC (多版本并发控制)也就是提供一致性非锁定读提供了不加锁的读取操作此时读取操作读取历史快照数据该隔离级别下读取历史版本的最新数据所以读取的是已提交的数据。写自动加排他锁
2.1.3 可重复读RR
事务开始后保证在整个事务过程中读取的数据是一致的。即事务执行过程中看到的数据一直跟与该事务启动时看到的数据是一致的。
问题避免了脏读和不可重复读问题但仍然可能出现幻读问题。
加锁情况该级别下也支持 MVCC此时读取操作读取事务开始时的版本数据。写自动加排他锁
2.1.4 串行化
最高级别的隔离级别事务串行执行确保了最高程度的隔离性。即在多个事务对这条记录进行读写操作时如果发生了读写冲突的时候后访问的事务必须等前一个事务执行完成才能继续执行
问题避免了脏读、不可重复读和幻读问题但可能导致较高的并发性能开销。
加锁情况该级别下给读加了共享锁写自动加排他锁。
2.2 命令
-- 设置隔离级别
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 或者采用下面的方式设置隔离级别
SET tx_isolation REPEATABLE READ;
SET global.tx_isolation REPEATABLE READ;
-- 查看全局隔离级别
SELECT global.tx_isolation;
-- 查看当前会话隔离级别
SELECT session.tx_isolation;
SELECT tx_isolation;
-- 手动给读加 S 锁
SELECT ... LOCK IN SHARE MODE;
-- 手动给读加 X 锁
SELECT ... FOR UPDATE;
-- 查看当前锁信息
SELECT * FROM information_schema.innodb_locks;2.3 并发读异常
准备工作
DROP TABLE IF EXISTS account_t;
CREATE TABLE account_t (id INT(11) NOT NULL,name VARCHAR(255) DEFAULT NULL,money INT(11) DEFAULT 0,PRIMARY KEY (id),KEY idx_name (name)
)ENGINE INNODB AUTO_INCREMENT0 DEFAULT CHARSET utf8;INSERT INTO account_t VALUES (7,M,1000), (1, C, 1000),(2, B, 1000),(3, A, 1000);2.3.1 脏读
一个事务读到了另一个事务未提交的修改读到脏数据。
不可重复读在 RU 隔离级别存在。在读写分离的场景下可以将 slave 节点设置为 READ UNCOMMITTED。此时脏读不影响在 slave 上查询并不需要特别精准的返回值。
例如:session B 读到了 session A 中事务未提交的脏数据。
seqsession Asession B1SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;2BEGIN;BEGIN;3UPDATE account_t SET money money - 100 WHERE name ‘A’;4UPDATE account_t SET money money - 100 WHERE name ‘A’;5COMMIT;COMMIT; 2.3.2 不可重复读
事务A) 可以读到另外一个事务B中提交的数据通常发生在一个事务中两次读到的数据是不一样的情况不可重复读在隔离级别 READ COMMITTED 存在。一般而言不可重复读的问题是可以接受的因为读到已经提交的数据一般不会带来很大的问题.
例如session B 读到了 session A 中事务提交的修改造成两次读取同一个数据不一样。
seqsession Asession B1SET TRANSACTION ISOLATION LEVEL READ COMMITTED;SET TRANSACTION ISOLATION LEVEL READ COMMITTED;2BEGIN;BEGIN;3UPDATE account_t SET money money - 100 WHERE name ‘A’;4SELECT money FROM account_t WHERE name ‘A’;5COMMIT;SELECT money FROM account_t WHERE name ‘A’;6COMMI; 2.3.3 幻读
两次读取同一个范围内的记录得到的结果集不一样。
例如以name 为唯一键的表一个事务中查询 select * from table where name tom; 不存在接下来 insert into table (name) values (tom); 出现错误此时另外一个事务也执行了 insert 操作
幻读在隔离级别 REPEATABLE READ 及以下存在但是可以在 REPEATABLE READ 级别下通过读加锁使用 next-key locking解决
例如由于 session A 的事务提交了插入操作导致 session B 两次查询范围的结果不一样。
seqsession Asession B1SET TRANSACTION ISOLATION LEVEL READ COMMITTED;SET TRANSACTION ISOLATION LEVEL READ COMMITTED;2BEGIN;BEGIN;3INSERT INTO account_t(id,name,money) VALUES (4,‘D’,1000);4SELECT * FROM account_t WHERE id 2;5COMMIT;6SELECT * FROM account_t WHERE id 2;7INSERT INTO account_t(id,name,money) VALUES (4,‘D’,1000);8SELECT * FROM account_t WHERE id 2 LOCK IN SHARE MODE;9COMMI; 解决通过读加锁next-key locking
seqsession Asession B1SET TRANSACTION ISOLATION LEVEL READ COMMITTED;SET TRANSACTION ISOLATION LEVEL READ COMMITTED;2BEGIN;BEGIN;3SELECT * FROM account_t WHERE id 2 LOCK IN SHARE MODE;4INSERT INTO account_t(id,name,money) VALUES (4,‘D’,1000);5INSERT INTO account_t(id,name,money) VALUES (4,‘E’,1000);6COMMI;7COMMI; 2.3.4 丢失更新
脏读、不可重复读、幻读都是一个事务写一个事务读由于一个事务的写导致另一个事务读到了不该读的数据。 丢失更新是两个事务都是写。丢失更新分为提交覆盖和回滚覆盖回滚覆盖会被数据库拒绝所以不可能产生重点关注提交覆盖。
seqsession Asession B1SET TRANSACTION ISOLATION LEVEL READ COMMITTED;SET TRANSACTION ISOLATION LEVEL READ COMMITTED;2BEGIN;BEGIN;3SELECT money FROM account_t WHERE name ‘A’;4SELECT money FROM account_t WHERE name ‘A’;5UPDATE account_t SET money 1100 WHERE name ‘A’;6COMMIT;7UPDATE account_t SET money 1100 WHERE name ‘A’;8COMMIT;
2.4 区别
1脏读和不可重复读的区别在于脏读是读取了另一个事务未提交的数据而不可重复读是读取了另一个事务提交后的修改。本质上都是其他事务的修改影响了本事务的读取。
2不可重复读和幻读比较类似不可重复读是两次读取同一条记录得到不一样的结果而幻读是两次读取同一个范围内的记录得到的结果集不一样可能不同个数也可能相同个数内容不一样比如x一行后又添加新行。
3不可重复读是因为其他事务进行了 update 操作幻读是因为其他事务进行了 insert或者 delete 操作。
隔离级别回滚覆盖脏读不可重复读幻读提交覆盖READ UNCOMMITTEDNOYESYESYESYESREAD COMMITTEDNONOYESYESYESREPEATABLE REANONONOYES (手动加锁)YES (手动加锁)SERIALIZABLENONONONONO
MySQL InnoDB 引擎的默认隔离级别是可重复读但是它可以很大程度上避免幻读现象。 1针对快照读通过 MVCC 方式解决了幻读。因为可重复读隔离级别下事务执行过程中看到的数据一直跟这个事务启动时看到的数据是一致的即使中途有其他事务插入了一条数据是查询不出来这条数据的所以就很好了避免幻读问题。
2针对当前读是通过 next-key lock记录锁间隙锁方式解决了幻读。因为当执行 select 语句的时候会加上 next-key lock如果有其他事务在 next-key lock 锁范围内插入了一条记录那么这个插入语句就会被阻塞无法成功插入所以就很好了避免幻读问题。
2.5 测试代码
DROP TABLE IF EXISTS account_t;
CREATE TABLE account_t (id INT(11) NOT NULL,name VARCHAR(255) DEFAULT NULL,money INT(11) DEFAULT 0,PRIMARY KEY (id),KEY idx_name (name)
)ENGINE INNODB AUTO_INCREMENT0 DEFAULT CHARSET utf8;SELECT * from account_t;rollback;
INSERT INTO account_t VALUES (7,M,1000), (1, C, 1000),(2, B, 1000),(3, A, 1000);insert into -- 脏读读取了另一个事务未提交的修改 其他事务的修改影响了本事务的读取
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN
-- 脏读事务1
UPDATE account_t SET money money - 100 WHERE name A;
-- 脏读事务2
-- SELECT money FROM account_t WHERE name A;
-- SELECT money FROM account_t WHERE name B;
-- 脏读事务1
UPDATE account_t SET money money 100 WHERE name B;
-- 脏读事务1
COMMIT;
-- 脏读事务2
-- COMMIT-- 不可重复读读取了另一个事务提交之后的修改其他事务的修改影响了本事务的读取
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN
-- 不可重复读事务2
SELECT money FROM account_t WHERE name A;
-- 不可重复读事务1
UPDATE account_t SET money money - 100 WHERE name A;
-- 不可重复读事务1
COMMIT;
-- 不可重复读事务2
-- SELECT money FROM account_t WHERE name A;
-- COMMIT-- 幻读两次读取得到的结果集不一样
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN
-- 幻读事务2
-- SELECT * FROM account_t WHERE id 2;
-- 幻读事务1
INSERT INTO account_t(id,name,money) VALUES (4,D,1000);
-- 幻读事务1
COMMIT
-- 幻读事务2
-- SELECT * FROM account_t WHERE id 2;
-- COMMIT;-- 丢失更新提交覆盖
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN
-- 丢失更新事务1
SELECT money FROM account_t WHERE name A;
-- 丢失更新事务2
-- SELECT money FROM account_t WHERE name A;
-- UPDATE account_t SET money 1100 WHERE name A;
-- COMMIT;
-- 丢失更新事务1
UPDATE account_t SET money 900 WHERE name A;
COMMIT;三、MVCC
多版本并发控制 MVCC(Multiversion Concurrency Control)用来实现一致性的非锁定读。非锁定读是指不需要等待访问记录 X 锁排他锁的释放。因此 MVCC 没有读写阻塞只有写写阻塞提高了并发性。
在 read committed 和 repeatable read 下innodb 使用MVCC但是它们对于快照数据的定义不同。 1在 read committed 隔离级别下对于快照数据总是读取被锁定行的最新一份快照数据 2而在 repeatable read 隔离级别下对于快照数据总是读取事务开始时的行数据版本
3.1 read view
read view 是事务进行快照读的时候产生的读视图保存了当前事务开启时所有活跃事务的列表。
在 read committed 和 read repeatable 隔离级别下MVCC 采用read view 来实现的它们的区别在于创建 read view 时机不同 1read committed 隔离级别会在事务中每个 select 都会生成一个新的 read view也意味着在同一个事务多次读取同一条数据可能出现数据不一致因为在多次读取期间可能有其他事务修改了该条记录并提交了
2read repeatable 隔离级别是启动事务时生成一个 readview在整个事务读取数据都才使用这个 read view这样保证了在事务期间读到的数据都是事务启动前的记录。
3.1.1 构成 1 m_ids 创建 read view 时当前数据库活跃事务已启动但未提交的事务的事务 id 列表
2min_trx_id 创建 read view 时 m_ids 中的最小事务 id
3max_trx_id 创建 read view 时当前数据库将为下一个事务分配的事务 id并不一定是 m_ids 中的最大事务 id
4creator_trx_id 创建 read view 所在事务的 id 3.2 当前读 快照读
3.2.1 当前读Current Read
当前读是指读取最新提交的数据。当进行当前读时事务会读取已经提交并且已经写入到数据库中的最新数据。当前读可以获取到最新的数据但也可能会受到并发事务的影响因为正在执行的事务可能会修改正在被读取的数据从而导致一致性问题。对此当前读使用行级锁定来确保读取最新提交的数据的一致性。
-- 出现当前读的情况
-- 1
select * from table where ... lock in share mode;-- 2
select * from table where ... for update;-- 3
insert into table values(...);-- 4
update table set ?? where ...;-- 5
delete from table where ...;3.2.2 快照读Snapshot Read 快照读是指读取一个指定时间点的数据快照。当进行快照读时事务会读取在事务开始之前已提交的数据状态即使在事务执行期间其他事务进行了更改或提交。这意味着快照读可以提供一种一致性的视图不受并发事务的干扰。
MVCC 的读指的是快照读非锁定读因为没有事务需要对历史数据进行 DML 操作。 在读已提交和可重复读的隔离级别下对于快照数据的定义不同 ∘ \circ ∘ RC 级别读取当前事务锁定行的最新行记录。 ∘ \circ ∘ RR 级别读取启动事务时的行记录版本。
-- 出现快照读的情况
select * from table where ......3.3 聚集索引隐藏列
1trx_id 当某个事务对某条聚集索引记录进行修改时将会把当前事务的 id 赋值给 trx_id
2roll_pointer 当某个事务对某条聚集索引记录进行修改时会将上一个版本的记录写到 undo log然后通过roll_pointer 指向旧版本记录通过它可以找到修改前的记录
3.4 事务的可见性问题
3.4.1 事务状态
1已提交的事务
2已启动未提交的事务
3还没开始的事务
3.4.2 事物的可见性
事务可以看到事物本身的修改。具体可以通过如下判断
1trx_id min_trx_id 说明该记录在创建 read_view 之前已经提交所以对当前事务可见
2trx_id max_trx_id 说明该记录是在创建 read_view 之后启动事务生成的所以对当前事务不可见
min_trx_id trx_id max_trx_id 此时需要判断是否在 m_ids 列表中 ∘ \circ ∘ 在列表中生成该版本记录的事务仍处于活跃状态该版本记录对当前事务不可见 ∘ \circ ∘ 不在列表中生成该版本记录的事务已经提交该版本记录对当前事务可见 四、redo log
redo 日志用来实现事务的持久性 内存中包含 redo logbuffer磁盘中包含 redo log file
当事务提交时必须先将该事务的所有日志写入到重做日志文件进行持久化待事务的 commit 操作完成才完成了事务的提交
redo log 顺序写记录的是对每个页的修改页、页偏移量、以及修改的内容在数据库运行时不需要对 redo log 的文件进行读取操作只有发生宕机的时候才会拿redo log 进行恢复。
五、undo log
undo 日志用来帮助事务回滚以及 MVCC 的功能存储在共享表空间中
undo 是逻辑日志回滚时将数据库逻辑地恢复到原来的样子根据 undo log 的记录做之前的逆运算比如事务中有 insert 操作那么执行 delete 操作对于 update 操作执行相反的 update 操作
同时 undo 日志记录行的版本信息用于处理 MVCC 功能