什么是事务?()

1:什么是事务?

事务(Transaction),一般是指要做的或所做的事情。
在计算机术语中:是指访问并可能更新数据库中各种数据项的一个程序执行单元 (unit)。事务通常由高级数据库操纵语言或编程语言(如SQL、C++、Java)书写的 用户程序的执行所引起,并用形如BeginTransaction和EndTransaction语句(或 函数调用来界定。
通俗讲:就是把多个要做的操作组合成一个整体,利用事务的特性来保证操作的安全性。如果一个事务做到一半出现任何错误,就会进行回滚操作,来恢复成最初的模样。

2:事务特性?

原子性(atomicity):事务是一个不可分割的工作单位,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态;
一致性(consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。即事务在完成时,必须是所有的数据都保持一致状态;
隔离性(isolation):一个事务的执行不能被其他事务所影响,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离;
持久性(durability):是指一个事务一旦正确完成后,它对数据库中数据的改变就是永久性的。

3:数据库事务

3.1:如何保证原子性?

开启事务:begin;
操作一:update table1 set ***;
操作二:update table2 set ***;
操作(***):其它写操作
提交事务:commit;

保证事务的原子性,就得等begin和commit之间的操作全部成功完成后,才将结果统一提交(commit)给数据库保存,如果途中任意一个操作失败,就撤销(rollback)前面的操作,且操作不会提交数据库保存,这样就保证了同生共死。

3.2:如何保证隔离性?

事务A:针对table1数据a,table3进行读写操作
事务B:针对table1数据a,table2进行读写操作

保证原子性的前提下,为了确保数据操作一致性不被破坏,需要数据的隔离机制,确保同时只能有一个事务A操作table1的数据a,事务A操作完了,另一个事务B才能操作table1的数据a。这需要对数据A加上互斥锁。

【注意】:在事务中更新某条数据获得的互斥锁,只有在事务提交或失败之后才会释放,在此之前,其他事务是只能读,不能写这条数据。

先获得了锁,然后才能修改对应的数据a,事务A完成后释放锁,给下一个要修改数据a的事务B,同一时间,只能有一个事务持有数据a的互斥锁,没有获取到锁的事务,需要等待锁释放

3.3.1:隔离性的四个级别

1.读未提交 read uncommitted:【会产生的问题:脏读、不可重复读、幻读】
2.读已提交 read committed(Oracle、PostgreSQL、SQL Server默认):【会产生的问题:不可重复读、幻读】
3.可重复读 repeatable read(MySQL默认):【会产生的问题:幻读】
4.串行化 serializable(SQLite默认):【会产生的问题:可以解决所有问题】

【注意】:隔离级别从小到大,安全性越来越高,但是效率越来越低。但是一般情况下不会修改数据库默认的隔离级别,只有在极特殊情况下才会做出修改已解决一些特殊问题。

3.3.2:并发事务导致的问题?

第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖

例:

脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据

例:

幻读也叫虚读:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。幻读是事务非独立执行时发生的一种现象

例:例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。

不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任

例:例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发生了不可重复读。

第二类丢失更新:是不可重复读的特殊情况。如果两个事物都读取同一行,然后两个都进行写操作,并提交,第一个事物所做的改变就会丢失

例:

3.4:如何保证持久性?

如果在事务提交后,事务的数据还没有真正落到磁盘上,此时数据库奔溃了,事务对应的数据会不会丢?
事务会保证数据不会丢,当数据库因不可抗拒的原因奔溃后重启,它会保证:

成功提交的事务,数据会保存到磁盘
未提交的事务,相应的数据会回滚

3.4.1:事务日志

数据库通过事务日志来达到持久性数据不会丢这个目标。
事务的每一个操作(增/删/改)产生一条日志,内容组成大概如下:

LSN:一个按时间顺序分配的唯一日志序列号,靠后的操作的LSN比靠前的大。
TransID:产生操作的事务ID。
PageID:被修改的数据在磁盘上的位置,数据以页为单位存储。
PrevLSN:同一个事务产生的上一条日志记录的指针。
UNDO:取消本次操作的方法,按照此方法回滚。
REDO:重复本次操作的方法,如有必要,重复此方法保证操作成功。

磁盘上每个页(保存数据的,不是保存日志的)都记录着最后一个修改该数据操作的LSN。数据库会通过解析事务日志,将修改真正落到磁盘上(写盘),随后清理事务日志(正常情况下):

在保证数据安全及安全的前提下,避免写盘导致数据丢失(数据库崩盘)或写盘产生大量IO影响性能的办法:
1.将数据的变更以事务日志的方式,按照时间先后追加到日志缓冲区,由特定算法写入事务日志,这是顺序IO,性能较好
2.通过数据管理器解析事务日志,由特定的算法择机进行写盘

3.4.2:数据库恢复

当因数据库崩盘或其他原因进行数据恢复时,有以下几个过程:

1.解析存在的事务日志,分析哪些事务需要回滚,哪些需要写盘(还没来得及写盘,数据库就崩溃了)
2.Redo,进行写盘。检测对应数据所在数据页的LSN,如果数据页的LSN>=事务操作的LSN,说明已经写过盘,不然进行写盘操作
3.Undo, 按照LSN倒序进行回滚
————————

1:什么是事务?

事务(Transaction),一般是指要做的或所做的事情。
在计算机术语中:是指访问并可能更新数据库中各种数据项的一个程序执行单元 (unit)。事务通常由高级数据库操纵语言或编程语言(如SQL、C++、Java)书写的 用户程序的执行所引起,并用形如BeginTransaction和EndTransaction语句(或 函数调用来界定。
通俗讲:就是把多个要做的操作组合成一个整体,利用事务的特性来保证操作的安全性。如果一个事务做到一半出现任何错误,就会进行回滚操作,来恢复成最初的模样。

2:事务特性?

原子性(atomicity):事务是一个不可分割的工作单位,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态;
一致性(consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。即事务在完成时,必须是所有的数据都保持一致状态;
隔离性(isolation):一个事务的执行不能被其他事务所影响,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离;
持久性(durability):是指一个事务一旦正确完成后,它对数据库中数据的改变就是永久性的。

3:数据库事务

3.1:如何保证原子性?

开启事务:begin;
操作一:update table1 set ***;
操作二:update table2 set ***;
操作(***):其它写操作
提交事务:commit;

保证事务的原子性,就得等begin和commit之间的操作全部成功完成后,才将结果统一提交(commit)给数据库保存,如果途中任意一个操作失败,就撤销(rollback)前面的操作,且操作不会提交数据库保存,这样就保证了同生共死。

3.2:如何保证隔离性?

事务A:针对table1数据a,table3进行读写操作
事务B:针对table1数据a,table2进行读写操作

保证原子性的前提下,为了确保数据操作一致性不被破坏,需要数据的隔离机制,确保同时只能有一个事务A操作table1的数据a,事务A操作完了,另一个事务B才能操作table1的数据a。这需要对数据A加上互斥锁。

【注意】:在事务中更新某条数据获得的互斥锁,只有在事务提交或失败之后才会释放,在此之前,其他事务是只能读,不能写这条数据。

先获得了锁,然后才能修改对应的数据a,事务A完成后释放锁,给下一个要修改数据a的事务B,同一时间,只能有一个事务持有数据a的互斥锁,没有获取到锁的事务,需要等待锁释放

3.3.1:隔离性的四个级别

1.读未提交 read uncommitted:【会产生的问题:脏读、不可重复读、幻读】
2.读已提交 read committed(Oracle、PostgreSQL、SQL Server默认):【会产生的问题:不可重复读、幻读】
3.可重复读 repeatable read(MySQL默认):【会产生的问题:幻读】
4.串行化 serializable(SQLite默认):【会产生的问题:可以解决所有问题】

【注意】:隔离级别从小到大,安全性越来越高,但是效率越来越低。但是一般情况下不会修改数据库默认的隔离级别,只有在极特殊情况下才会做出修改已解决一些特殊问题。

3.3.2:并发事务导致的问题?

第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖

例:

脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据

例:

幻读也叫虚读:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。幻读是事务非独立执行时发生的一种现象

例:例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。

不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任

例:例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发生了不可重复读。

第二类丢失更新:是不可重复读的特殊情况。如果两个事物都读取同一行,然后两个都进行写操作,并提交,第一个事物所做的改变就会丢失

例:

3.4:如何保证持久性?

如果在事务提交后,事务的数据还没有真正落到磁盘上,此时数据库奔溃了,事务对应的数据会不会丢?
事务会保证数据不会丢,当数据库因不可抗拒的原因奔溃后重启,它会保证:

成功提交的事务,数据会保存到磁盘
未提交的事务,相应的数据会回滚

3.4.1:事务日志

数据库通过事务日志来达到持久性数据不会丢这个目标。
事务的每一个操作(增/删/改)产生一条日志,内容组成大概如下:

LSN:一个按时间顺序分配的唯一日志序列号,靠后的操作的LSN比靠前的大。
TransID:产生操作的事务ID。
PageID:被修改的数据在磁盘上的位置,数据以页为单位存储。
PrevLSN:同一个事务产生的上一条日志记录的指针。
UNDO:取消本次操作的方法,按照此方法回滚。
REDO:重复本次操作的方法,如有必要,重复此方法保证操作成功。

磁盘上每个页(保存数据的,不是保存日志的)都记录着最后一个修改该数据操作的LSN。数据库会通过解析事务日志,将修改真正落到磁盘上(写盘),随后清理事务日志(正常情况下):

在保证数据安全及安全的前提下,避免写盘导致数据丢失(数据库崩盘)或写盘产生大量IO影响性能的办法:
1.将数据的变更以事务日志的方式,按照时间先后追加到日志缓冲区,由特定算法写入事务日志,这是顺序IO,性能较好
2.通过数据管理器解析事务日志,由特定的算法择机进行写盘

3.4.2:数据库恢复

当因数据库崩盘或其他原因进行数据恢复时,有以下几个过程:

1.解析存在的事务日志,分析哪些事务需要回滚,哪些需要写盘(还没来得及写盘,数据库就崩溃了)
2.Redo,进行写盘。检测对应数据所在数据页的LSN,如果数据页的LSN>=事务操作的LSN,说明已经写过盘,不然进行写盘操作
3.Undo, 按照LSN倒序进行回滚