数据库事务以及锁 知识点

      为了保证并发时数据的完整性以及正确性, 数据库中引入了事务的概念 , 意为在某一个功能单元 , 要么同时成功 , 要么同时失败 .

1.事务

    1.1  数据库事务有四大特性:

       1.1.1原子性(Atomicity):事务是数据库的逻辑工作单位,它对数据库的修改要么全部执行,要么全部不执行。
       1.1.2 一致性(Consistemcy):事务执行前后,数据库的状态都满足所有的完整性约束。
       1.1.3 隔离性(Isolation):并发执行的事务是隔离的,保证多个事务互不影响。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。通过设置数据库的隔离级别,可以达到不同的隔离效果。

       1.1.4 持久性(Durability):一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。

 1.2 数据库事务有四大隔离级别:

        MySQL InnoDB事务的隔离级别有四级,默认是可重复读REPEATABLE READ) ; oracel 默认级别是  读已提交;

·        未提交读(READUNCOMMITTED)。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)。

·        提交读(READCOMMITTED)。本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(不重复读)。

·        可重复读(REPEATABLEREAD)。在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象(稍后解释)。

·        串行化(SERIALIZABLE)。读操作会隐式获取共享锁,可以保证不同事务间的互斥。

四个级别逐渐增强,每个级别解决一个问题。

        可能产生的问题

.         脏读,最容易理解。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据。

·        不重复读。解决了脏读后,会遇到,同一个事务执行过程中,另外一个事务提交了新数据,因此本事务先后两次读到的数据结果会不一致。

·        幻读。解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会惊奇的发现了这些新数据,貌似之前读到的数据是鬼影一样的幻觉。

隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能

1.3 事务运行的三种模式
    1.3.1 自动提交事务:默认事务管理模式。如果一个语句成功地完成,则提交该语句;如果遇到错误,则回滚该语句。
    1.3.2 显式事务:以BEGIN TRANSACTION显式开始,以COMMIT或ROLLBACK显式结束。

    1.3.3 隐性事务:当连接以此模式进行操作时,sql将在提交或回滚当前事务后自动启动新事务。无须描述事务的开始,只需提交或回滚每个事务。它生成连续的事务链。

1.4 数据库事务与事务日志

        以mysql的innodb的事务为例,通过事务日志实现ACID特性。事务日志遵循WAL(Write-Ahead Logging 预写日志方式)协议。

        以oracel为例: 查看数据库日志文件路径可用   SELECT * FROM V$logfile;  查看近期操作日志可用SELECT * FROM V$sql;    不过查出来的日志直接看看得不是很直观 ,  可用oracel提供的LogMiner来查看分析后的日志.

         数据库日志文件是非常重要的文件 , 如果不小心对数据库进行误删等操作 ,  恢复时日志文件是很好的依据.

2. 锁

    数据库事务底层的实现就是通过锁机制来实现的, 从大的分类上, 我们可以将锁分为乐观锁和悲观锁, 而悲观锁又可延伸出共享锁(读锁)  排他锁(写锁)  再细分一点  可以分为行锁 表锁 页锁

乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

通常实现是这样的:在表中的数据进行操作时(更新),先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。

共享锁【S锁】
又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

排他锁【X锁】
又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。

行锁,由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。

比如SELECT * from city where id = "1"  lock in share mode; 

由于对于city表中,id字段为主键,就也相当于索引。执行加锁时,会将id这个索引为1的记录加上锁,那么这个锁就是行锁。

表锁,和行锁相对应,给这个表加上锁。

mysql innodb事务死锁:

   事务A需要先查询数据1,然后修改数据2;

   事务B需要先查询数据2,然后修改数据1;

此时2个事务并发执行,由于mysql innodb的默认隔离级别是可重复读,读锁、写锁只能在事务结束才会释放。

事务A中需要等待事务B释放数据2的共享锁,才能执行对数据2添加排他锁;事务B需要等待事务A释放数据1的共享锁,才能执行对数据1添加排他锁。这样会就出现事务死锁

这种死锁在oracle中就不会出现,因为oracle的默认隔离级别是读已提交,共享锁在执行完查询之后就会释放,不会导致排他锁无法添加的问题。

如果大家在看博客时发现有说得不严谨的地方 , 欢迎回复交流. 


MySQL InnoDB事务的隔离级别有四级,默认是可重复读REPEATABLE READ)。

·        未提交读(READUNCOMMITTED)。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)。

·        提交读(READCOMMITTED)。本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(不重复读)。

·        可重复读(REPEATABLEREAD)。在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象(稍后解释)。

·        串行化(SERIALIZABLE)。读操作会隐式获取共享锁,可以保证不同事务间的互斥。

四个级别逐渐增强,每个级别解决一个问题。

猜你喜欢

转载自blog.csdn.net/qq_28233015/article/details/80047858
今日推荐