概念
Redis是一种key-value型非关系数据库。
特点:
1、速度快,存在于内存中,类似于HashMap,HashMap的操作和查找的时间复杂度都是O(1)
2、支持丰富的数据类型,包括字符串、哈希、列表、集合、有序集合五种数据类型。
3、支持事务,操作都是原子性的,要么都执行要么都不执行。
4、memcached所有值都是简单的字符串,Redis可以持久化数据(RDB、AOF)。
过期策略
定时过期(定时器)、惰性过期(访问时判断)、定期过期(每隔一段时间扫描)
如何保证Redis都是热点数据?(数据回收)
最近最少使用/随机/最近最少使用/更早过期时间的
持久化(RDB和AOF)
RDB:一定时间将内存数据以快照形式保存到硬盘dump.rdb。通过save参数配置快照周期。
保存成一个文件,更安全。数据量大时,启动效率更高。子进程单独进行持久化保证Redis高性能。持久化过程中发生事故数据容易丢失。
AOF:将Redis执行命令写到单独日志文件中。
数据安全。通过append追加到日志,即使服务器宕机也可通过redis-check-aof工具解决数据一致问题。AOF文件更大,恢复更慢,启动效率低。
保证Redis和MYSQL数据一致
缓存单删: 更新DB前先删除缓存,再更新库,查询缓存无数据则直接访问DB。
延时双删: 为了解决缓存单删,在高并发读情况下数据不一致问题。操作数据前,先删除缓存,接着操作DB,然后延迟一段时间,再删除缓存。
定时+增量更新: 主要是利用库里行数据的更新时间字段+定时增量查询。具体为:每次更新库里的行数据,记录当前行的更新时间;然后把更新时间做为一个索引字段(加快查询速度)
定时任务: 每隔几秒,比如5秒(自定义),把库里最近5秒钟的数据查询出来,写入缓存并记录查询结束时间。查询过程和放入缓存都是单线程不存在并发问题。每次同步成功,下次执行把结束时间作为开始时间,当前时间作为结束时间实现增量查询。
优点:架构简单、逻辑和业务解耦、依赖少,最多加个分布式定时任务或者Redis分布式锁。
监听binlog+MQ: 监听MYSQL binlog把数据库的更新插入删除操作通过MQ同步给下游消费者,下游拿到日志和业务数据再放入缓存。
优点:缓存和业务解耦,减少业务代码的侵入性。
缺点:技术和架构复杂,中间件的运维,引入MQ后带来的问题,比如数据乱序:同一条数据先发后至,后发先至到达消费者,通过redis lua+数据的时间戳比较方案,解决并发问题和数据乱序问题
redis缓存三兄弟问题
PHP+Redis分布式锁
Redis分布式锁是通过线程锁来实现,同时只允许一个线程执行,其他线程进入等待状态。
优点:降低MySQL压力,比队列的并发量高。缺点:线程需要排队等待,并发量也不是特别高。
php
$key = "test:lock:".$id;$uuid = Uuid::uuid1()->getHex();try {
$ret = Redis::set($key, $uuid, 'EX', 10, 'NX');
if (!$ret) {
usleep(10);
return $this->test($id);
}
$stock = Skill::query()->where('id', $id)->value('stock');
if ($stock > 0) {
Skill::query()->where('id', $id)->decrement('stock');
$msg = '抢购成功';
} else {
$msg = '库存不足,抢购失败';
}
if (Redis::get($key) == $uuid) {
Redis::del($key);
}
return $msg;} catch (\Exception $exception) {
return '抢购失败';}
PHP+Redis乐观锁
Redis的乐观锁就是借助Redis事务和watch监控,采用事务打断的方式实现
优点:并没有锁定任何资源,多线程可以并行。缺点:这是PHP层面的控制,而PHP是有性能瓶颈的。
php
$key = 'stock:'.$id;Redis::watch($key);$stock = Redis::get($key);
if (is_null($stock)) {
return '没有商品';
}
if ($stock == 0) {
return '库存不足';
}
Redis::multi();Redis::decr($key);
$res = Redis::exec();
if ($res) {
Skill::query()->where('id', $id)->decrement('stock');
return '抢购成功';
} else {
return '抢购失败';}
Nginx结合Lua做桶限流+Redis乐观锁(最优方案)
这种方案是最优方案,直接绕过应用层,在接入层实现限流和防止超卖的操作,只消耗很少的服务器性能,但是可抗并发量级特别大,性能上远超上述几种方案。
逻辑分析:先使用 Nginx+Lua 漏桶算法过滤掉大部分请求,再使用Lua连接Redis,使用Redis乐观锁的方式控制库存。假设只有10个秒杀商品,那这里只保留10个请求进入应用层(PHP和MysqL),应用层不需要进行其他操作,直接操作数据库就可以。
项目应用
1、ERP的商品报表模块,API接口处理好数据,序列化成字符串后给定过期时间存储到Redis中。
2、序列号生成器,我们又很多单号,像订单、生产、调拨,但需求层面要求我们的订单尾号按照顺序来,我们生成器中会根据类别将对应序列号hash进行自增。
3、订单提取模块,
持续更新,未完待续~