事务的简单介绍:
当我们要完成一件事情,而这件事情又包含好几个步骤的时候,我们就可以将这件事情看作一个事务。
只有当事务中的每个部分都执行成功以后,这个事务才算执行成功。
Mysql的 Buffer Poll 机制
InnoDB是MySQL的存储引擎之一,数据是存放在磁盘中的。如果我们每次读写数据都需要进行磁盘IO的话,读写的效率就会很低。所以,InnoDB提供一种缓存机制,也就是Buffer Pool,在Buffer Poll 种包含了磁盘中部分数据页的映射,作为访问数据库的缓冲。但我们需要从数据库种读取数据的时候,首先会从Buffer Pool 中读取,当Buffer Poll 中没有需要的数据时,则从磁盘中读取该数据并存入Buffer Pool 中;当我们需要向数据库写入数据时,会首先将该数据写入Buffer Pool 中,Buffer Pool 中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。Buffer Pool 的使用大大提高了数据读写的速率,但是同时也存在了新的问题:如果MySQL宕机,并且此时Buffer Pool 中的数据
还没有刷新到磁盘中,就会导致数据的丢失,事务的持久性无法保证。所以,就需要用到MySQL中的日志来解决这一问题
事务的四大特性:
1、原子性(Atomicity):
一个事务中的全部操作,要么全部完成,要么全部失败,不允许部分成功,部分失败。体现了事务不可分割的特性,即原子性
实现原理:通过 Undo Log 日志来实现,在Undo log 日志中记录了与我们所执行的SQL语句意义相反的一组SQL语句,例如:当我们执行了一条 insert 语句时,在 Undo Log 日志中就会对应的记录一条 delete 语句;当我们执行了一条 delete 语句时,Undo Log 日志中就会记录一条 insert 语句;当我们执行一条 update 语句时,Undo Log 中会记录一条相反的 update 语句。如果事务执行失败或者调用了 rollback 时,导致事务需要回滚,便可以利用 Undo Log 中的记录信息将数据恢复到修改前的样子。
2、一致性(Consistency):
一个事务执行前后,数据库数据必须保持一致,也就是说,一致性保证事务只能把数据库从一个有效(正确)的状态 “转移” 到另一个有效(正确的状态)。而正确的状态一般指的是满足这个数据库事先定义好的一些规则的状态。比如我们现在有一张账户表(Account),有ID(int)、Name(String)、Money(int)三个字段,可以事先将Money定义为值不能小于零。如果对数据的操作违反了这一规则(Money
大于等于0),那么就违反了事务的一致性。
举个例子:
create table Account(
ID int,
Name String,
Money int
);
有这样一张表(规定Money大于等0),表中有两行数据,其中甲的 Money 为500,乙的 Money 为100。现在要让甲给乙转账1000元,如果这一事件成功发生,则此时甲的 Money 为 -500 ,乙的 Money 为 1100 。甲的 Money 出现了一个负数,这与我们的规定不符,所以数据库会避免这样的操作,阻止本次转账。需要注意的是,数据库本身并不会自己判断数据的变化是否符合逻辑,需要我们去做出这些规定,如果我们没有规定Money大于等于0这一规则,那么数据库就会认为这是合理的,从而不会阻止本次转账发生。
如果事务的其他三个特性可以保证,那么一致性也一定可以保证。
实现原理:一致性 = 原子性原理 + 隔离性原理 + 持久性原理 (可以把一致性理解为目的,而其他三个特性理解为为了实现一致性的工具)
3、隔离性(Isolation):
数据库允许多个事务同时对数据进行读写,当多个事务并发执行时,为了保证数据的安全性,将一个事务的内部的操作与其他的事务的操作隔离起来,不被其他正在执行的事务看到。
实现原理:MySQL锁机制 + MVCC机制
Record Locks(记录锁):在索引记录上加上锁
Gap Locks(间隙锁):在索引记录之间加锁,或者在第一个索引记录之前加锁,或者在最后一个索引记录之后加锁。
Next-key Locks:在索引记录上加锁,并且在索引记录之前的间隙加锁。它相当于Record Locks 和Gap Locks 的结合体。
行锁和表锁:
表级锁:开销小,加锁快,不会出现死锁;锁定的粒度大,所以发生所冲突的概率就高,并发度较低
行级锁:开销大,加锁慢,会出现死锁;锁定的粒度小,所以发生所冲突的概率就低,并发度较高
MVCC机制:
MVCC是一种多版本并发控制机制,大多数的存储引擎如InnoDB等,都不使用一种简单的行锁机制,事实上,他们是和MVCC并发控制机制一起来使用的。锁机制虽然可以控制并发操作,但是其系统开销较大,使用MVCC可以在大多数情况下代替行级锁,能够降低系统开销。MVCC是通过保存数据在某一时间点的快照来实现的,每一个事务只操作自己版本下的数据。这样做就是为了避免使用Mysql的这些锁,一但加上锁之后,事务与事务之间的执行顺序就变成了完全的串行执行,使得执行效率很低。
4、持久性(Durability):
即事务完成以后,对数据库中数据的修改是永久性的,即使系统发生了故障,数据也可以恢复。
实现原理:redo log 日志
当进行数据的修改时,除了会修改Buffer Poll 中的数据,还会再redo Log 中记录这次操作,如果Mysql 宕机,重启时可以读取redo Log 中的数据对数据库进行恢复。redo Log 采用的是 WAL(Write Ahead Logging,预写式日志),也就是所有的修改先写入日志,然后再更新到Buffer Pool 中,保证了数据不会因为Mysql 宕机而丢失,保证了数据的持久性。
事务的隔离级别:
事务的隔离级别指,当多个事务处理同一个数据时,其中的一个事务修改数据后,其他事务所能看到被修改数据的时机。事务的隔离级别有四个:Read uncommitted(未提交读)、Read committed(已提交读)、Repeatable read(可重复读)、Serializable(串行化)
由上到下,事务的隔离级别依次提高
脏读(Dirty Read):一个事务读取了另一个事务未提交的数据,比如当A事务与B事务同时执行时,B事务读取到了A事务更新后但并未提交的数据,如果此时A事务发生rollback,那么B事务读取到的就是无效的数据。
不可重复读(NonRepeatable Read):当多个事务并发执行的时候,其中一个事务对数据的修改导致了另一个事务读取到的该数据前后不一。例如:A事务与B事务并发执行的时候,B事务首先读取数据x,然后A事务对数据x进行了修改,最后B事务又去读取了数据x,发现前后两次所读取到的数据不一致。
幻读(Phantom read):当多个事务并发执行的时候,其中一个事务的操作导致了另一个事务前后两次查询的结果数据量不同。例如:①当A事务与B事务并发执行时,B先读取到了一条数据x,但是A将数据x删除掉了,此时B事务再去查询x数据的时候,发现刚刚存在的数据不见了。②当A事务与B事务并发执行时,B事务一开始没有读到数据x,然后A事务添加了一条数据x,此时B事务再去查询的时候突然发现多出了一条数据x。
什么是 Mysql 锁机制:
在 MyIsam 引擎下:
当操作数据时,MyIsam 引擎锁住的是整张表,称之为表锁。
在 InnoDB 引擎下:
InnoDB既支持表锁也支持行锁,与表锁不同,行锁就是操作数据时,仅仅锁住我们所操作的这几行数据,而不影响表中的其他数据。InnoDB 的行锁是根据索引项的记录添加的,当通过索引条件检索数据的时候,InnoDB才会使用行锁,否则将使用表锁。
注意:
不论是行锁还是表锁,都可以分为两类:即读锁与写锁,读锁又称为共享锁,写锁又称为排他锁。“读读不互斥”(都可以操作成功)、“读写”与 “写写” 互斥(只允许一个操作成功),即对数据进行读取操作的时候(如:select语句)加的是读锁,而对数据进行修改的时候(如 insert语句、update语句、delete语句)加的是写锁。
表锁特点:
1、开销小,加锁快
2、不会出现死锁
3、锁定力度大,发生锁冲突的概率最高,并发度(同时成功的概率)最低
行锁特点:
1、开销大,加锁慢
2、可能会出现死锁
3、锁定力度小,发生锁冲突的概率最低,并发度(同时成功的概率)最高