目录
一.初识Redis
1.NoSQL
2.认识redis
3.安装redis
下载redis安装包,启动redis,安装redis图形化页面
如果要取设置的name jack的话,使用"get name"命令
二.redis命令
1.redis数据结构介绍
Redis官网:http://redis.io/
Redis官方文档:http://redis.io/documentation
Redis教程:http://www.w3cschool.cn/redis/redis-intro.html
Redis下载:http://redis.io/download
redis英文文档 https://redis.io/topics/data-types
redis中文文档 http://www.redis.cn/documentation.html
《redis设计与实现 3.0版本》 http://redisbook.com/index.html
redis菜鸟教程 https://www.runoob.com/redis/redis-tutorial.html
redis源码解读 3.2.8版本 https://blog.csdn.net/men_wen/article/details/75668345
Redis知识体系详解https://www.pdai.tech/md/db/nosql-redis/db-redis-overview.html
Redis - 知识体系 | OddFar's Notes
————————————————
版权声明:本文为CSDN博主「yujkss」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39900031/article/details/124359962
2.Redis通用命令
如果不设置过期时间,默认是永久的,使用TTL命令返回值是-1,代表永久,-2代表没有此key
3.String类型
SETNX KEY VALUE=SET KEY VALUE NX它两的效果等价
del key 删除key
4.Key的层级格式
5.Hash类型
6.List类型
7.Set类型
8.SortedSet
三.Redis的java客户端
1.客户端对比
2.Jedis
(1)jedis用法
(2).jedis连接池
3.Spring Data Redis
(1)介绍
(2)Spring Data Redis 使用
name和李四存进redis之后是看不懂的字符,这时因为默认使用了jdk的序列化
解决方法:
四.redis企业实战
1.实战课程介绍
2.导入项目
3.基于session实现登录
4.集群的session共享问题
5.基于redis实现共享session登录
6.商户查询缓存
(1)什么是缓存
(2)添加redis缓存
(3)缓存更新策略
初始状态缓存与数据库都是10
下列情况缓存与数据库不一致,初始状态都是10,发生概率较高,写入缓存的操作比数据库操作快
看右边的图,初始状态都是10
见右图,假设初始状态缓存失效,数据库是10,查询缓存未命中,查询数据库为10,线程2更新数据库为20,接着删除缓存,线程1把查询到的10写入缓存,发生概率较低:1.两个线程并行执行2.需要满足缓存刚好失效,3.需要更新数据库比写入缓存快
(4)实现商铺缓存与数据库的双写一致
(5)缓存穿透
方法二实现较复杂,通常选择方法一
什么是布隆过滤器
布隆过滤器(Bloom Filter)是 1970 年由布隆提出的,是一种非常节省空间的概率数据结构,运行速度快,占用内存小,但是有一定的误判率且无法删除元素。它实际上是一个很长的二进制向量和一系列随机映射函数组成,主要用于判断一个元素是否在一个集合中。
通常我们都会遇到判断一个元素是否在某个集合中的业务场景,这个时候我们可能都是采用 HashMap的Put方法或者其他集合将数据保存起来,然后进行比较确定,但是如果元素很多的情况下,采用这种方式就会非常浪费空间,最终达到瓶颈,检索速度也会越来越慢,这时布隆过滤器(Bloom Filter)就应运而生了。
布隆过滤器的优点:
支持海量数据场景下高效判断元素是否存在
布隆过滤器存储空间小,并且节省空间,不存储数据本身,仅存储hash结果取模运算后的位标记
不存储数据本身,比较适合某些保密场景
布隆过滤器的缺点:
不存储数据本身,所以只能添加但不可删除,因为删掉元素会导致误判率增加
由于存在hash碰撞,匹配结果如果是“存在于过滤器中”,实际不一定存在
当容量快满时,hash碰撞的概率变大,插入、查询的错误率也就随之增加了
布隆过滤器中一个元素如果判断结果为存在的时候元素不一定存在,但是判断结果为不存在的时候则一定不存在。因此,布隆过滤器不适合那些对结果必须精准的应用场景。
其他问题
不支持计数,同一个元素可以多次插入,但效果和插入一次相同
由于错误率影响hash函数的数量,当hash函数越多,每次插入、查询需做的hash操作就越多
布隆过滤器适合的场景
区块链中使用布隆过滤器来加快钱包同步;以太坊使用布隆过滤器用于快速查询以太坊区块链的日志
数据库防止穿库,Google Bigtable,HBase 和 Cassandra 以及 Postgresql 使用BloomFilter来减少不存在的行或列的磁盘查找。避免代价高昂的磁盘查找会大大提高数据库查询操作的性能
判断用户是否阅读过某一个视频或者文章,类似抖音,刷过的视频往下滑动不再刷到,可能会导致一定的误判,但不会让用户看到重复的内容
网页爬虫对URL去重,采用布隆过滤器来对已经爬取过的URL进行存储,这样在进行下一次爬取的时候就可以判断出这个URL是否爬取过了
使用布隆过滤器来做黑名单过滤,针对不同的用户是否存入白名单或者黑名单,虽然有一定的误判,但是在一定程度上还是很好的解决问题
缓存击穿场景,一般判断用户是否在缓存中,如果存在则直接返回结果,不存在则查询数据库,如果来一波冷数据,会导致缓存大量击穿,造成雪崩效应,这时候可以用布隆过滤器当缓存的索引,只有在布隆过滤器中,才去查询缓存,如果没查询到则穿透到数据库查询。如果不在布隆过滤器中,则直接返回,会造成一定程度的误判
WEB拦截器,如果相同请求则拦截,防止重复被攻击。用户第一次请求,将请求参数放入布隆过滤器中,当第二次请求时,先判断请求参数是否被布隆过滤器命中。可以提高缓存命中率。Squid 网页代理缓存服务器在 cache digests 中就使用了布隆过滤器。Google Chrome浏览器使用了布隆过滤器加速安全浏览服务
Google 著名的分布式数据库 Bigtable 使用了布隆过滤器来查找不存在的行或列,以减少磁盘查找的IO次数
Squid 网页代理缓存服务器在 cache digests 中使用了也布隆过滤器
Venti 文档存储系统也采用布隆过滤器来检测先前存储的数据
SPIN 模型检测器也使用布隆过滤器在大规模验证问题时跟踪可达状态空间
Google Chrome浏览器使用了布隆过滤器加速安全浏览服务
如果允许误判率的话,可以使用布隆过滤器。
增加元素
当一个元素加入布隆过滤器中的时候,会进行如下操作:
- 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)
- 根据得到的哈希值,在位数组中把对应下标的值置为 1
布隆过滤器如何构建?
布隆过滤器本质上是一个 n 位的二进制数组,用0和1表示。
假如我们以商品为例,有三件商品,商品编码分别为,id1、id2
a)首先,对id1
,进行三次哈希,并确定其在二进制数组中的位置。
b)对id2
,进行三次哈希,并确定其在二进制数组中的位置
三次哈希,对应的二进制数组下标分别是 2、7、98,将原始数据从 0 变为 1。
下标 2,之前已经被操作设置成 1,则本次认为是哈希冲突,不需要改动。
Hash 规则:如果在 Hash 后,原始位它是 0 的话,将其从 0 变为 1;如果本身这一位就是 1 的话,则保持不变。
布隆过滤器使用:
跟初始化的过程有点类似,当查询一件商品的缓存信息时,我们首先要判断这件商品是否存在。
-
通过三个哈希函数对商品id计算哈希值
-
然后,在布隆数组中查找访问对应的位值,0或1
-
判断,三个值中,只要有一个不是1,那么我们认为数据是不存在的。
注意:布隆过滤器只能精确判断数据不存在情况,对于存在我们只能说是可能,因为存在Hash冲突情况,当然这个概率非常低。
7、如何减少布隆过滤器的误判?
a)增加二进制位数组的长度。这样经过hash后数据会更加的离散化,出现冲突的概率会大大降低
b)增加Hash的次数,变相的增加数据特征,特征越多,冲突的概率越小
————————————————
版权声明:本文为CSDN博主「养歌」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wuhuayangs/article/details/121830094
(6)缓存雪崩
(7)缓存击穿
(8)缓存工具封装
(9)总结
7.优惠券秒杀
(1)全局唯一ID
(2) 实现优惠券秒杀下单
(3)超卖问题
低并发情况:
高并发情况:
(4)实现一人一单功能
(5)集群下的线程并发安全问题
用postman打断点测试或者用jmeter并发测试,发现依旧存在线程安全问题,同一个用户由于负载均衡请求在两台服务器上依旧出现一人多单情况。
8.分布式锁
1.什么是分布式锁
2.基于redis的分布式锁
保证set命令具有原子性:
3.redis分布式锁误删问题
线程1误删了线程2的锁,导致线程2和线程3并发,线程1超时释放锁导致线程1和线程2并发(线程123是同一个用户)
4、解决分布式锁误删问题
锁的key为static final的uuid+threadId,所以相同jvm的uuid一样,线程ID不一样,不同jvm的uuid不一样,就算线程一样,也不会重复,总结:用uuid区分不同的jvm,用线程id区分不同的线程
5.分布式锁的原子性问题
判断锁标识和释放锁不是原子性操作,还会出现误删问题,也会出现并发问题,误删问题导致线程2和线程3并发,会先判断数据库中有没有数据,在进行插入,这块线程1业务已经完成了,所以判断不通过,线程2和3不会插入数据,超时释放锁导致线程1和线程2并发,但是线程2虽然获得锁,但是线程1的业务已经完成了,所以线程2判断已经有用户下过单了,所以也不会插入数据。
6.Lua脚本解决多条命令原子性问题
写死的脚本:
传参的脚本:
7.Java调用Lua脚本改造分布式锁
如何保证数据库和缓存一致性:
延迟双删
9.Redission功能介绍
1.Reddison入门
2.Redisson可重入锁原理
下面这个例子说明当前锁不可重入,method1调用method2,还是同一个线程,但是method2拿不到锁了。
3.基于Redis的分布式锁优化
4.分布式锁-Redisson的multiLock原理
以上两张图含义:Java应用在主节点获取到了锁,主节点要向从节点同步数据的时候,主节点发生了故障,同步尚未完成,redis中有哨兵监听集群状态,当发现主节点宕机之后,首先客户端连接会断开,哨兵机制会从两个从节点选出一个作为新的主节点,但是这时候之前的主节点的数据因为宕机没有向从节点同步数据,锁已经丢失了,Java应用来访问新的主节点时,发现锁没了(锁失效),这时候如果有其他线程进来,也会获取锁成功,就会出现并发安全问题,这就是主从一致性导致的锁失效问题。
redisson如何解决主从一致性问题:可以不建主从关系,都用主节点,也可以建立主从关系(如下图),必须每个节点获取锁成功,才算获取锁成功
就算有一个节点宕机,从这个选出来的新节点可以获取锁,那么其他线程也不会获取锁成功,因为必须拿到每个节点的锁,才算获取锁成功
注:这块要读源码
5.Redis秒杀优化-异步秒杀思路
下图中存在的问题:操作数据库较多,tomcat中业务逻辑是串行,并且为了保证一人一单,还用了分布式锁。
6.秒杀优化-基于Redis完成秒杀资格判断
7.Redis消息队列的三种方式对比 实现异步秒杀
(1)Redis实现消息队列的方式
10.达人探店
1.发布探店笔记
2.查看探店笔记
3.点赞功能
4.点赞排行榜
11.好友关注
1.关注和取关
2.共同关注
3.关注推送
方案1:节省内存空间,延迟高
拉模式也叫读扩散。这是因为,进行阅读的用户本身不接受任何数据,当开始阅读时,才会将关注的内容拉取。模型图如下:
优点:
节省内存空间,因为收件箱阅读结束后就会清空,相当于消息只在个人的发件箱中保存了一份。
缺点:
耗时较久,因为除了需要拉取消息外,还需要对消息进行排序,这个过程比较耗时,当一个用户关注的人较多,一次性拉取的消息过多这个耗时会更久。
方案2:内存占用率高,延迟低
推模式也叫写扩散。这是因为当一个人发表动态时,会将该信息推送到每个关注他的收件箱。
优点:延时低,当粉丝读取消息时,已经是一个排序好的数据。
缺点:占用内存空间,同一份消息需要存储n份。当一个人粉丝很多时,存储过多重复数据。
粉丝数量多的账号拥有一个发件箱,发件箱会推送给活跃用户的收件箱,而对于普通用户不经常使用的,采取拉模式,当开始阅读时,主动从发件箱中拉去。对于粉丝数量不多的一般用户,直接采取推模式即可。
Feed流的分页问题
我们通常对一个页面能够展示的数据有一定限制,因此当收件箱存在很多数据时,我们需要实现分页功能。如何实现Feed流的分页功能是一个复杂的问题,如果采用MyBatis中的分页插件是不能满足分页功能的,因为Feed流是一个动态的,数据会不断更新,因此数据的角标会不停更新,当我们需要查询第5-10条的数据时,可能又保存了一个新的数据到数据库,从而导致我们分页查找的数据存在与上一次查询的数据重复的问题,具体问题流程图如下:
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/zmbwcx/article/details/134838765
因此我们需要采用滚动分页,所谓滚动分页就是每次查询过数据库后,我们需要记录这一次查询结果的最后一条数据,下一次查询时,从上一次的最后一条开始往后查。流程图如下
而满足滚动分页的Redis数据结构只有ScoreSet,记录每次执行结束后最后一个数据的分数,下一次查询时,从该分数的下一个开始查询。因为我们要根据时间进行排序,因此,这里的分数应该是时间戳。那么实现滚动分页的语句应该为
ZRANGEBYSCORE key -inf +inf WITHSCORES LIMT offset count
命令解释:
-inf:表示最小值
+inf:表示最大值,这样可以在不知道分数的情况下,对全部的值进行操作
offset:偏移量,表示当前数据之后第几个偏移量后开始获取元素
count:需要获取元素的数量
在我们实际开发中,还需要注意一个问题就是score重复的问题。对于score重复我们应该记录本次查询结果中,最小score的数量,来充当下一次的偏移量,比如说我在Redis中存在如下几个数据
如果我们一次查询3条数据,在记录到当前查询的最小分数后,下次查询时指定偏移量为1时,那么可能会存在如下问题
zrevrangebyscore feed:test +inf -inf withscores limit 0 3
此时我们查找到m5与n5,接下来我们应该从i5开始查找,那么如果将偏移量设置为 1 进行测试观察结果
zrevrangebyscore feed:test 5 -inf withscores limit 1 3
可以看到,他是从第一个分数等于5的开始计算,获取之后的三个元素,因此,我们需要在开发中,对偏移量进行计算,方便我们下次进行查询时设置偏移量,大概的Java代码如下
//lastId指的是本次查询的最小分数值
//offset指的是偏移量
public Result queryPage(Long lastId, Integer offset) {
//2、查询属于自己的收件箱
Set<ZSetOperations.TypedTuple<String>> typedTuples =
stringRedisTemplate.opsForZSet()
.reverseRangeByScoreWithScores(FEED_KEY, 0, lastId, offset, 3);
//如果set等于null
if (typedTuples==null || typedTuples.isEmpty()){
return Result.ok();
}
//3、解析数据minTime
//记录最小分数
long score=0;
//记录偏移量
int os=1;
List<String> pageValue=new ArrayList<>(typedTuples.size());//设置列表长度为typedTuples的大小
for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
String value = typedTuple.getValue();
//将查询的数据存储在集合中
pageValue.add(value);
//记录当前分数
long time = typedTuple.getScore().longValue();
if (time==score){
//如果当前分数相同,则偏移量加1
os++;
}else {
//如果不同,说明当前的分数更低,刷新偏移量
score=time;
os=1;
}
}
//返回最小的分数以及偏移量供下次调用时使用。
Pages pages = new Pages();
pages.setPageValue(pageValue);
pages.setLastId(score);
pages.setOffset(os);
return Result.ok(pageValue);
}
12.附近商户
1.GEO数据结构
2.附近商户搜索
13.用户签到
1.BitMap的用法
2.签到功能
3.签到统计
14.UV统计
1.HyperLogLog用法
2.实现UV统计
15.分布式缓存
1.redis持久化-单节点redis的问题
2.redis持久化-RDB持久化
3.Redis持久化-AOF持久化
16.Redis主从
1.搭建主从架构
2.数据同步原理
3.增量同步原理
17.redis哨兵
1.哨兵的作用和工作原理
2.搭建哨兵集群
见文档
3.RedisTemplate的哨兵模式
18.redis分片集群
1.搭建分片集群
2.散列插槽
3.集群伸缩
步骤:先启动一个7004的实例 ,添加到之前的集群,并作为一个master节点,然后执行如下命令如下图(由之前信息可知num在2765这个卡槽上,所以这里把0-2999从7001移到7004)
4.故障转移
5.redisTemplate访问分片集群
19.多级缓存(亿级流量的缓存方案)
1.什么是多级缓存
2.JVM进程缓存
(1)导入商品案例
(2)初识Caffeine
说明:去掉延时10s,打印结果三个key都存在,因为缓存不会立即清除,如果加上延时,就会发现打印前两个key都为null
(3)实现进程缓存
3.Lua语法入门
4.多级缓存
5.tomca集群的负载均衡
6.nginx本地缓存
7.缓存同步
20.redis开发实践
1.Redis键值设计
2.批处理优化
(1)pipeline
(2)集群下的批处理
21.服务端优化
1.持久化配置
2.慢查询
3.命令及安全配置
4.内存配置
22.redis优化-集群还是主从
五.Redis原理篇
1.数据结构
1.SDS
2.IntSet
3.Dict
4.ZipList
5.QuickList
6.SkipList
7.RedisObject
8.五种数据类型
2.Redis网络模型
1.用户空间和内核空间
2.阻塞IO
3.非阻塞IO
4.IO多路复用
5.IO多路复用-select
6.IO多路复用-poll
7.IO多路复用-epoll
8.IO多路复用-事件通知机制(epoll的ET和LT模式)
9.IO多路复用-web服务流程
10.信号驱动IO
11.异步IO
12.同步和异步
13.Redis是单线程么,为什么这么使用?
14.Redis网络模型
3.Redis通信协议
1.RESP协议
2.模拟Redis客户端
4.Redis内存策略
1.过期key处理(过期策略)
2.Redis内存淘汰策略
本文ppt截图及笔记来自于B站黑马教程redis实战,感兴趣的可以去看原版视频哦:https://www.bilibili.com/medialist/detail/ml2804022285?type=1&spm_id_from=333.999.0.0