Patrick

并发引发的问题

  在并发访问资源的时候,会附带出许多的问题,比如:

  • 脏读:一个事务读取的是另一个事务完成一半没有提交的数据。用户A、B看到的值都是1,用户B将值改为了2,但A读取的值仍然是1。脏读也可以理解为跟不可重复读类似,A事务第一次读取数据为1,B事务将数据改为2,A事务第二次读取数据为2,B事务回滚,这时数据依旧是1,但是事务A读取的结果是2。
  • 数据丢失:一个事务对数据的修改被另一个事务的修改覆盖。用户A将数据由1变成了2,用户B将数据由2变成了1,这是A的更新就丢失了。
  • 不可重复读:一个事务对一个数据进行多次读取的过程中另外一个事务对该数据进行了访问和修改,那么就导致了第一个事务对该数据前后读取的结果不同。
  • 幻读:指当事务不是独立执行发生的一种现象,例如第一个事务对一个表中的数据进行修改,这种修改涉及到表的全部数据行,同时第二个事务也修改了这个表中的数据,这种修改是向表中插入一行新数据。那么,这是第一个事务执行完后就会发现还有没有修改的数据行,显得很诡异。举个栗子:一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。

  以上的问题都会在并发访问资源的时候出现,对于这些问题,我们需要引入并发控制,而这种并发控制常用的方法就是加锁。下面介绍控制并发冲突的两种锁机制,乐观锁和悲观锁。
事务隔离—维基百科

乐观锁

乐观锁假设多用户并发的事务在处理的时候是不会互相影响的,各事务能够在不产生锁的情况处理好各自影响的那部分数据。在提交更改之前,每个事务都会检查在该事务读取数据之后有没有其他事务修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。

  乐观并发控制机制多数应用于数据争用不大冲突较少的环境中,在这种环境中,偶尔回滚事务的成本会低于读取数据时锁定数据的成本,因此可以获得比其他并发控制方法更高的吞吐量。
  乐观锁的应用有两种方法,一种是版本号自增,另一种是时间戳的方式。

悲观锁

悲观锁假设多用户并发的事务在处理的时候是会互相影响的,那么悲观锁可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作对某行数据加了锁,那么只有在这个事务释放锁后其他事务才能够执行与该锁冲突的操作。

  悲观锁并发控制机制主要用于数据争用激烈的环境,以及发生并发冲突时,使用锁保护数据的成本要低于回滚事务的成本循环中。
  缺点:悲观锁采用的是数据库自身的锁机制,所以在效率方面因为加锁让数据库产生了额外的开销导致效率下降,还有可能增加死锁的机会。

高并发实例

Q1:订票系统,某车次只有一张火车票,假定有1w个人同时打开12306网站来订票,如何解决并发问题?
Q2: 假设系统中图片存储在TFS(Taobao File System)中,接口提供缩略图服务,首先在缓存中查找是否有缩略图,如果没有,则从TFS加载原图片,然后请求缩略图服务,缩略图计算完成后,设置回缓存服务中。遇到的问题:当一张图片分享给100w个人以后,同一时间有1w个并发请求,由于缩略图计算耗时较长(假设1s), 在这1s内,每个请求查询缓存都没有找到然后申请计算缩略图,导致重复的缩略图计算量和资源消耗。
Q3: 单点峰值流量,在并发系统中,除了请求整体的并发量高,还常见单一热点资源的并发请求量很高。例如,1万个人每人分享了一张图片,其中9999张图片的缩略图请求在10 QPS以内,剩下的一张图片为新闻热点图片,峰值请求在10万QPS左右, 系统会遇到的容量问题包括:1)接口前端机容量不够;2)缓存资源单实例遇到瓶颈。
高并发系统中的常见问题