跳至主要內容

(6)高并发设计


1.如果系统并发增涨100倍,怎样保障可用性?

  • 集群化部署

引入负载均衡层比如Nginx,将请求均匀打到应用层。采用集群化部署多个应用实例,确保可以动态的扩容,扛住初步的并发压力。

  • 数据库分片

单机数据库的并发能力很弱,有必要把一个库拆分为多个库,部署在多个数据库服务上。如果是读多些少的场景,可以进行读写分离,比如1主2从,主库写,从库读。

  • NoSQL数据库

如果MySQL也无法支撑,可以尝试把热门数据放入分布式NoSQL数据库,专门承载当日数据的高并发的读写。每过一段时间做数据归档,把NoSQL里不再频繁使用的冷数据迁移到MySQL里去。

  • 缓存集群

不要盲目进行数据库扩容,数据库服务器成本昂贵。针对写少读多的请求,引入缓存集群Redis,能够极大的缓解数据库的读压力。

  • 消息中间件

采用消息中间件集群,将异步化的请求写入MQ。消息的作用是削峰填谷,消费消息后才把数据落库,大大缓解数据库的写入压力。

  • 数据检索

应对海量数据的检索,可以把索引构建在Elasticsearch里,从NoSQL+MySQL的异构存储来提取明细数据即可。

2.如何解决高并发减库存问题?

关系型数据库如MySQL的单机并发能力很弱,高并发下表字段的加减操作,可能出现幻读。电商的秒杀活动典型的高并发减库存场景,这类问题有三种优化性能的思路:

  1. 异步处理减库存,而不是同步。
  2. 在内存中操作减库存。
  3. 分布式处理,分摊压力。

常见的架构方案是Redis + MQ + MySQL,操作步骤如下:

  1. 采用Redis搭建分布式缓存,在内存中操作库存值。通过数据控制模块提前将库存值放入Redis,将每个秒杀商品在Redis中用一个hash结构表示。
"goodsId" : {
    "Total": 100
    "Booked": 0
}

goodsId表示商品ID,Total表示该商品的库存数量,Booked表示该商品已被订购的数量。
扣量时,服务器通过请求Redis获取下单资格,通过以下lua脚本实现,由于Redis是单线程模型,lua可以保证多个命令的原子性。

local vals = redis.call("HMGET", KEYS[1], "Total", "Booked");
local total = tonumber(vals[1])
local blocked = tonumber(vals[2])
if not total or not blocked then
    return 0       
end   
if blocked + 1 <= total then
    redis.call("HINCRBY", KEYS[1], "Booked", 1)                                   
    return 1;   
end                
return 0

如果HGET goodsId Booked执行结果为1,表示扣减库存成功,这是可以生成订单了。

  1. 调用接口发送创建订单消息,采用MQ的目的是对下单请求削峰填谷,也将同步下单变为了异步,前端引导用户到等待处理订单的页面。
  2. MySQL:创建订单前,保底检查一次剩余库存量是否大于1;创建订单完成,库存总量减1。

参考(摘抄的文字版权属于原作者)

https://blog.csdn.net/u010006156/article/details/124831431open in new window
https://help.aliyun.com/document_detail/63920.htmlopen in new window

上次编辑于: