事务
什么是事务, 数据库系统概论中的定义
mylsam不支持事务, 引入innodb之后才支持事务
原子性
mysql中如何保证原子性? undolog
一致性
从一个正确的状态迁移到另一个正确的状态, 但这种学术化的定义在实际中几乎无法满足
数据库本身无法保证 一致性一般有应用层的业务逻辑来保证
隔离性
并发数据问题与对应的隔离级别
1
脏读 不可重复读 幻读 更新丢失 读未提交 读已提交 可重复读 串行化
悲观并发控制
锁升级: 索引失效,
所谓的悲观并发控制就是假定并发过程一定会出现数据的读写冲突, 通过外部资源(一般是锁)来控制并发过程, 让可能出现冲突的代码按照设定的优先级执行.
在mysql中, 基于锁的并发控制LBCC就是悲观并发控制思想的一种具体实现
行锁和表锁: —
MySQL InnoDB支持三种(排它锁, 写锁)行锁定方式:InnoDB的默认加锁方式是next-key 锁。
- 行锁(Record Lock):锁直接加在索引记录上面,锁住的是key。
- 间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变。间隙锁是针对事务隔离级别为可重复读或以上级别而已的。
- 临键锁(Next-Key Lock):行锁和间隙锁组合起来就叫Next-Key Lock。
默认情况下,InnoDB工作在可重复读(Repeatable Read)隔离级别下,并且会以Next-Key Lock的方式对数据行进行加锁,这样可以有效防止幻读的发生。Next-Key Lock是行锁和间隙锁的组合,当InnoDB扫描索引记录的时候,会首先对索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。加上间隙锁之后,其他事务就不能在这个间隙修改或者插入记录。 read committed隔离级别下
Gap Lock在InnoDB的唯一作用就是防止其他事务的插入操作,以此防止幻读的发生。Innodb自动使用间隙锁的条件: (1)必须在Repeatable Read级别下 (2)检索条件必须有索引(没有索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)
innodb数据引擎会自动对修改数据的操作添加行锁, 具体哪种行锁会根据SQL语句中的条件来自动给出
RU级别: 语句级别的读写锁; 处于更新中的数据会被加锁, 一旦当前记录处理完毕, 锁就会被释放, 不管当前事务是否执行完毕, 因此会影响其他事务所读到的数据, 一旦当前事务发生回滚, 就可能发生脏读
RC级别: 让行锁在当前事务提交之后释放即可
RR级别: 单纯用锁实现RR隔离级别需要对所有在事务运行过程中能读到数据加排它锁(如果加共享锁则有可能被其他事务加的排它锁暂时阻塞, 从而使得数据依然被修改), 这就使得对锁的维护成本极高, 并且很容易造成死锁; 因此LBCC在实际应用中几乎很少被使用.
乐观并发控制
MVCC只是工作在两种事务级别底下:(a) Read Committed (b) Repeatable Read;
因为其他两种:
(c)READ UNCOMMITTED==》总是读取最新的数据,不符合当前事务版本的数据行, (d)Serializable则会对所有的行加锁。
尽管Innodb在语句级别采用的是悲观并发控制策略, 即默认给更新插入删除操作添加
但Innodb数据引擎对事务隔离级别的实现默认使用的是乐观并发控制策略
相比于假定冲突必然发生的悲观并发控制, 乐观并发控制的核心思想就是先执行操作, 当冲突发生时再手动处理.
乐观并发控制一般通过记录数据的版本号来实现, 再访问可能出现冲突的数据时通过判断版本号是否变化就能观察出当前数据是否被其他并发实体所修改,
在mysql中, 基于版本号的并发控制MVCC就是乐观并发控制思想的一种具体实现, 事务执行前会记录相关数据的版本快照, 根据不同的隔离级别, 运行时的快照读取策略也有所不同, 从而实现了对事务隔离级别的控制
RR级别(MVCC+临键锁):间隙锁只有在事务隔离级别 RR 中才会产生; 唯一索引只有锁住多条记录或者一条不存在的记录的时候,才会产生间隙锁,指定给某条存在的记录加锁的时候,只会加记录锁,不会产生间隙锁; 普通索引不管是锁住单条,还是多条记录,都会产生间隙锁; 间隙锁会封锁该条记录相邻两个键之间的空白区域,防止其它事务在这个区域内插入、修改、删除数据,这是为了防止出现 幻读 现象;普通索引的间隙,优先以普通索引排序,然后再根据主键索引排序(多普通索引情况还未研究); 事务级别是RC(读已提交)级别的话,间隙锁将会失效。
持久性
crash-safe能力
redolog (Write-Ahead Logging, WAL) 先写日志
为了降低I/O成本, 对数据的修改会首先保存在内存中, 如果数据修改和读取只依赖内存的缓冲区,那么一旦数据库宕机,内存中的数据都会丢失。所以MySQL使用之前讲过的redo log来实现异常重启的数据恢复,简单来说,就是在更新缓冲区之前,先写入redo log,保证异常重启之后可以正常恢复缓冲区中的数据。
存储在Innodb_log_buffer中
然后会通过以下三种方式将innodb日志缓冲区的日志刷新到磁盘 1,Master Thread 每秒一次执行刷新Innodb_log_buffer到重做日志文件。 2,每个事务提交时会将重做日志刷新到重做日志文件。 3,当重做日志缓存可用空间 少于一半时,重做日志缓存被刷新到重做日志文件 由此可以看出,重做日志通过不止一种方式写入到磁盘,尤其是对于第一种方式,Innodb_log_buffer到重做日志文件是Master Thread线程的定时任务。 因此重做日志的写盘,并不一定是随着事务的提交才写入重做日志文件的,而是随着事务的开始,逐步开始的。 另外引用《MySQL技术内幕 Innodb 存储引擎》(page37)上的原话: 即使某个事务还没有提交,Innodb存储引擎仍然每秒会将重做日志缓存刷新到重做日志文件。 这一点是必须要知道的,因为这可以很好地解释再大的事务的提交(commit)的时间也是很短暂的。
二次提交 osbuffer fsync
有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
分布式
主从同步
读写分离
canal redis
SQL优化
存储过程