数据库之事务与并发控制

一.事务的特性(ACID)

1.原子性:单个或多个操作为一个整体,要么全执行,要么全不执行(回滚)
2.一致性:事务执行是从一个一致性状态转为另一个一致性状态
3.隔离性:一个事务在提交前对数据的修改对于其它事务不可见
4.持久性:事务一旦提交对数据的改变是永久的

二.并发事务可能导致的问题

1.丢失数据修改 (修改的数据被其它线程事务覆盖)
2.脏读(B事务读取了A事务尚未提交的数据,且A事务回滚数据)
3.不可重复读(一个事务中两次读取的数据内容不一致,重点在update)
4.幻读(一个事务中两次读取的数据量不一致,重点在insert,delete)

三.事务并发访问问题的解决方式

1.锁

1.1 共享锁和排他锁

(1)共享锁(S锁,读锁):本事务只可进行读操作,其它事务加S锁也可读
(2)排他锁(x锁,写锁):本事务可进行读写,不允许其它事务加锁和操作
(3)更新所(u锁):防止通常形式的死锁,两个拥有共享锁的事务要同时更新数据时,需要将共享锁转换为排他锁,所以他们都需要X锁并且要等待对方释放s锁(x锁和其它事务的S锁不兼容),会发生死锁

1.2 临时锁与持续锁

 锁的时效性,指明加锁生效期是当前语句结束还是当前事务结束

1.3表级锁和行级锁

 锁的粒度,指明加锁的对象是当前表还是当前行
 

1.4乐观锁和悲观锁
这两种锁的说法,主要是对“是否真正在数据库层面加锁”进行讨论。
(1)悲观锁:悲观锁假定当前事务操纵数据资源时,肯定还会有其他事务同时访问该数据资源,为了避免当前事务操作受到影响,悲观锁需使用数据库的锁机制实现。java中的synchronized锁就是悲观锁。悲观锁非常影响并发性能,所以谨慎使用
(2)乐观锁假定当前事务操纵数据资源时,不会有其他事务同时访问该数据资源,乐观锁使用由程序逻辑控制的技术来避免可能出现的并发问题。最常用的方式是基于数据版本记录机制实现(一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据)所以不能避免脏读问题

2.设置事务的隔离级别

(1) 读未提交:事务写阻塞其它事务的写不阻塞读(加“持续-X锁”实现)
      ---避免丢失数据修改
      
(2)读已提交:事务写会阻塞其它事务的读和写(写操作加“持续-X”锁,读操作加“临时-S锁”实现)
     ---避免丢失数据修改和脏读
     
(3)可重复读:事务读会阻塞其它事务的写,不会阻塞读,事务写会阻塞其它事务读写(写操作加“持续-X”锁,读操作加“持续-S锁”实现)    
     ---避免丢失数据修改和脏读和不可重复读幻读
     
(4)串行化:事务的最高级别,在每个读的数据行上,加上锁,使之不可能相互冲突,因此,会导致大量的超时现象
    ---解决了所有问题
   

3.三级加锁协议

   (1)一级加锁协议:读数据不加锁,修改数据前加X锁(解决丢失数据修改)
   (2)二级加锁协议:读取时加s锁,修改时加x锁(解决丢失数据修改+脏读)
   (3)三级枷锁协议:全程加x锁和s锁(解决所有问题)
    原文作者:kaka
    原文地址: https://segmentfault.com/a/1190000014675383
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞