数据库中事务基本原理和概念(单机)

前置阅读

https://blog.csdn.net/define_us/article/details/79738918

事务

我们很熟悉,事务必须符合原子性,一致性,隔离性,持久性。简称ACID。其中隔离性最复杂,有四个隔离级别

  • 未提交读 事务中的修改,即使没有提交,也对其他事务可见
  • 提交读(不可重复读) 事务在执行过程中,可以看到其他事务提交的结果。也就是说,连续读取两次,读取的结果可能发生变化
  • 可重复读。解决了上述问题。但是还是有幻读的问题。这是因为只使用了行级锁(如果使用范围锁或者表锁就不会出现这个问题)。在两次查询统一范围内的数据时,会发现数据变多了。Innodb通过MVCC机制解决了幻读的问题。这个级别是Mysql的默认事务隔离级别
  • 串行化。没有任何并发问题。

数据库中的锁

按照作用分:

  • 共享锁 select语句会加入共享锁,数据库机制规定共享锁和排他锁不能同时添加到一个资源上。所以,一句select对资源加了共享锁,另一句select也可以对同一资源加共享锁,但是,一句update则必须等待,因为,他无法把排他锁加到一个已经上了共享锁的资源上。
  • 排他锁 其它任何资源,既不能读,也不能更改
  • 意向锁
  • 计划锁
  • 更新锁 为了解决共享锁和排他锁所带来的死锁问题。假设事物1对资源1加了共享锁,事物2对同一资源也加了共享锁。如果他们随后都想对该资源加入排他锁,就会陷入死锁。所以,事物1一开始应该对资源加入更新锁,表示我已经获取了取得该资源的排他锁资格,但是目前只对该资源加上了共享锁,你们可以读(加上共享锁),但是不要试图加上拍他锁。此时,事物2在试图对该资源加拍他锁后就会等待。

按照粒度分

  • 行级锁
  • 页级锁
  • 表级锁

锁机制

对应事务的隔离隔离界别,需要采用数据库的锁机制。

  • 1、一级封锁协议 (对应 read uncommited)   
    一级封锁协议是:事务在对需要修改的数据上面(就是在发生修改的瞬间) 对其加共享锁(防止其它事物同时更改,但是,此时另一个事物如果要读取,就会导致“脏读”),直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。一级封锁协议不能避免 丢失更新,脏读,不可重复读,幻读!

  • 2、二级封锁协议 (对应read commited) 
    二级封锁协议是:1)事务在对需要更新的数据 上(就是发生更新的瞬间) 加 排他锁 (直到事务结束) , 防止其他事务读取未提交的数据,这样,也就避免了 “脏读” 的情况。2)事务 对当前被读取的数据 上面加共享锁(当读到时加上共享锁),一旦读完该行,立即 释放该 该行的共享锁 - 从数据库的底层实现更深入的来理解,既是,数据库会对游标当前的数据上加共享锁 , 但是当游标离开当前行的时候,立即释放该行的共享锁。 
    二级封锁协议除防止了“脏读”数据,但是不能避免丢失更新,不可重复读,幻读 。

  • 3、三级封锁协议 (对应reapetable read)
    三级封锁协议是:二级封锁协议加上事务(在读取数据的瞬间)必须先对其加共享锁 , 但是直到事务结束才释放。这样保证了可重复读(既是其他的事务职能读取该数据,但是不能更新该数据)。但是,这样,不能避免幻读,也不能避免丢失更新。为了防止丢失更新,需要程序员手动对读取的数据并要在之后进行更新的数据加上排他锁。

  • 4、最强封锁协议(对应Serialization)
    四级封锁协议是对三级封锁协议的增强,其实现机制也最为简单,直接对 事务中所读取或者更改的数据所在的表加表锁,也就是说,其他事务不能 读写 该表中的任何数据。这样所有的 脏读,不可重复读,幻读 ,都得以避免!

redo undo日志在事务中的作用

如果让我自己用JAVA编一个事务数据库,我们可能会按照上述思路。但计算机科学家毕竟不能和程序员一个档次。在数据库的世界里,数据从来都不重要,日志才是最重要的,有了日志就有了一切。
REDO UNDO机制简单介绍如下。
https://blog.csdn.net/define_us/article/details/84139912
一个标准的事务执行流程如下

  • 写undo日志到log buffer;
  • 执行事务,并写redo日志到log buffer;
  • 如果innodb_flush_log_at_trx_commit=1,则将redo日志写到log file,并刷新落盘。(提交事务。)

MVCC机制

上面只是解释了ACID的持久性和原子性的实现。另一方面,我们还需要隔离性和一致性。
多版本并发控制。简单而讲,同一份数据临时保留多版本的方式,实际上也就是行锁的一种变种。这种做法牺牲的是存储空间,为每一行额外保存一个版本列。MVCC只适用于repeatable read和read commit两个隔离级别。
每一个数据行都有自己的一个版本标志。数据库保证每个事务的ID都是递增的。每一个数据行的版本标志就是修改它的事务ID。同一时刻内一行数据会有多个版本。
MVCC 数据库需要更一个一条数据记录的时候,它不会直接用新数据覆盖旧数据,而是将旧数据标记为过时(obsolete)并在别处增加新版本的数据。这样就会有存储多个版本的数据,但是只有一个是最新的。这种方式允许读者读取在他读之前已经存在的数据。

  • read uncommited 读数据时永远读最新版本的数据

  • read commited 所有事务公用一个read-view,每次commit后都会为当前正在进行的事务更新该read-view

  • reapetable 所有事务都持有一个read-view,read 每次commit时不会为当前正在进行的事务更新read-view。相应的增加了很大的维护成本。设想一下,如果你有一个程序在执行一个长长的事务试图生成一个报表。那么这个事务执行期间,所有涉及行的版本都必须被存储。如果还有一部分行是热点行被频繁修改,那么数据库甚至可能被迫存储同一行数据的上千个版本。

  • Serialization 对事务的所有行直接加锁。

MVCC的核心优点在于读不加锁,读写不冲突。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值