最近在琢磨并发操作控制的问题,在此小小总结一下关于状态保持机制和并发解决方案,如有问题希望大家留言指正。
并发问题分为几类:
1.一个办理页面仅允许一个人进,第二个人再点就提示已经被别人访问,难点在于如何解决用户非法退出时放锁。
2.一个人“签出”后另一个人无法办理。(这是情景1的简化版)
3.多个人同时点“提交”按钮,但是系统应该只允许第一个人成功,后面的人都应该提交失败。
4.一个人更新了表单的内容到数据库,另一个人w3wp进程的用户刷新后依旧读缓存而没读取数据库,缓存不同步。
情景1和2已经在我另一篇帖子里解决了,这里主要讨论情景3和4的解决方案.
对于这种提交操作,大体来讲可以把并发控制的执行流程分为“判锁-上锁-办事-解锁”四个阶段,“锁”是一个表,可以是哈希表,也可以是数据库表。对于提交/保存这样的情景,“办事”阶段仅仅是一个十毫秒级的简单操作,那么放在数据库里显然不妥,不仅严重增大了各方的压力,而且还存在延迟问题,不保证并发安全。
接下来分析一下可能能够应用在并发控制当中的几种状态保持机制:
1.Session是一种会话相关的状态保存方式,每当浏览器去访问w3wp进程请求页面时,都会在本地生成一个写有SessionId的Cookie,浏览器负责将SessionId加到Http请求报文当中,每新开一个浏览器进程SessionId都不同。在w3wp进程内(或是状态服务当中)每个SessionId对应一张KeyValue哈希表,也就是说一个浏览器进程是读不到另一个浏览器进程写的同名Session的,因为它和会话相关,所以显然不能用作并发控制。
2.Application是一种“进程内”哈希表,问题就出在于它是和进程相关的,在多w3wp进程的情况下进程间不能共享Application,也难怪它起了这么个名字,于是它也不能拿来控制并发。
3.Cache数据缓存,广泛应用在减少数据库访问压力上,但是目前没找到接口能把它写到进程外,它和Application一样同样存在多w3wp进程的问题,w3wp进程之间是不共享缓存的。
如果不能用数据库,那么通过上面的分析,唯一要解决的问题就是这个可以跨w3wp进程的哈希表到底放在哪才能共用。我们先来解决多w3wp进程下情景4的问题。
既然是多进程,显然每个进程都有一个id,于是我们可以在Application_Start的时候给每个进程创建一个名为进程id的文件夹,假设A和B同时通过两个进程查看一个表单数据,A提交了表单修改到数据库,数据库写入成功后,可以在B的文件夹下写一个名为Guid的文件,文件里写着表单的主键id,这是对提交过程的修改。对于读取过程,B在读取缓存之前,先去遍历自己的文件夹,如果发现有A写进去的文件,则直接清缓存读库,然后删了文件。
上面的方案通过文件的方式实现了对数据修改的“通知”来使多进程间的缓存“伪同步”,但是硬盘IO毕竟有性能损耗,而且仔细分析一下,由于网络延迟,两个人在“判锁”的时候会同时成功进入后面的逻辑,有很严重的安全隐患。
最后想到的办法就是用Memcached做分布式缓存了。