基础-事务
- 两个指令是冲突的:两个指令是不同事务在相同数据上的操作,且其中至少有一个是write指令
- 冲突可串行化:若一个调度与一个串行调度冲突等价
- 锁:加锁是为了防止不同的线程访问同一资源而造成混乱。
共享锁:可读不可写(许多事物可以同时持有共享锁)
排他锁:可读可写(当其他事务在一个数据项不持有任何锁的情况下,一个事务才能持有该数据项上的排他锁)
- 锁粒度:
表锁:开销小(获得锁,检查锁,释放锁),系统的并行程度低。
行锁:开销大,系统的并行程度高。
- 时间戳:为每一个事务分配一个时间戳。对于数据项,系统维护两个时间戳。数据项的读时间戳记录读取该数据项的事务的最大时间戳。数据项的写时间戳记录写入该数据项当前的事务的时间戳。
- 事务:一组原子性的SQL语句,当数据库能成功执行所有SQL语句的时候,则执行该组查询,有一句SQL因为崩溃或者其它原因无法执行,则所有的语句都不能执行。即:要不全部执行,要不一个都不执行。
- ACID:
原子性(atomicity):一个事务必须被看作是最小的工作单元。事务对数据库的操作,要么全反映,要么全不反映
一致性(consistency):一个事务必须使数据库从一个一致性的状态转换到另一个一致性的状态 ,满足可串行性,可恢复性(不会出现脏读,即提交的事务没有读取回滚的事务)
隔离性(isolation):并行的事务之间是隔离的,一个事务的执行不会被其它的事务所影响
4个隔离级别:
未提交读:允许脏读。(读不加锁,写加行锁)。可以看到未提交的事务新插入的元祖。
已提交读:允许读取已提交的数据。可以看到已提交的事务插入的元祖,而且能看到已提交事务对数据的更新。
可重复读:一个事物多次读取同一数据时,读到的数据跟事务刚开始的时候是一致的。禁止不可重复读和脏读,可能产生幻读获取到之前没有的数据。可以读取到已提交事务的新插入的元祖(在这个事务之前提交),但是不能看到已提交事务的更新(晚于此事务提交)。
串行化:每个事务都是被串行化执行。
持久性(durability):一旦事务提交,那么对数据库所做的修改都永远保存在数据库。
- 死锁:两个或两个以上的事务,因为争夺资源而造成互相等待的现象。
四个必要条件:
互斥使用(资源不能被共享)
拥有并等待:
非抢占:
循环等待:
InooDB处理死锁的办法:将持有最少行级排他锁的事务回滚。
死锁处理方式:
死锁预防(对锁请求进行排序或者检测,如果出现可能死锁 问题,回滚)、死锁检测(等待图)、死锁恢复(选择牺牲者,回滚)。
- 乐观锁与悲观锁:
乐观:所有的操作都进行,但在事务提交的时候进行隔离性和完整性的约束。如果有违反的,则终止事务。
悲观:从一开始,检测每一个操作是否满足隔离性和完整性的约束。如有违反,则终止事务。
- 并发控制技术:
基于时间戳:
事务时间戳,数据时间戳(读、写)
多版本:维护数据项的多个版本,一个事务允许读取旧的版本的数据项,而不是被一个未提交的事务或串行化排在后面的事务写入的新的版本的数据项。快照隔离是实现多版本控制的一个技术。
基于提交提交顺序
基于串行化图测试验证
基于锁
- 两段锁协议:可保证冲突可串行化,通过增长阶段的最后位置,称为封锁点。根据封锁点来进行排序。但不可以保证死锁
增长阶段:可以加锁,不能释放锁。
缩减阶段:可以释放锁,不能加锁。
- mvcc快照读:多版本并发控制,它的目标是在保证数据一致性的前提下,提供一种高并发的访问性能。在MVCC协议中,每个用户连接数据库时看到的都是具有一致状态的镜像,每个事务在提交之前对其它的用户是不可见的。当事务需要更新数据的时候,不是通过覆盖旧的数据,而是生成一个新版本的数据。因此一条数据会有多个版本存储。因此读的时候总是保证当前时刻的数据视图被读到,不管之后是否被修改或删除。
数据项都记录了一个系统版本号,每次修改时系统版本号都会被更新。每创建一个事务,系统版本号递增。在读取事务,系统会给事务一个版本号,事务会读取的版本号<=当前版本号。
两种实现方式:实时保留一个或多个历史版本
在需要时通过undo日志构造出历史版本。(Oracle、MySQL)
已提交读:
MVCC:每次读取的数据都是最新的版本(每一次SQL都会生成视图)。
锁:对于读出的记录添加共享锁,但读完立即释放。
可重复读:
MVCC:在事务开始的时候生成视图,事务中的所有操作都读取同一个视图。
锁:对于读出的记录,添加共享锁直到事务结束。 (数据库中,一般都是行锁加间隙锁 next-key locking(基于索引的并发控制)))
- 日志技术:记录数据库修改的结构
为了保证在数据迁移的过程是安全的,将改变数据状态的操作的对象、数据和过程都以日志的形式记录下来。从而当系统发生故障需要做系统恢复的时候,根据日志进行回放。就可以对应对事务的原子性(重执已提交的事务,还原未提交的事务的旧数据)和持久性(刷出数据到存储,包括日志数据提前刷出,脏数据被保存点checkpoint刷出)
通过事务日志能加快数据库的效率,完成操作之后,将操作行为写到事务日志,然后再将内存中修改的值写到磁盘中。每次事务执行写操作时,必须在数据库修改前建立该次写操作的日志记录将加到日志中。一但日志记录已经存在,就可以根据需要修改输出到数据库中。
更新日志记录的数据:
事务的标识:
数据项的标识:那个数据库中那个模式下的哪个表的哪个数据项被修改,可以被换成物理存储下的哪个物理地址的数据项被修改。
旧值:被修改之前的值
新值:被修改之后的值
事务提交日志:该事务最后的一个日志记录——输出到稳定存储器后,就认为该事务已经提交
如果系统崩溃发生在日志记录输出到稳定存储器之前,事务T将回滚。
包含了redo log 和undo log:
redo log:记录事务标识、数据项标识,修改后的值。
undo log:记录事务标识,数据项标识,修改前的值。
数据库修改:
步骤:
事务在主存中自己私有的部分执行某些计算
事务修改主存的磁盘缓冲区中包含该数据项的数据块
将数据块写到磁盘中
重做和撤销:
如果日志包括<T start>记录,但既不包括<T commit> 也不包括<T abort>记录,则需要对事务进行撤销
如果日志包括<T start>记录,并且包括<T commit>或者<T abort>记录,则需要对事务进行重做
检查点:(1)将当前位于主存的所有日志记录输出到稳定存储器
(2)将所有修改的缓冲块输出到磁盘
(3)将日志记录<check point L> 输出到稳定存储器。其中L是执行检查点时正在活跃的事务的列表
在系统崩溃发生之后,系统检查日志以找到最后一个Check point记录。只需要对L中的事务,以及最后一条checkpoint记录写到日志之后才开始的事务进行undo或redo。
一点检查点完成,就可以删除掉Ti Start之前的所有日志记录了。
恢复算法:
事务回滚:对事务Ti的回滚
从后往前扫描日志,对于发现的Ti的每一个型如<Ti, Xj, V1, V2>的日志记录。
(1)值V1被写到数据项Xj中,(2)往日志中写一个特殊的只读记录<Ti, Xj, V1>, 称作补偿日记记录。
系统崩溃后的恢复:
重做阶段:从最后一个检查点开始正向地扫描日志来重放所有事物的更新。
将要回滚的事务的列表undo-list初始设定为<checkpoint L>日志记录中的L列表
一但遇到形为Ti, Xj, V1, V2>的正常日记记录,或形为<Ti, Xj, V2>的redo-only日记记录,就重做这个操作。
一但发现形为<Ti Start>的日记记录就把Ti加到undo-list中
一但发现形为<Ti, abort> 或<T1, commit>的日志记录,就把Ti从undo-list中去掉
撤销阶段:系统回滚undo-list中所有事物,从尾端开始反向扫描日志来执行回滚。
一但发现undo-list中的事务,进行事务回滚
一但发现形如<Ti, start>日志记录,就往日志中写一个<Ti, abort>日志记录,并把Ti从undo-list中去掉
undo-list为空,则撤销结束
缓冲区管理:
日志记录缓冲:将日志记录写到主存的日志缓冲区,之后再用一次输出操作输出都稳定存储器中
在日志记录<Ti commit>输出到稳定存储器后,事务Ti进入到提交状态;
在日志记录<Ti commit>输出到稳定存储器前,与事务Ti有关的所有日志记录必须已经输出到稳定存储器中
在主存中的数据块输出到数据库前,所有与该数据块中数据有关的日志记录必须已经输出到稳定存储器中。(先写日志(write-ahead logging, WAL)规则)
远程备份系统:
主站点:执行事务处理
远程备份点:有主站点的随时备份
故障检测:
控制权的移交:原站点从原备份点收到redo日志,并将它们应用到本地以赶上更新。
恢复时间:为了恢复时间简短,远程备份站点可以周期性地处理它收到的redo日志,并执行一个检查点,从而日志早期的部分可以删除。热备份(几乎在一瞬间可以接管,远程备份站点不断地处理到达的redo日至记录)
提交时间:
一方保险:事务的提交日志一写入主站点的稳定存储器,事务就提交
两方强保险:主站点和备份站点都写入稳定存储器
两方保险:如果备份站点不活跃,则写入主站点即可
- 索引:是存储系统用于快速找到记录的一种数据结构。

浙公网安备 33010602011771号