数据库四大特性和隔离级别小记

数据库四大特性

数据库的四大特性缩写为ACID,分开来说就是原子性(A tomicity)、一致性(C onsistency)、隔离性(I solation)、持久性(D urability)。事务是访问并可能更新各种数据项的一个不可再分的程序执行单元。我们要求数据库系统维护事务的ACID性质。(ACID是数据库事务正确执行的4个基本要素,只有满足这4种特性的数据库,才可称之为支持事务的数据库)

  • 原子性:事务的所有操作在数据库中要么全部正确反映出来,要么完全不反映。
  • 一致性:隔离执行事务时(在没有其他事务并发执行的情况下)保持数据库的一致性。
  • 隔离性:指并发的事务是相互隔离的。
  • 持久性:一个事务成功完成后,它对数据库的改变必须是永久的,即使出现系统故障。

一个银行转账的例子:
假设A账户中有1000元,B账户有2000元,存在一个事务,账户A向账户B转账50元。

  • 原子性:A在向B转账过程中,出现了系统故障,A账户剩余950元,而B账户未收到转账,依旧为2000元,此时状态称为不一致状态,为了维护事务的原子性,我们需要从日志中恢复旧值(对于写操作,数据库系统在磁盘上记录其旧值,这个信息记录在一个称为日志的文件中),此时A账户恢复为1000元,B账户为2000元。
  • 一致性:此时的一致性,要求转账前后A+B的账户总金额不变。
  • 隔离性:假设在转账过程中,A账户金额转出50元,而B账户金额未到账,此时存在并行事务T想要读取A、B账户金额总和,若此时读取,会读到不一致数据,此时解决方案之一是让T事务在转账完成后串行执行。关于并发事务的隔离级别,下文会有更详细的介绍,为了保证数据库的高效性,实际系统中有更多的并发控制方案。
  • 持久性:一旦转账成功执行,系统就必须保证任何系统故障都不会引起与这次转账相关的数据丢失。

事务隔离性级别

为了提高数据库系统的资源利用率和吞吐量以及减少等待时间等,事务处理系统允许多个事务并发的执行。由此承接银行转账例子中的隔离性,为了保证数据库的高并发性,SQL标准允许事务以一种不可串行化的方式执行。存在以下几个隔离性级别:

  • 可串行化(serializable):并发事务以串行方式执行。
  • 可重复读(repeatable read):只允许读取已提交数据,而且在一个事务两次读取一个数据项期间,其他事务不得更新该数据。
    MySQL默认级别是repeatable read
  • 已提交读(read committed):只允许读取已提交数据,但不要求可重复读。比如,在事务两次读取一个数据项期间,另一个事务更新了该数据并提交。
  • 未提交读(read uncommitted):允许读取未提交数据。对于在A事务里面修改了一条记录(例如上面转账的例子,甲的钱转出了,乙未到账),B事务可以读取到修改后的记录(读取到甲账户转出后的金额,此时乙未到账,存在不一致状态)。最低一致性级别。

此处的隔离性级别指的是事务之间的关系,比如read uncommitted是指事务A中的修改即使没提交也会被其他事务读取。

以上所有隔离级别都不允许脏写,即如果一个数据项已经被另外一个尚未提交或中止的事务写入,则不允许对该数据项执行写操作。

与隔离性相关的几个名词:
脏读:指一个事务读取了另一个未提交的事务。
幻读(虚读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。一个事务中包含两次读操作,在第二次读之前,其他事务进行了修改,导致事务1两次读的结果不同。

生产中使用RC隔离级别主要原因如下:

使用RR隔离级别,1.如果存在间隙锁,就容易产生死锁,2.如果条件列未命中索引会引起锁表,其他事物只能读,不能写,影响性能。
还有在RC隔离级别下,半一致性读(semi-consistent)的特性增加了update操作的并发性。

mysql默认RR隔离级别是有历史原因的,mysql主从复制是基于binlog,在5.0以前binlog的格式(binlog_format)只支持statement格式(记录的是修改sql语句),这种格式在RC隔离级别下有BUG,因此mysql将RR隔离级别作为默认的隔离级别。

这个BUG大致是这样:一张表test有字段x并且是主键,x列有一行数据:1;sessionA设置隔离级别为RC,然后开启事务。sessionB设置隔离级别为RC,也开启事务。sessionA执行 delete from test where x >= 3;不提交。sessionB执行 insert into test select 2;sessionB执行commit;提交成功。然后sessionA执行commit;提交成功。此时在主(master)上执行 select * from test;可以查到x列有值为2。但是此时在从(slave)执行select * from test;查到是空的。这样就产生主从不一致的问题!原因其实就是,在master上执行的顺序为先删后插,而此时binlog为statement格式,他记录的顺序是先插后删,从(slave)同步的是binlog,因此主从执行的顺序不一致,就会出现主从不一致。

解决这个问题的方案有两种,1.就是设置隔离级别为RR,在该隔离级别下引入间隙锁,当sessionA执行delete时,会锁住间隙,sessionB执行insert时就会阻塞。2.将binlog的格式binlog_format改为row(记录的是每行实际数据的变更)。此格式是基于行的复制,自然就不会出现sql执行顺序不一样的问题!奈何row格式是在mysql5.1版本开始才引入的。因此由于历史原因,mysql将默认的隔离级别设为RR可重复读,保证主从复制不出问题。

参考链接:
- 《数据库系统概念(本科教学版)》
- MySql事务隔离级别
- 面试问烂的 MySQL 四种隔离级别(原文内容不准确,只看评论区即可)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值