数据库四大特性大家应该都知道(ACID)
原子性,确保不管交易过程中发生了什么意外状况(服务器崩溃、网络中断等),不能出现A账户少了一个亿,但B账户没到帐,或者A账户没变,但B账户却凭空收到一个亿(数据不一致)。A和B账户的金额变动要么同时成功,要么同时失败(保持原状)。
隔离性,如果A在转账1亿给B(T1),同时C又在转账3亿给A(T2),不管T1和T2谁先执行完毕,最终结果必须是A账户增加2亿,而不是3亿,B增加1亿,C减少3亿。
持久性,确保如果 T1 刚刚提交,数据库就发生崩溃,T1执行的结果依然会保持在数据库中。
一致性,确保钱不会在系统内凭空产生或消失, 依赖原子性和隔离性。
对于如何保证隔离性,数据库提供了四种隔离级别:
1、串行化(Serializable,SQLite默认模式):最高级别的隔离。两个同时发生的事务100%隔离,每个事务有自己的"世界", 串行执行。
2、可重复读(Repeatable read,MySQL默认模式):如果一个事务成功执行并且添加了新数据(事务提交),这些数据对其他正在执行的事务是可见的。但是如果事务成功修改了一条数据,修改结果对正在运行的事务不可见。所以,事务之间只是在新数据方面突破了隔离,对已存在的数据仍旧隔离。
3、读取已提交(Read committed,Oracle、PostgreSQL、SQL Server默认模式):可重复读+新的隔离突破。如果事务A读取了数据D,然后数据D被事务B修改(或删除)并提交,事务A再次读取数据D时数据的变化(或删除)是可见的。这叫不可重复读(non-repeatable read)。
4、读取未提交(Read uncommitted):最低级别的隔离,是读取已提交+新的隔离突破。如果事务A读取了数据D,然后数据D被事务B修改(但并未提交,事务B仍在运行中),事务A再次读取数据D时,数据修改是可见的。如果事务B回滚,那么事务A第二次读取的数据D是无意义的,因为那是事务B所做的从未发生的修改(已经回滚了嘛)。这叫脏读(dirty read)。
接下来就仔细聊聊这四种隔离级别 ——

    事务1
    begin  —— 开启事务
    insert into table_name values(name,age);
    update table_name set name = 'abc',age = 'male' where id = 1;
    commit; —— 提交

    事务2
    begin —— 开启事务
    select count(*) from table_name; —— 第一次读count
    select name from table_name where id = 1; —— 第一次读*

    select count(*) from table_name; —— 第二次读count
    select name from table_name where id = 1; —— 第二次读*
    commit; 

1、串行化顾名思义,数据库事务按照队列的形式,依次执行,互不干扰。可以解决数据库事务脏读、幻读、不可重复读的问题,但是缺点就是会造成超时和锁竞争的问题;
2、可重复读
事务2执行到一半,事务1已经提交:
——此时第一次读count和第二次读count数据不一致,因为事务1已经提交,新纪录的插入会导致两次count()的数值不一致,造成幻读,不隔离新数据;
——第一次读name和第二次读name的数据是一致的,隔离已经存在的数据,可以重复读;
3、读已提交
事务2执行到一半,事务1已经提交:
——此时第一次读count和第二次读count数据不一致,因为事务1已经提交,新纪录的插入会导致两次count()的数值不一致,造成幻读,不隔离新数据;
——第一次读name和第二次读name的数据也是不一致的,不隔离已经存在的数据,不可以重复读;
4、读未提交
事务2执行到一半时,事务1 还未提交
——事务2中 第二次读count得到的值和第一次读count得到的值不一样(因为事务1新增了一条数据),产生幻读,不隔离新增的数据。
——事务2中 第一次读name 和第二次读name得到的值是不一样的(事务1未提交),对最新版本的值可见,不隔离已经存在的数据。 不可以重复读,读到的数据是不一样的。
——如果此时事务1因为其他原因回滚了,事务2第二次读到的数据是无意义的,因为修改没有发生(回滚了),产生脏读 。

另外附上一张隔离级别比较的图片
图片说明