- A+
实战篇 实现秒杀下单
全局唯一ID
在一般业务中秒杀功能,存在并发问题如果两个线程同时执行插入操作导致数据库id 自增 同时为一个数 就会导致写入数据失败
全局Id生成器
点击查看代码
public static class RedisIdWork { private readonly static long BEGIN_TIMSTAMP = 1678966413L; private readonly static int COUNT_BITS = 32; public static long nextId(string keyPrefix) { //1、生成时间戳 long nowTimeSeconds = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); long timesamp = nowTimeSeconds - BEGIN_TIMSTAMP; //2、生成序列号 string date = DateTime.Now.ToString("yyyyMMdd"); var redis = new RedisHelper(); long count = redis.database().StringIncrement("icr:" + keyPrefix + ":" + date, 1); //3、拼接返回 return timesamp << COUNT_BITS | count; } }
秒杀超卖问题
同时间 抢购会导致库存超卖问题(多个线程交叉执行导致的)例如两个线程同时查询到库存为 1 (此时库存中满足大于0 所以两个都会执行扣减)
悲观锁 AND 乐观锁
悲观锁会认为线程安全一定会发生 所以在操作数据之前就先获取锁,确保线程串行执行
主要说一下乐观锁,常见的处理方法
版本号法。乐观锁的关键就是判断之前查询到的数据是否有被修改过
CAS 法 (可以说是版本号法的升级版)
如果说是一个减库存操作,可以使用库存数据作为版本标识
生成的Sql 语句
where id = ? and version > 0
为什么版本要大于0 而不是等于 (因为只要是在这个区间就可以 不小于0 即可)
一人一单秒杀
分布式锁
redis 分布式锁
场景:目的是为了解决多台相同服务之间同时工作产生的并发问题,(例如订单系统,假设订单系统部署在两台机器上,但是库存是固定的,接着每个订单系统实例都去数据库里查了一下,由于并发问题导致超卖,这肯定是不允许的)(当然并发特别大的话是需要进行分段数据,数据分段会导致整体业务流程更加复杂,如果没有这方面的需求建议不要使用)
SETNX LOCK Thread1
为了保证原子性 (带上过期时间) 如果不设置过期时间会出现死锁问题
Set lock thread1 EX 10 NX
高级篇
数据持久化
RDB模式
RDB (redis Database Backup file)数据备份文件
save # redis主进程执行备份,会阻塞所有命令
bgsave #开启子线程执行备份,避免主线程受到影响
save 5 1 (表示五秒内有一次修改就执行备份操作) 可以在redis.conf中修改
bgsave fork主进程开启一个子进程,共享内存空间,(这时候物理内存是只读模式)
如果会有写入操作会拷贝一份数据
AOF模式
AOF全称为 Append only file(追加文件),redis会处理每一个写入命令都会记录在aof文件中
AOf默认是关闭的
appendonly yes appendfilename "appendonly.aof"
AOF和RDB两者区别
分布式主从集群
搭建主从集群
单节点Redis并发能力邮上线,要提升并发能力需要搭建主从集群,实现读写分离(大部分业务都是读的业务)
一般写入到master 节点,同步到子节点
需要三台redis 服务器