离线并发:多个数据库事务中支持多线程的各种应用服务器
1. 并发问题:
1)丢失更新(同时编辑文件,相继保存,最终丢失先保存者更新的内容)
2)不一致性(读取期间,数据有更新)
2. 执行语境:
1)从与外界交互角度看的2个语境:
请求:对应于软件工作的外部环境发出的单个调用,处理请求的软件会决定是否返回一个应答(过程大部分是在服务器端进行,而客户端则假设为在等待应答)
会话:客户端和服务器端之间一次长时间的交互
2)操作系统的2个语境:
进程:重量级的执行语境,将其正在处理的内部数据域外部隔离开(有效隔离,减少冲突,但需要消耗很多资源)
线程:轻量级的执行语境,一个单独的进程里可以存在多个线程(可以充分利用资源,但易导致并发问题)
3. 隔离与不变性:
企业应用2个解决方案:
1)隔离:
方案1:划分数据,使得每一个片数据都只能被一个执行单元访问(操作系统为每个进程单独分配一片内存,并且只有这个进程可以对这片内存进行读写操作)
方案2:文件锁,一个人打开文件,其他人再无法打开或者只打开该文件只读拷贝并且不能修改
(好的并发设计应该是:找到各种创建隔离区的办法,并且保证在每个隔离区里能够完成尽可能多的任务)
2)不变性:
方案1:识别哪些是不变的数据(只有在共享数据可以修改的情况下,并发问题才会出现),不用考虑这些数据的并发问题二广泛地共享它们
方案2:把那些只读取数据的程序分开,让他们只是用拷贝的数据源
4. 乐观并发控制盒悲观并发控制:
1)2种锁:
乐观锁:
关于冲突检测的(仅当提交更新时才检查冲突)
优点:并发性高,限制较少,使用起来比较自由
缺点:业务数据比较复杂,难以自动合并,并且使用者难以发现差别时,只能丢弃原有数据,从头开始更新
悲观锁:
关于冲突避免的(只要有人已是用当前数据或者文件,拒绝其他人使用当前数据或文件)
优点:减少并发程度
缺点:用户体验差
两种策略选择标准:冲突的频率与严重性(冲突少或者冲突后果不严重时,选择乐观锁;否则选择悲观锁)
2)避免不一致性:
a. 检测不一致读:
悲观策略:通过读加锁(read lock,共享锁)和写加锁(write lock,排他锁),可以一次多个人对同一份数据加只读锁,但只要有人得到一个只读锁,其他人就都无法得到写锁;一旦有人得到一个写锁,其他人都不能得到两种所中任意一种
乐观策略:基于数据的某种版本标识(时间戳或者顺序计数器),核对将要更新数据的版本标识和共享数据的版本标识,如果两者一样,系统允许更新数据并更新共享数据的版本标识
b. 时序读:每次读取数据时都是用某种时间戳或其他不变的标签作为约束条件,数据库根据时间或者标签返回数据(源代码控软件可以用,但数据库比较困难并且代价昂贵)
3)死锁:
处理死锁的方法:
a. 用软件来检测死锁的发生,选择一个牺牲者,放弃他的工作和他所加的锁;
b. 给每个锁都加上时间限制,一旦达到时间限制,所加的锁就会失效,工作就会丢失;
防止死锁的方法:
强制人们在开始工作时就获得所有可能需要的锁,在此之后就不再允许得到更多的锁;
可以硬性规定每个人获得锁的顺序(如按姓名字母顺序)
5. 事务:
1)ACID:原子性、一致性、隔离性、持久性
2)事务资源:
定义:可以进行事务处理的任何事物
事务控制方法:
保证事务尽可能短(尽可能不让事务跨越多个请求,跨越多个请求的事务称为长事务);
尽可能晚打开事务
3)减少事务隔离以提高灵活性:
SQL的4种隔离级别:可串行化的、可重复读、读已提交、读未提交
不必给所有的事务设置相同的隔离级别,而应该仔细观察每个事务并根据每个事务具体情况来决定如何权衡灵活性与正确性
4)业务事务和系统事务:
系统事务:由关系数据库系统和事务监视器所支持的事务 (一般不用干预)
业务事务:简单办法:在单个系统事务中执行完整的业务事务(产生长系统事务);复杂的,采用离线并发(通过工作单元来保存更新数据)
6. 离线并发控制的模式:
乐观离线锁、悲观离线锁
7. 应用服务器并发:
每会话一线程 VS 每会话一进程:线程节省资源,但创建和进入隔离区很重要