那抽奖为例吧
首先是整体设计
抽奖一般不会作为独立的业务出现,而是作为某个系统的某个业务出现, 一般而言系统越简单 性能越好,为了降低业务耦合度可以把抽奖拆分为一个子系统来处理,也方便扩容。
前端:动静分离 , 尽量不要动态获取数据 , 如奖品的基本信息等可以写死 , 静态页面上CDN 。
后端:基于nginx做负载均衡 分布式集群部署 (当然nginx也是可以做集群的)
存储:缓存 + 数据库(数据库读写分离 , 分库 ,分表)
那具体怎么做呢 接下来讲一下细节吧
首先是动静分离和分布式部署就不用再说了,先讲一下缓存 , 一般而言,redis的读取性能大于数据库,而接口缓存在内存中,性能比redis要高,
数据是不是应该缓存以及应该缓存在哪里,缓存多长时间要是业务场景来定,比如说用户的基本信息和奖品的基本信息,对于抽奖系统来说,这些信息是不变的 ,完全可以缓存在内存中,而且缓存时间可以设置的稍微长一点;
抽奖避不开的两个强数据一致性关键数据是抽奖的次数和库存,
先说一下抽奖次数,首先这个数据会随时间快速增长,查询和插入成本会迅速增加,我认为比较好的方式是把抽奖的次数记在用户信息内,一般而言用户可以抽多次将,那抽奖的记录数就是用户数的N呗,用户信息本要查询,
放到一起减少了一次查询而且减少了查询数据量,但是这又会返回来影响用户缓存数据的管理,方案有两个,一用户请求hash,保证某个用户的请求落到某一个特定的机器上,这样就避免了用户信息缓存在多台服务器而造成的数据一致性问题,
当然用memeryCahe这样的集中式缓存来解决也行,第二种方式是把用户信息缓存在redis来解决缓存数据一致性问题,当然,性能肯定要比在内存中差一些 ;
虽然用到了缓存,但是肯定有需要访问数据库的时候,数据库可以做哪些优化呢,首先是分库,抽奖可以单独使用一个数据库实例,然后读写分离,分表,分表的规则有很多,采用哪种规则取决于业务场景,
比如用户信息,可以采用用户id来HASH来分,如抽奖记录我们这边一般是每天多少次抽奖机会,所以特别适合按时间分表,当然用户用户Hash也是可以的。
此外就是索引,把常用查询字段加上索引,提升查询效率,适当的数据冗余,比如中奖纪录可以把用户基本信息冗余进去,减少连表查询等等常规操作。
然后再讲一下库存,库存对数据一致性要求极高,而且读写频繁,并发读很容易造成库存判断错误。我认为比较好的解决方案有两种,第一种方式是加锁:当其中一个线程抽中了奖品时,将此商品锁住,处理完前不让其他线程进入此商品的抽奖环节或等待,
加锁肯定会影响性能,但是考虑到抽奖是一个漏斗模型,抽中奖品的人毕竟很少,对整体的性能影响不大。
但是如果中奖概率需求高时怎么办呢 , 有个办法就是分库存,如果一个奖品有1万个,可以分成100组100个库存,这样就等于把单线程抽奖分成了一百个线程抽奖,至于怎么分,把单个奖品当初一类奖品就行了,库存放到redis和数据库都行。
是不是这样做就万事大吉了呢 ,还可以优化,那就是奖品预设,简单的说,比如有一万个奖品,预先生成一万条中奖纪录,用户抽中奖品后只是关联用户和抽奖纪录而不是写一条数据,这样就避免了超写。
最后补充一个小技巧,比如100万人竞争10个奖品,实际上没有必要100%的用户都参与抽奖,可以让99% 的用户直接返回不中奖,降低计算成本,相当两次抽奖,第一次是抽取实际抽奖资格。
原文:https://www.cnblogs.com/du1216/p/13054646.html