Mysql事务相关介绍
1. 事务的含义
事务(Transaction)是一系列不可再拆分的对数据操作的集合。事务可以在隔离应用程序间并发操作,启到相互隔离左右。
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点之上。通常一个分布式事务会涉及对多个数据源或业务系统的操作。
2. 事务的特性
2.1 ACID特性
事务具有ACID特性:原子性(A,atomicity)、一致性(C,consistency)、隔离性(I,isolation)、持久性(D,durabulity)。
原子性(A|atomicity): 事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行过程中,要么全部成功,要么全部失败。
一致性(C|consistency):事务的执行不能破坏数据的完整性和一致性,一个事务在执行前后,数据库都必须处于一致性状态。即事务的执行结果必须是使数据库从一个一致性状态转变到另一个一致性状态。
隔离性(I|isolation): 在并发环境中,并发的事务是相互隔离的,事务之间互不干扰。
持久性(D|durabulity): 一个事务一旦提交,对数据库中对应数据的状态变更就应该是永久性的。即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束时的状态。
2.2 CAP
一个分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本要求,最多只能满足其中的两项。分区容错性可以说是一个分布式系统最基本的要求。
一致性(C:Consistency): 在分布式环境中,一致性是指数据在多个副本之间是否能够保持一致的特性。
可用性(A:Availability):系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回正常的响应结果。
分区容错性(P:Partition tolerance):要求一个分布式系统在遇到任何网络分区故障的时候,仍然能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。
2.3 BASE理论
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写。BASE是对CAP中一致性和可用性权衡的结果。其核心思想是让每个应用都可以根据自身的业务特点,采用适当的方法来使系统达到最终一致性的结果。
基本可用(Basically Available):分布式系统在出现不可预知故障的时候,允许损失部分可用性(不等价于系统不可用)
软状态(Soft state):允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同的数据副本之间进行数据同步的过程存在延时。
最终一致性(Eventually consistent):系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。经典案例主从异步复制或延迟复制模式可以保证最终数据达到一致性。
3. 并发事务的问题
1)脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”。
2)不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在第一个事务中的两次读数据之间,由于第二个事务的修改(update/delete)导致第一个事务两次读取的数据可能不一样的结果。这种情况称为不可重复读。
3)幻读(Phantom read): 与不可重复读类似,幻读的重点在于数据新增(insert)这种数据变更类型。
4. 事务隔离级别
SQL92标准定义了4种隔离级别:Read Uncommitted(未提交读)、Read Committed(提交读)、Repeatable Read(可重复读)Serializable(串行化)
简称 | 隔离级别 | 解析 | 解决的并发问题 |
RU | Read Uncommitted(未提交读) | 即允许读取未提交的数据。事务未提交对其他事务也是可见的,脏读(dirty read) | 否 |
RC | Read Committed(提交读) | 一个事务开始之后,只能看到自己提交的事务所做的修改,不可重复读(non repeatable read) | 脏读问题 |
RR | Repeatable Read(可重复读) | 在同一个事务中多次读取同样的数据结果是一样的,这种隔离级别未定义解决幻读的问题 | 不可重复读问题 |
Serializable(串行化) | 最高的隔离级别,通过强制事务的串行执行 解决所有问题 | 所有并发问题 |
InnDB 引擎默认的事务隔离级别是可重复读(Repeatable Read),在该级别中它把幻读的问题也解决了。InnDB 中事务隔离级别通过锁、MVCC 实现。
# 设置和查看事务隔离级别
SET [GLOBAL | SESSION] TRANSACTION
ISOLATION LEVEL [type | access_mode]
type:
REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED | SERIALIZABLE
access_mode:
READ WRITE |READ ONLY
或者
set @@global.tx_isolation = 'read-uncommitted' | 'read-committed' | 'repeatable-read' | 'serializable'
set @@session.tx_isolation = 'read-uncommitted' | 'read-committed' | 'repeatable-read' | 'serializable'
/* 查看全局/当前会话的隔离级别 */
SELECT @@global.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@global.tx_isolation | @@tx_isolation |
+-----------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set, 2 warnings (0.00 sec)
mysql>
/* 设置全局隔离级别 -InnoDB */
set global transaction isolation level read committed;
/* 设置当前会话的隔离级别 */
set session transaction isolation level read committed;
5. 多版本并发控制(MVCC)
MVCC全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
5.1 MVCC的实现方式
1) 将数据记录的多个版本保存在数据库中,当这些不同版本数据不再需要时,垃圾收集器回收这些记录。如:postgresql(VACUUM)
2) 数据库保存最新版本数据,旧版本数据存储单独的位置。如: SQL Server(tempdb),Oracle/InnoDB(undo)
5.2 当前读与当前读
当前读: 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。像select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读。
快照读:不加锁读取的记录,即不加锁的非阻塞读。
像不加锁的select操作就是快照读,快照读的实现是基于多版本并发控制,避免了加锁操作,降低了开销;快照读可能读到的并不一定是数据的最新版本。
5.3 InnoDB实现MVCC
InnoDB的MVCC实现为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。它依赖于记录中的 3个隐式字段,undo日志 ,Read View 来实现的。
隐式字段:
DB_TRX_ID: 6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
DB_ROLL_PTR: 7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
DB_ROW_ID: 6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。
Read View:事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID。
6. 锁模式
6.1 不同存储引擎支持的锁级别
1. MyISAM、Aria(MariaDB中对myisam的改进版本)和memory存储引擎只支持表级别的锁。
2. innodb支持行级别的锁和表级别的锁,默认情况下在允许使用行级别锁的时候都会使用行级别的锁。
3. DBD存储引擎支持页级别和表级别的锁。
6.2 表锁与行锁的区别:
- 锁定粒度:表锁 > 行锁
- 加锁效率:表锁 > 行锁
- 冲突概率:表锁 > 行锁
- 并发性能:表锁 < 行锁
InnoDB 存储引擎支持行锁和表锁(另类的行锁),InnoDB 的表锁是通过对所有行加行锁实现的。
6.3 锁的类型
- 共享锁(行锁 | S):Shared Locks,即读锁,不涉及数据变更,在检索数据时才申请的锁。
- 排他锁(行锁 | X):Exclusive Locks,增、删、改等涉及变更操作的时候,都会申请独占锁。
-- 下面锁类型只有支持行锁或页锁的存储引擎才支持
- 意向锁共享锁(表锁 | IS):Intention Shared Locks,获取低级别共享锁的同时,在高级别上也获取特殊的共享锁,这种特殊的共享锁是意向共享锁。
- 意向锁排它锁(表锁 | IX):Intention Exclusive Locks,获取低级别独占锁的同时,在高级别上也获取特殊的独占锁,这种特殊的独占锁是意向独占锁。
- 自增锁:AUTO-INC Locks
6.4 锁的兼容性表
6.5 行锁的算法
- 记录锁:Record Locks,当 SQL 执行按照唯一性(Primary key、Unique key)索引进行数据的检索时,查询条件等值匹配且查询的数据是存在,这时 SQL 语句加上的锁即为记录锁 Record Locks,锁住具体的索引项。
- 间隙锁:Gap Locks,当 SQL 执行按照索引进行数据的检索时,查询条件的数据不存在,这时 SQL 语句加上的锁即为 Gap locks,锁住数据不存在的区间(左开右开)
- 临键锁:Next-key Locks,当 SQL 执行按照索引进行数据的检索时,查询条件为范围查找(between and、<、>等)并有数据命中,则此时 SQL 语句加上的锁为 Next-key locks,锁住索引的记录 + 区间(左开右闭)。InnoDB 默认的行锁算法。
6.6 锁相关视图
show engine innodb status\G;
select * from information_schema.INNODB_TRX\G;
select * from information_schema.INNODB_LOCKS\G;
select * from information_schema.INNODB_LOCK_WAITS\G;
SELECT
r.trx_id AS waiting_trx_id,
r.trx_mysql_thread_id AS waiting_thread,
r.trx_query AS waiting_query,
b.trx_id AS blocking_trx_id,
b.trx_mysql_thread_id AS blocking_thread,
b.trx_query AS blocking_query
FROM
information_schema.innodb_lock_waits w
JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id\G;
SELECT
trx_id,
trx_state,
lock_id,
lock_mode,
lock_type,
lock_table,
trx_mysql_thread_id,
trx_query
FROM
information_schema.innodb_trx t
JOIN information_schema.innodb_locks l ON l.lock_trx_id = t.trx_id\G;
备注: 文中图片均来自互联网。