定义
锁
在计算机世界中,当在同一个时间点,有大于1个进程需要使用到同一个资源,这个时候就需要锁了。这里有两个关键词:一个资源,多个进程。
兼容锁
兼容锁的意思是,虽然资源被锁上了,但是还可以被使用。
排他锁
排他锁的意思是,只要资源被锁上,就只能被一个进程使用。
正文
因为工作中的一些经历,这篇文章作者主要想讲一讲排它锁的锁中状态。
在工作中,对于锁的使用常常都是排他的。比如,用一个文件去记录线上的当前版本,当更新这个文件的时候,写入时并发请求会读到空的情况;又比如,如果用git去搞一个发布工具,当一个人在合并分支的时候,另外人也请求合并分支,会发生什么情况?这个时候,一般人想到的就是用一个排他锁去锁住它。
一旦用到锁,有个问题是必须回答的:锁的中间状态,如何处理其它调用?
一般有下面几种方式:
- 其它调用阻塞,等待锁被释放。这种方式可能造成死锁,把进城堵塞死。
- 其它调用直接返回错误。比如上面多人同时操作git的情况。
但是,问题来了。上面说到的第一个例子,网站的请求,总不能在版本发布的时候直接访问错误吧?
mysql的事务处理是这样子的:
- 对于被锁住的数据,提供一个旧的版本,加上个写的互斥锁
- 如果是一个读的调用,直接读取到旧版本快照
- 如果是一个写的调用,则阻塞主
注:这里其实也牵扯到可用性和一致性的取舍,但这不是本文的主题
按照这种思路,我们来解决下下面几个案例:
例子一、用文件管理线上版本
可能遇到的情况
版本上线,更新这个文件的时候,并发请求会读到空的情况
解决思路
- 是不是点了更新按钮用户一定要马上看到最新页面?【显然不是】
- 那么我们可以也搞一个旧版本的快照,这种情况就是另外一个文件。每次更新的时候,先更新 VERSION,更新完成后再去更新 VERSION.old。每次读取版本,如果从 VERSION 中取到为空,再去 VERSION.old 取。
例子二、使用git管理php线上版本
注:这里php的部署方式是fpm的。由于php的加载模式,所以会碰到git版本变更的中间状态
- 是不是点了更新按钮用户一定要马上看到最新页面?【显然不是】
- 我们同样可以搞一个旧版本的快照。这应该是另外一个文件夹,还需要一个管理两个文件夹切换的方案,可以参考使用文件管理版本的逻辑。
例子三、使用git的工具合并版本
- 这是一个写入操作,也是一个操作量很小的工具
- 一个人在使用这个文件夹,其他人直接返回不能使用就行了。
总结
- 先确认并发的操作是写还是读
- 如果是写,一般而言可以采用返回错误或者阻塞的方式
- 如果是读,看它是否对一致性要求很高,如果不高的话,还可以使用一个旧的快照来增加可用性