数据库与锁机制

数据库事务的ACID四大特性:

原子性(Atomicity)

事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。

一致性(Consistency)

在事务开始和完成时,数据都必须保持一致状态。比如用户下单,订单、订单商品、用户扣款数据必须同时成功,要么就全部失败,保证数据从一个一致状态过渡到另一个一致状态。

隔离性(Isolation)

数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。

持久性(Durability)

事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。

 

数据库并发事务存在问题:

脏读: 一个事务可以读取到其他事务未提交的内容。

 

不可重复读: 在一个事务范围内,先后两次读取了同一条记录,却获得不同的结果。这是因为在第一次读取后,有其他事务修改了这条记录并提交到了数据库,再次读取后的记录是被修改后的数据。

 

幻读: 在一个事务范围内,先后两次读取同一个范围列表,却获得不同的结果集。这是因为在两次读取的过程中,有其他事务往这个范围中插入了新的数据。

 

丢失更新:在不加锁的情况下,一个事务内先读取数据,做业务处理之后再更新该记录。在多线程并发的时候,将会造成丢失更新的问题。这是因为一个事务读取了数据,在做业务处理的过程中,有其他事务更新了数据并提交到了数据库,当前事务再更新的时候,就会把之前的更新覆盖掉,导致丢失更新的问题。打个比方,小郭去A窗口买2张高铁票,售票员先查询余票,发现还有10张,就给小明办理买票手续。此时小明在B窗口也买了1张同一班的高铁票并取票离开了,B窗口的售票员将余票更新成了9张。A窗口售票员给小郭办好了出票手续,将之前查询出来的10张高铁票减去两张,并更新数据库中的余票数量为8张。结果就是明明卖了三张高铁票,余票却只减少了两张!

 

隔离级别: 读未提交(read uncommitted)

一个事务可以读取到其他事务未提交的内容。

该级别并发度最高,但完全不能避免脏读、不可重复读、幻读

读已提交(read committed)

一个事务可以读取其他事务已提交的内容。

避免了脏读,但不能避免不可重复读和幻读

该级别为多数数据库的默认隔离级别,如: oracle

可重复读(repeatable read)

一个事务中反复读取同一条记录得到是完全相同的结果

避免了脏读、不可重复读,正常情况下不能避免幻读(mysql除外)

mysql innoDB的默认隔离级别为可重复读,可以避免幻读

串行化(serializable)

Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率非常低下,消耗数据库性能,一般不使用。

 

数据库锁

表级锁:每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;

行级锁:每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;

页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

  mysql锁详解:看这里

 

 

下面以mysql innodb为例探究数据库事务与所机制

         下表作为测试,order_num为主键,item_id为索引

 

 

设置mysql 默认可重复读隔离级别:

set session transaction isolation level repeatable read;

start transaction;

 

测试1(可重复读):

事务A

 

事务B

 

select * from order_test;

 

 

 

 

 

update order_test set buy_num=10;

 

select * from order_test;

 

 

 

 

 

commit;

 

select * from order_test;

 

 

 

事务A三次查询结果完全一致,事务B的数据更新、提交后,A查询出来的还是之前的数据,解决了读未提交和不可重复读的问题。

 

 

测试2(不完全可靠的可重复读):

事务A

 

事务B

 

select buy_num from order_test where order_num=105;   返回橙子购买数量为15

 

 

 

 

 

update order_test set buy_num=10 where order_num=105; commit;

更新橙子购买数量为10,并提交

 

Update order_test set buy_num=buy_num+1 where order_num=105;

橙子的购买数量加1

 

 

 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzxxff.html