文章目录
1. Mysql架构
1.1 架构图
1.2 读写锁
共享锁:读锁
多个用户同一时刻可以同时读取同一个资源互不干扰
排他锁:写锁
写锁会阻塞其他的写锁和读锁
1.3 锁粒度
表级锁
- 开销最小的锁策略
- 对整张表加锁
获取表级写锁会阻塞其他用户对该表的所有读写操作,读锁之间是不互相阻塞的
写锁比读锁具有更高的优先级,写锁请求可能会插入到读锁队列前
除储存引擎,Mysql本身也可以使用有效的表锁来实现不同的目的
行级锁
- 带来最大的锁开销
- 可以最大程度支持并发处理
行级锁只在储存引擎(InnoDB 和 XtraDB)层实现,Mysql服务器层没有实现
1.4 事务
- 事务内的语句,要么全部执行成功,要么全部执行失败。支持事务的系统要通过严格的ACID测试。
- 对于不需要事务的业务场景,用户可以选择一个非事务型的存储引擎,来获得更高的性能,当然即使存储引擎不支持事务,Mysql服务器的表级锁也可以提供一定程度的保护。
原子性(atomicity)
一个事务必须被视为一个不可分割的最小执行单元,要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分。
一致性(consistency)
数据库总是从一个一致性状态转换到另一个一致性状态,除非事务最终提交,不然事务的修改不会提交到数据库中。
隔离性(isolation)
一个事务在最总提交前,通常对其他事务不可见,如事务a过程中对账号金额200进行了修改,但是事务a还未提交,这时事务b看到的账号金额还是是未修改的200。
持久性(durability)
一旦事务提交,其所做修改会永久保存在数据库中。
1.5 隔离级别
每种存储引擎实现的隔离级别不尽相同
READ UNCOMMITTED(未提交读)
- 事务中的修改,即使没有提交,对其他事务也都是可见的。
- 事务可以读取到未提交的数据,这也称为脏读(Dirty Read)。
- 除非真的有非常必要的理由,在实际应用中一般很少使用。
READ COMMITTED(提交读)
- 大多数数据库系统的默认隔离级别(Mysql不是0)。
- 一个事务只能“看见”已经提交的事务所做的修改。一个事务从开始直到提交之前,所做的修改对其他事务不可见。但是可能出现一个事务中两次执行同样查询时,得到结果不一样的情况。
- 该级别也叫不可重复读(nonrepeatable read)。
不可重复读,例如:
- 事务A:
select money from user where account = "123";
结果:2000
- 事务B:
update user set money = 3000 where account = "123";
事务B提交
- 事务A:
select money from user where account = "123";
结果: 3000
事务A提交
事务A过程中出现了前后查询读取到的数据不一致的问题,所以该级别被称作不可重复读。
REPEATABLE READ(可重复读)
- 该级别保证了同一事务中多次读取同样记录的结果是一致的。
- 该级别是Mysql的默认事务级别。但是理论上,该级别还是无法解决另一个幻读(Phantom Read)问题。
- 幻读(Phantom Read)指某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取某个该范围的记录时,就会产生环行。
- InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC, Multiversion Concurrency Control)解决幻读问题。
幻读,例如:
- 事务A:
select * from user;
结果有10行
- 事务B:
Insert into user value(11,"asd");
事务B提交
- 事务A:
select * from user;
结果有11行
此时就出现了幻读,两次读取结果行数不一致。
SERIALIZABLE(可串行化)
- 最高隔离级别。
- 通过强制事务串行执行避免幻读,简单说就是在读取的每一行数据上都加锁,可能导致大量超市和锁争用的问题。
- 实际很少使用,除非要非常需要确保数据的一致性而且可以接受没有并发的情况下。
隔离级别图表
1.6 死锁
当多个事务视图以不同的顺序锁定资源时,就可能会产生死锁。例如,设想下面两个事务同时处理StockPrice表:
事务1:
START TRANSACTION;
UPDATE StockPrice SET close = 45.50 WHERE stock_id = 4 and date = '2002-05-01';
UPDATE StockPrice SET close = 19.80 WHERE stock_id = 3 and date = '2002-05-02';
COMMIT;
事务2:
START TRANSACTION;
UPDATE StockPrice SET close = 45.50 WHERE stock_id = 3 and date = '2002-05-01';
UPDATE StockPrice SET close = 19.80 WHERE stock_id = 4 and date = '2002-05-02';
如果凑巧,两个事务都执行了第一条UPDATE语句,同时也锁定了该行数据,接着每个事务都尝试去执行第二条UPDATE语句,却发现该行被锁定了,然后两个事务都等待对方释放锁,同时又持有对方需要的锁,陷入死循环。
- 为了解决这些问题,数据库系统实现了各种死锁检测和死锁超时机制。
- InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚。
1.7 事务日志
修改数据流程:
修改数据改内存拷贝 -> 修改事务日志 -> 持久化事务日志 -> 内存拷贝持久化
- 事务日志可以提升事务的效率。
- 存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘上。
- 事务日志采取追加方式,写日志的操作是在磁盘上的一小块区域进行顺序I/O,不像随机I/O多次移动磁头,所以事务日志效率较高。
- 事务日志持久以后,内存中被修改的数据在后台可以慢慢地刷回到磁盘。
- 如果修改已经记录到事务日志并持久化,但是数据本身并没有写回磁盘,此时系统崩溃,存储引擎在重启时可以自动恢复这部分修改的数据。
1.8 MySQL中的事务
MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster,另外还有一些第三方的存储引擎也支持事务:XtraDB和PBXT。
自动提交(AUTOCOMMIT)
- Mysql默认采用自动提交,如果不是显示地开始一个事务,则每个查询都会被当作一个事务执行提交操作。
- 当前连接中,可以通过设置AUTOCOMMIT变量来启用或者禁用自动提交操作。