前言
本节并发讲解多用户同时访问同一个数据时,引起的数据不一致性,数据失效性,即我们开发中的事务处理以及版本控制。
并发问题
- 更新丢失(lost updates):当两个用户同时操作一条数据时,A读取了数据进行操作,在A提交数据之前,B进行了数据的更改,如果A此时提交成功,则B修改的数据被丢失掉了。
- 不一致性(inconsistent read):当用户A在访问多个数据时,由于时间较长,在此期间,另一个用户B删除了其中的数据,此时A取到的数据就不是一致的。
执行语境
执行语境,从外界交互的角度看,有两个重要的执行语境:请求和会话。请求即调用,请求添加一个用户等等。会话是客户端和服务器端一次长时间的交互,可以是单独的请求,也可以是多次请求组成的,如登陆到添加用户。(session 保存会话状态)
隔离与不变性
并发的解决方案:隔离(isolation)、不变性(immutability)
- 隔离:对修改隔离,同一时刻只能一个用户修改,在用户对修改未提交或者放弃修改之前,其他用户都不能进行修改,修改独占,当然在web环境中实现难度较大,用户可以轻易的获取独占,但是放弃是难于捕获的,所以现在有行记录版本控制,非最新版本不能保存修改。
- 不变性:分离数据中不变的地方。
乐观并发控制和悲观并发控制
- 乐观锁:两个人都能得到一个文件的拷贝,都可以自由的编辑,先提交的先保存,后提交的有冲突则不能保存,我们的svn都采用这种控制。(冲突监测)
- 悲观锁:当A得到一个文件时,其他人不能修改。(冲突避免)
避免不一致读:
在读数据时添加读锁(read lock,共享锁),写数据添加写锁(write lock,排它锁),读锁发生时,可多人同时加锁,但是不能加写锁。另外一种情况是,加了写锁,则都不能加上读锁。
死锁
当一个加上写锁之后,另外也要加上写锁,则只能等待到前一个完成之后,才能进行。死锁出现的原因是人们在已经得到锁的状态下,还希望得到更多的锁,所以防止死锁的发生就是强制人们一次性读取所有可能需要的锁,同时加上超时控制和检测机制处理。
事务
大家对事务都足够了解了,操作要么成功要么失败。事务有四个特性:
ACID
- 原子(atomicity)
- 一致性(consistency)
- 隔离性(isolation)
- 持久性(durability)
事务资源
用事务资源来表示可以进行事务处理的任何事物—即使用事务来控制并发过程。(数据库控制事务,过程开启事务,结束事务)
减少事务隔离以提高灵活性
即事务的四个级别的选择:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交 | 是 | 是 | 是 |
读已提交 | 否 | 是 | 是 |
可重复读 | 否 | 否 | 是 |
可串行化 | 否 | 否 | 否 |
业务事务和系统事务
系统事务即数据库本身要实现的事务处理,如写成功,业务事务为程序所定义的事务。
离线并发控制的模式
采用乐观离线锁,局限是:只能在提交数据时才发现业务事务将要失败,代价变大,输入了一个小时的东西,结果失败了。
应用服务器并发
我们常用的web服务器都采用每一个会话一个进程,采用线程安全。
并发是计算机系统必须面对的问题,不管在哪个方面,即使在现实社会也不例外,我们的大脑一次处理一个事情成功率高很多与一次处理多个事情,大家做饭的话会有很好的体会。