数据库原理二---MySQL事务与锁

数据库事务的四大特性

  • 原子性A
    事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用

  • 一致性C
    执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的

  • 隔离性I
    并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的

  • 持久性D一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响

原子性和持久性定义了事务的边界,行为的开始和结束,一致性和隔离性即是对事务中间状态的管理。

ACID的核心是C,大家都是为得到C而提出的不同纬度的限制和规范
A确定一个功能的完整性,D对状态负责,I作为C的等级系数,不同的I策略会出现不同的C。
隔离性I的设定就是对一致性不同程度的破坏,事实上,如果我们顺序对数据进行读写,ACD是完全可以保证的,但这样效率会非常低下。
选择合适的隔离策略是为了在一致性和性能之间平衡,取得最好的综合表现。

脏读、幻读、不可重复读

  • 脏读(Drity Read)
    某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

  • 不可重复读(Non-repeatable read)
    在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

  • 幻读(Phantom Read)
    在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。
更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。

事务的隔离级别

为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别

  • READ-UNCOMMITTED(读取未提交)
    最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

  • READ-COMMITTED(读取已提交)
    允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。

  • REPEATABLE-READ(可重复读)
    对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

  • SERIALIZABLE(可串行化)
    最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

**Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别 **

注意:当在Java中使用@Transactional注解而没有指定隔离级别时,它实际上会依赖于底层数据库的默认设置。

MySQL的锁机制

MySQL数据锁机制(行锁,页锁,表锁)

MySQL数据锁按照锁的粒度划分可分为:行锁、表锁、页锁

  • 行锁
    行级锁是MySQL中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。
    行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。有可能会出现死锁的情况。
    行级锁按照使用方式分为共享锁和排他锁。

    • 共享锁
      共享锁允许一个事务读数据,不允许修改数据,如果其他事务要对该行加锁,只能加共享锁。即共享锁允许多个线程同时获取一个锁,一个锁可以同时被多个线程拥有。
    • 排他锁
      排他锁是修改数据时加的锁,可以读取和修改数据,一旦一个事务对该行数据加锁,其他事务将不能再对该数据加任务锁。即一个锁在某一时刻只能被一个线程占有,其他线程必须等待锁被释放之后才可能获取到锁。
  • 表锁
    表级锁是MySQL锁中粒度最大的一种锁,表示当前的操作对整张表加锁,资源开销比行锁少,不会出现死锁的情况,但是发生锁冲突的概率很大。
    被大部分MySQL引擎支持,MyISAM和InnoDB都支持表级锁,但是InnoDB默认是行级锁。

  • 页锁
    页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级锁冲突少,但速度慢。
    所以取了折中的页级,一次锁定相邻的一组记录。BDB支持页级锁。

隔离级别与锁的关系

在Read Uncommitted级别下,写事务时加共享锁,读事务不加锁(由于是共享锁,写事务未提交前其他事务仍能读,故会导致脏读)

在Read Committed级别下,写事务时增加排他锁,事务结束才释放,读事务加共享锁,读完立即释放(读事务第一次读取数据结束后释放共享锁,此时其他事务可以更新数据,该事务第二次读取数据时发现与第一次数据值不同,故会导致不可重复读)

在Repeatable Read级别下,读事务和写事务加的锁都持续到事务结束才释放(读写事务只是对其所读的行加了行级锁,此时其他事务虽然不能更改这些行,但是能添加新行,故会导致幻读)

序列化:读写事务加表级锁

MVCC

MVCC(Multiversion concurrency control),多版本并发控制,提供并发访问数据库时,对事务内读取的到的内存做处理,用来避免写操作堵塞读操作的并发问题。

MVCC提供了 时间一致性的 处理思路,在MVCC下读事务时,通常使用一个时间戳或者事务ID来确定访问哪个状态的数据库及哪些版本的数据。读事务跟写事务彼此是隔离开来的,彼此之间不会影响。假设同一份数据,既有读事务访问,又有写事务操作,实际上,写事务会新建一个新的数据版本,而读事务访问的是旧的数据版本,直到写事务提交,读事务才会访问到这个新的数据版本。

MVCC与 数据锁的区别

MVCC主要解决的是读-写冲突,通过保持数据的多个版本来实现非锁定读,从而提高并发性能;而共享锁排他锁则是通过锁定数据来解决写-写冲突和读-写冲突

InnoDB的MVCC实现机制

InnoDB的MVCC实现,是通过保存数据在某个时间点的快照来实现的。一个事务,不管其执行多长时间,其内部看到的数据是一致的。也就是事务在执行的过程中不会相互影响。
InnoDB的MVCC,通过在每行记录后面保存两个隐藏的列来实现:一个保存了行的创建时间,一个保存行的过期时间(删除时间),当然,这里的时间并不是时间戳,而是系统版本号,每开始一个新的事务,系统版本号就会递增。

RC , RR 级别下的 InnoDB MVVC实现的快照读有什么不同

RC级别下,每一次执行Select语句时都会生成一个新的Read View,一次事务可能产生多个Read View,在需要当前读(SELECT ... FOR UPDATE)时,会使用排他锁。
RR级别下,Read View在事务开始时生成,一次事务只产生一个Read View,在修改数据时,使用排他锁。

MVCC在MySQL中只在 Read Committed 和 Repeatable Read两个隔离级别下工作。其他两个隔离级别和MVCC不兼容,Read Uncommitted总是读取最新的记录行,不需要MVCC的支持;Serializable 则会对所有读取的记录行都加锁,单靠MVCC无法完成。

间隙锁

锁定索引记录之间间隙的一种锁,它并不锁定具体的记录本身,而是锁定一个范围,防止其他事务在这个范围内插入新的记录。主要用于解决并发控制中的幻读问题

间隙锁如何解决幻读问题

当事务使用范围条件(如BETWEEN、>、<等)检索数据并请求共享或排他锁时,InnoDB不仅会对符合条件的已有数据记录的索引项加锁,还会对键值在条件范围内但并不存在的记录(即间隙)加锁。这样,其他事务就无法在这个间隙内插入新的记录,从而避免了幻读的发生。

posted @ 2021-10-02 12:49  cos晓风残月  阅读(53)  评论(0编辑  收藏  举报