(6)高并发设计
1.如果系统并发增涨100倍,怎样保障可用性?
- 集群化部署
引入负载均衡层比如Nginx,将请求均匀打到应用层。采用集群化部署多个应用实例,确保可以动态的扩容,扛住初步的并发压力。
- 数据库分片
单机数据库的并发能力很弱,有必要把一个库拆分为多个库,部署在多个数据库服务上。如果是读多些少的场景,可以进行读写分离,比如1主2从,主库写,从库读。
- NoSQL数据库
如果MySQL也无法支撑,可以尝试把热门数据放入分布式NoSQL数据库,专门承载当日数据的高并发的读写。每过一段时间做数据归档,把NoSQL里不再频繁使用的冷数据迁移到MySQL里去。
- 缓存集群
不要盲目进行数据库扩容,数据库服务器成本昂贵。针对写少读多的请求,引入缓存集群Redis,能够极大的缓解数据库的读压力。
- 消息中间件
采用消息中间件集群,将异步化的请求写入MQ。消息的作用是削峰填谷,消费消息后才把数据落库,大大缓解数据库的写入压力。
- 数据检索
应对海量数据的检索,可以把索引构建在Elasticsearch里,从NoSQL+MySQL的异构存储来提取明细数据即可。
2.如何解决高并发减库存问题?
关系型数据库如MySQL的单机并发能力很弱,高并发下表字段的加减操作,可能出现幻读。电商的秒杀活动典型的高并发减库存场景,这类问题有三种优化性能的思路:
- 异步处理减库存,而不是同步。
- 在内存中操作减库存。
- 分布式处理,分摊压力。
常见的架构方案是Redis + MQ + MySQL,操作步骤如下:
- 采用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,表示扣减库存成功,这是可以生成订单了。
- 调用接口发送创建订单消息,采用MQ的目的是对下单请求削峰填谷,也将同步下单变为了异步,前端引导用户到等待处理订单的页面。
- MySQL:创建订单前,保底检查一次剩余库存量是否大于1;创建订单完成,库存总量减1。
参考(摘抄的文字版权属于原作者)
https://blog.csdn.net/u010006156/article/details/124831431
https://help.aliyun.com/document_detail/63920.html