其它-关于系统并发的思考

系统并发思考点

Posted by Kang on September 16, 2019

  系统的并发思考从大的方面来说其实分为两部分:资源的利用率、并发点的处理

资源的利用率

  其实从全局上来看,很多场景下的并发是对硬件资源特别是数据库资源的竞争。在数据库事务操作中,我们通常通过两种方式来使用数据库:

  1. 悲观锁
    对于悲观锁,通过行级锁的方式,来控制多个事务的并发。这种方式下有一个很明显的缺点:所有并发的事务,都将在数据库操作时竞争锁,若竞争失败,则会排队等待。很明显,这种情况下大量的数据库链接等资源都被等待线程持有,并且处于未使用状态。
  2. 乐观锁
    对于乐观锁,通过增加版本等方式,提高了资源的利用率,但是也有一个很明显的缺点:同一时刻竞争的资源只有一个能操作成功。这样对于竞争失败的事务,则必须回滚处理。很明显,这种情况下大量虽然资源利用率高,但是在并发竞争激烈的场景下,有效利用率较低,都做了无用功,还不如悲观锁。

有效利用率的思考

  通过上面的可以看出,减少资源的持有等待和提高资源的利用率是优化的主要方向与最终的目标。

  1. 减少资源的持有等待
    宁愿在不持有资源的情况下等待也要尽量避免在竞争资源处的等待。
  2. 提高资源的利用率
    宁愿不做功也不要做无用功,特别是无用功会随着并发量升高而成线性或者成倍增加的情况下。

解决资源竞争

  将资源竞争点前移,通过使用消息、队列等将并发的竞争资源拉平为非并发访问资源。

  1. MQ消息
    对于处理顺序要求不高或者无要求的场景下,通过MQ的消息队列化,能很好的将并发的竞争资源拉平为非并发访问资源。
  2. Redis的List队列 对于有顺序的访问,一种简单的处理方式:Redis+定时器处理。由于Redis天然的单线程高并发特性,客户端的并发任务将天然的在Redis中被处理为线性的,这样通过定时器定时的拉取Redis中队列的消息,也能将并发处理为非并发。
  3. 部分流程前置化(业务三段式)
    在例如购票的场景中,可以使用业务三段式,将最核心也最容易失败的部分(锁票/锁座位)提前处理-preAction,将其他的业务操作(落库存、消费积分)作为核心,这样在很大程度上较少失败概率,当核心业务中存在多步时若存在失败,则再进行回滚或者人工介入(核心步骤时效性要求不高)– 最终一致性。
  4. 快慢分离 其实和(3)差不多,将满业务剥离出来,通过缓存等等技术手段进行加速。
  5. 拆分竞争资源 比如将库存分为N段,在Redis中使用N个分布式锁,每个分布式锁对应一个库存段。– –>类ConcurrentHashMap.

需要注意的是,前面提到的前两个解决方案都是最终一致性场景。在事务场景中,若需要消费端不能一定成功或者需要感知消费结果,则不适合使用消息,而应该使用TCC等直接调用消费方。 若消费方消费失败,则需要自动补偿成功或者人工补偿处理。