一、引言
1.1 数据库压力过大
由于用户量增大,请求数量也随之增大,数据压力过大
1.2 数据不同步
多台服务器之间,数据不同步
咱们登录之后,会把你的数据存到Session里面,随后进入主页显示
在微服务项目或使用多台服务器,多个服务器的Session是不能在其他服务器使用
1.3 传统锁失效
多台服务器之间的锁,已经不存在互斥性了。
在服务器1加锁,那么在服务器2其实是没用的
现在有十个服务器,每个服务器都有一个项目,这个时候我在我的服务器加锁
你在另一个服务器调用另一个服务的时候,是不能对当前服务加锁的
在微服务中,分模块开发,访问的都不是同一个项目
这样一个项目加锁,对其他项目没用
二、Redis介绍
2.1 关于关系型数据库和NOSQL数据库(键值对存储)
关系型数据库是基于关系表的数据库,最终会将数据持久化到磁盘上,而nosql数据库是基于特殊的结构,并将数据存储到内存的数据库。从性能上而言,nosql数据库要优于关系型数据库,从安全性上而言关系型数据库要优于nosql数据库,所以在实际开发中一个项目中nosql和关系型数据库会一起使用,达到性能和安全性的双保证。
NOSQL产品: redis,mongodb,memcached...
Redis就是一款NoSQL
NoSQL -> 非关系型数据库 -> Not Only SQL。
Key-Value:Redis ...
文档型:ElasticSearch,Solr,Mongodb ...
面向列:Hbase,Cassandra ...
图形化:Neo4j ...
除了关系型数据库都是非关系型数据库
NoSQL只是一种概念,泛指非关系型数据库,和关系型数据库做一个区分
2.2 Redis介绍
有一位意大利人,在开发一款LLOOGG的统计页面,因为MySQL的性能不好,自己研发了一款非关系型数据库,并命名为Redis。Salvatore。
Redis(Remote Dictionary Server)即远程字典服务,Redis是由C语言去编写,Redis是一款基于Key-Value的NoSQL,而且Redis是基于内存存储数据的,Redis还提供了多种持久化机制,性能可以达到110000/s读取数据以及81000/s写入数据,Redis还提供了主从,哨兵以及集群的搭建方式,可以更方便的横向扩展以及垂直扩展。
Redis之父
![](https://i-blog.csdnimg.cn/blog_migrate/599a27a198d2bfc46bf6ceb42b86dc8f.png)
2.3 使用Redis的好处
Redis基于纯内存操作,效率非常快
Redis读的时候大概每秒11万字,写的时候大概8.1w字左右
Redis是单线程,避免了CPU的来回切换比较安全
为什么说Redis是单线程效率还不低呢?
因为Redis是基于 IO多路复用
网络上会说6.0版本之前是单线程,6.0之后是多线程
其实不用管这些,Reids工作线程只有一个
在6.0版本中,默认的还是单线程,多线程需要自行开启
但是为了保证线程安全,却又不得不加锁,但是加锁效率就会降低
三、Redis安装
3.1 安装Redis
官网: https://redis.io
下载: http://download.redis.io/releases
中文网: https://www.redis.net.cn/
官网提供安装方式如下:(安装redis编译的c环境,此步骤没有编译)
Installation
Download, extract and compile Redis with:
$ wget < http://download.redis.io/releases/redis-5.0.5.tar.gz >
$ tar xzf redis-5.0.5.tar.gz $ cd redis-5.0.5
$ make
The binaries that are now compiled are available in the src directory. Run Redis with:
$ src/redis-server
You can interact with Redis using the built-in client:
$ src/redis-cli
redis> set foo bar
OK
redis> get foo
"bar"
3.2 安装步骤
1.把下载好的redis-6.2.6.tar.gz安装包拷贝到当前虚拟机root目录下,解压到/usr/local下
[root@Gao ~]# tar -zxvf redis-6.2.6.tar.gz -C /usr/local
2.编译的c环境,注:安装时如果显示yum正在运行,需要先将yum进程kill后再执行该命令
[root@Gao ~]# yum install gcc-c++^C
3.进入redis-6.2.6目录 使用make命令编译redis(若报错,先make distclean,再make)
[root@Gao redis-6.2.6]# make
4.使用make PREFIX=/usr/local/redis-6.2.6 install命令安装(安装后会出现bin目录)
[root@Gao redis-6.2.6]# make PREFIX=/usr/local/redis-6.2.6 install
5.启动redis服务端(前台启动)
[root@Gao redis-6.2.6]# cd bin
[root@Gao bin]# ./redis-server
![](https://i-blog.csdnimg.cn/blog_migrate/feddcc3778bade178f9f2fa499ad4b22.png)
启动后看到如上欢迎页面,但此窗口不能关闭,窗口关闭就认为redis也关闭了,所以我们需要在后台启动,然后再启动客户端进行连接,所以首先Ctrl+C退出。
解决:可以通过修改配置文件配置redis的后台启动(即服务器启动了但不会创建控制台窗口)
步骤如下:
1.切换到redis-6.2.6目录下,把当前目录下的redis.conf文件拷贝到bin目录下
[root@Gao bin]# cd ../
[root@Gao redis-6.2.6]# cp redis.conf bin/redis.conf
2.切换到bin目录下,修改redis.conf文件
[root@Gao redis-6.2.6]# cd bin
[root@Gao bin]# vim redis.conf
3.将redis.conf文件中的daemonize的值从no修改成yes表示后台启动
![](https://i-blog.csdnimg.cn/blog_migrate/36428e9309bb05dc02b26c9b68029d9e.png)
4.启动redis服务端(后台启动)
[root@localhost bin]# ./redis-server redis.conf
5.查看是否启动成功
[root@localhost bin]# ps -ef | grep redis
6.启动客户端
[root@localhost bin]# ./redis-cli
7.存取数据进行测试
127.0.0.1:6379> set name jack
OK
127.0.0.1:6379> get name
"jack"
![](https://i-blog.csdnimg.cn/blog_migrate/105929be74050b6cb0520ed03adff5f2.png)
这边会有几道相关面试题:
Redis是单线程还是多线程
Redis单线程为什么效率高
怎么保证Redis和MySQL的数据一致性
3.3 Redis的配置文件
配置项名称 | 配置项值范围 | 说明 |
daemonize | yes、no | yes表示启用守护进程,默认是no即不以守护进程方式运行。其中Windows系统下不支持启用守护进程方式运行 |
port | 指定 Redis 监听端口,默认端口为 6379 | |
bind | 绑定的主机地址,如果需要设置远程访问则直接将这个属性备注下或者改为bind * 即可,这个属性和下面的protected-mode控制了是否可以远程访问 。 | |
protected-mode | yes 、no | 默认是yes,即开启。设置外部网络连接redis服务,设置方式如下:1、关闭protected-mode模式,此时外部网络可以直接访问2、开启protected-mode保护模式,需配置bind ip或者设置访问密码 |
timeout | 300 | 当客户端闲置多长时间后关闭连接,如果指定为 0,表示关闭该功能 |
loglevel | debug、verbose、notice、warning | 日志级别,默认为 notice |
databases | 16 | 设置数据库的数量,默认的数据库是0。整个通过客户端工具可以看得到 |
rdbcompression | yes、no | 指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大。 |
dbfilename | dump.rdb | 指定本地数据库文件名,默认值为 dump.rdb |
dir | 指定本地数据库存放目录 | |
requirepass | 设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH <password> 命令提供密码,默认关闭 | |
maxclients | 0 | 设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息。 |
maxmemory | XXX <bytes> | 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区。配置项值范围列里XXX为数值。 |
四、redis-benchmark官方自带的性能测试工具
4.1 在安装的redis中可以看到redis-benchmark
![](https://i-blog.csdnimg.cn/blog_migrate/5af6ed013e7a3c7e033640d70e4ea9da.png)
4.2 关于redis-benchmark 的一些参数
![](https://i-blog.csdnimg.cn/blog_migrate/9f7b07d581d8a1c14d9c3ea682d7cb69.png)
测试:100个并发,十万个请求,单机测试
[root@Gao bin]# ./redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000
![](https://i-blog.csdnimg.cn/blog_migrate/384a5356f11749ff81b127cf16175738.png)
====== SET ====== 100000 requests completed in 0.94 seconds 表示对十万个请求进行写入测试100 parallel clients 100个并发客户端
3 bytes payload 每次写入3个字节
keep alive: 1 只有一台服务器处理请求
host configuration "save": 3600 1 300 100 60 10000 RDB持久化方式默认开启的规则
host configuration "appendonly": no AOF持久化方式未开启
multi-thread: no 多线程未开启
99.998% <= 12.559 milliseconds (cumulative count 99999)
99.999% <= 12.567 milliseconds (cumulative count 100000)
100.000% <= 12.567 milliseconds (cumulative count 100000) 请求在多少毫秒处理完成
throughput summary: 106837.61 requests per second 每秒处理请求数
另一种写法:
只测试 set 和 get 命令后退出
[root@Gao bin]# ./redis-benchmark -h 127.0.0.1 -p 6379 -t set,get -n 10000 -q
五、Redis的数据类型
常用的5种数据结构:
key-string:一个key对应一个值。
key-hash:一个key对应一个Map。
key-list:一个key对应一个列表。
key-set:一个key对应一个集合。
key-zset:一个key对应一个有序的集合。
另外三种数据结构:
HyperLogLog:计算近似值的。
GEO:地理位置。
BIT:一般存储的也是一个字符串,存储的是一个byte[]。
redis是一种高级的key-value的存储系统,其中的key是字符串类型,尽可能满足如下几点:
1.key不要太长,最好不要操作1024个字节,这不仅会消耗内存还会降低查找效率
2.key不要太短,如果太短会降低key的可读性
3.在项目中,key最好有一个统一的命名规范(根据企业的需求)
value最常用的五种数据类型:
1.字符串(String):最常用的,一般用于存储一个值
2.列表(List):使用list结构实现栈和队列结构
3.集合(Set) :交集,差集和并集的操作
4.有序集合(sorted set) :排行榜,积分存储等操作
5.哈希(Hash):存储一个对象数据的
![](https://i-blog.csdnimg.cn/blog_migrate/eb5fd32872a73343df88eb39a8578b51.png)
5.1 字符串(String)
set key value:设定key持有指定的字符串value,如果该key存在则进行覆盖操作,总是返回"OK"
get key:获取key的value。如果与该key关联的value不是String类型,redis将返回错误信息,因为get命令只能用于获取String value,如果该key不存在,返回null
setex key seconds value:设置key以及对应的value,还可以设置过期时间
setnx key value:当key不存在时,设置对应的value,当key存在时,不做任何操作
incr key:将指定的key的value原子性的递增1.如果该key不存在,其初始值为0,在incr之后其值为1。如果value的值不能转成整型,如hello,该操作将执行失败并返回相应的错误信息。
decr key:将指定的key的value原子性的递减1.如果该key不存在,其初始值为0,在incr之后其值为-1。如果value的值不能转成整型,如hello,该操作将执行失败并返回相应的错误信息。
incrby key increment:将指定的key的value原子性增加increment,如果该key不存在,器初始值为0,在incrby之后,该值为increment。如果该值不能转成整型,如hello则失败并返回错误信息。
decrby key decrement:将指定的key的value原子性减少decrement,如果该key不存在,器初始值为0,在decrby之后,该值为decrement。如果该值不能转成整型,如hello则失败并返回错误信息。
使用场景如下:
1.简单的缓存存储(最常用)
2.消息的失效性(过期时间的设置)
3.分布式锁的实现(redisson)
上面安装完Redis时咱们简单的测试了一下set get的使用方式
这边咱们简单说一下setnx应用场景:在Redis加锁的时候可以用
![](https://i-blog.csdnimg.cn/blog_migrate/97efd86ae4362a9bf87aeb067efda1cd.png)
setnx 键有了就不允许再赋值,不让覆盖 可以看状态码1 0
![](https://i-blog.csdnimg.cn/blog_migrate/25d23a2e4cdf5a4dc4288b493bad5c69.png)
多个线程进来程序时,如果不加锁可能会出现超领超卖的情况(只有一百件上商品这时进来一千个人因为是多线程的原因,就可能导致一个商品卖给了多个人。同理抢优惠券同样如此,可能会出现一张优惠券被多人领取)
单体架构项目可以通过加锁解决
微服务项目中,一台服务器加锁是没用的,这个时候我们就该考虑在Redis加锁了(Redis一定是一个,哪怕是搭建了集群)
这个时候就该使用setnx了,只要setnx成功,就证明我拿到这个锁了
如果不成功,那就在外面等候
![](https://i-blog.csdnimg.cn/blog_migrate/fbc5925ce8cd011a328cbe18a44d8018.png)
设置过期时间,这里设置20秒,通过ttl指令查看
![](https://i-blog.csdnimg.cn/blog_migrate/ab077055448c8d10b2c57dd2f02c0f08.png)
setnx后,要执行这个程序
执行完之后我要释放这个锁在在(这个时候给这个key删了就行了)
![](https://i-blog.csdnimg.cn/blog_migrate/e7519bed714a5ce1a66731c8ae40eca5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/874c88b20e199d1430e089ae46251f15.png)
举个例子 删除name后 这个时候get肯定是没有的
所以在这个时候setnx name一定会成功
按照正常流程来走,我们这个是不会出问题的
但是万一出什么问题呢?
在执行程序的时候响应的时间比较长,或者出现异常了
最终是没有执行到释放锁这行代码
这个时候会出现什么问题?
这个时候如果没有执行释放锁的代码,那么别人是不是永远都进不来(就像你一直在抢购,手机卡了不动,因为你没有拿到这把锁,所以不会返回抢购成功,就会一直在那等)
会造成多个线程在这个程序里
解决:
在当前设计key时,给一个 过期时间 到了一定时间,这个key自动删除
删除成功后,另外的线程就可以setnx成功,就会进到程序里
自增自减我们就不演示了,感兴趣的可以自己尝试
set num 1 定义num变量
incr num 自增
在购买商品时,我们点一个加号就可以自增1
或者说点赞,点一次自增1
decr num 自减1
incrby num 100 直接加100
decrby num 100 直接减100
String里命令很多,咱们就说是这么多
总结一下:
String中set get用的最多也是最简单的,除此以外咱们也演示了setnx setex在设置key的有效期加锁用的比较多
5.2 列表(List)
list就是前面加一个l,push就是往里面存数据
常见的数据结构,一个是队列,还有一个就是栈
队列的话就是先进先出 栈的话就是先进后出
栈一般有两个方法。压栈push方法,通俗点就是先压进枪的子弹最后射出来。还有一个叫做弹栈pop移除的方法,这个在list集合的API里面就有,感兴趣的伙伴可以去看看
lpush key value1 value2...:在指定的key所关联的list的头部插入所有的values,如果该key不存在,该命令在插入的之前创建一个与该key关联的空链表,之后再向该链表的头部插入数据。插入成功,返回元素的个数。
rpush key value1 value2…:在该list的尾部添加元素。
lrange key start end:获取链表中从start到end的元素的值,start、end可为负数,若为-1则表示链表尾部的元素,-2则表示倒数第二个,依次类推….
![](https://i-blog.csdnimg.cn/blog_migrate/9b023ad243f10d79f40e4fcd4887a977.png)
lpop key:返回并弹出指定的key关联的链表中的第一个元素,即头部元素。
rpop key:从尾部弹出元素。
![](https://i-blog.csdnimg.cn/blog_migrate/fc4f503b9b69705674f7e4bbe71e2c59.png)
llen key:返回指定的key关联的链表中的元素的数量。
使用场景如下:
消息流的场景:
1.用户的id作为key,发送的消息作为value,例如:朋友圈发布,微博发布,公众号发布(只要是带顺序的都可以用list去存)...
5.3 集合(Set,不允许出现重复的元素)
sadd key value1 value2…:向set中添加数据,如果该key的值已有则不会重复添加。
smembers key:获取set中所有的成员。
scard key:获取set中成员的数量。
![](https://i-blog.csdnimg.cn/blog_migrate/19c3a79d550d9711f42a1357ffae6b32.png)
sismember key member:判断参数中指定的成员是否在该set中,1表示存在,0表示不存在或者该key本身就不存在。
srem key member1 member2… :删除set中指定的成员。
![](https://i-blog.csdnimg.cn/blog_migrate/6882cdf8a108b22c00bdcc4663cebf0d.png)
srandmember key:随机返回set中的一个成员。
![](https://i-blog.csdnimg.cn/blog_migrate/82634ae68220576e3f154b5cf563d1d5.png)
spop key:随机返回set中的一个成员并从set中移除。
sdiff key1 key2:返回key1与key2中相差的成员,而且与key的顺序有关,即返回差集。
![](https://i-blog.csdnimg.cn/blog_migrate/3e618496a66587a0c2d8fe7ce8aa5a14.png)
sdiffstore destination key1 key2:将key1、key2相差的成员存储在destination上。
![](https://i-blog.csdnimg.cn/blog_migrate/ab46b9063063974728dad5027d3dfb43.png)
sinter key[key1,key2…]:返回交集。
sinterstore destination key1 key2:将返回的交集存储在destination上。
![](https://i-blog.csdnimg.cn/blog_migrate/19ffff94f167f95f5fd9d0772bc4291c.png)
sunion key1 key2:返回并集。
![](https://i-blog.csdnimg.cn/blog_migrate/f2a09836458094607859320e73151ad8.png)
sunionstore destination key1 key2:将返回的并集存储在destination上
![](https://i-blog.csdnimg.cn/blog_migrate/be32d5728faf9c8de332038b8f7f8735.png)
使用场景如下:
公司年会,随机抽奖小程序
1.把所有用户统一存入set集合中
2.查看所有抽奖人数
3.随机抽取指定得奖人数并从set集合中删除
实现:
1.sadd choujiang userid1,userid2,userid3...添加
2.smembers choujiang
3.spop choujiang [count] 移除
微信点赞,微博收藏
1.点赞,创建集合并加入对应用户
2.取消点赞,从集合中删除对应用户
3.检查用户是否点过赞
4.获取点赞用户列表
5.获取点赞用户数量
实现:
1.sadd dianzan userid1,userid2,userid3... 点赞了加进来
2.srem dianzan userid1 不点赞了取消点赞了再给他移出去
3.sismember dianzan userid1 想看谁点赞了
4.smembers dianzan 想看都有多少个人
5.scard dianzan 只看数量
查看共同好友,推荐可能认识的人
1.两个集合取交集
实现:
1.sinter user1list,user2list...
2.sinterstore list user1list,user2list...
5.4 有序集合(sorted set)
这个是有序set,那么他肯定在格式上有什么不一样的地方
zadd key score member score2 member2 … :将所有成员以及该成员的分数存放到sorted-set中。
![](https://i-blog.csdnimg.cn/blog_migrate/3ff5b615f791f74570bee262b307d670.png)
zcard key:获取集合中的成员数量。
![](https://i-blog.csdnimg.cn/blog_migrate/fa4d95a34e867b72c90edf76faee79ef.png)
zcount key min max:获取分数在[min,max]之间的成员。
![](https://i-blog.csdnimg.cn/blog_migrate/4bf81d7e2edf13abbc4588cdbd90ed81.png)
zincrby key increment member:设置指定成员的增加的分数。
zrangebyscore key min max [withscores] [limit offset count]:返回分数在[min,max]的成员并按照分数从低到高排序。[withscores]:显示分数;[limit offset count]:offset,表明从脚标为offset的元素开始并返回count个成员。
![](https://i-blog.csdnimg.cn/blog_migrate/6eae4cceae21fb162b88d62af87becbe.png)
zrevrangebyscore key min max [withscores] [limit offset count]:上面类似(score需从大到小),从高到底排序
使用场景如下:
排行榜实现
1.对播放的视频,分数自增1
2.展示排行榜前十的视频
实现:
1.zincrby videos 1 video1id
2.zrevrangebyscore videos 100 0 withscores limit 0 10 (zrevrangebyscore从大到小排序)
5.5 哈希(Hash)
hset key field value:为指定的key设定field/value对(键值对)。
hget key field:返回指定的key中的field的值。
hgetall key:获取key中的所有filed-vaule。
hlen key:获取key所包含的field的数量。
hincrby key field increment:设置key中filed的值增加increment
hdel key field [field ...]:删除key中的属性
![](https://i-blog.csdnimg.cn/blog_migrate/b3acc2becc0876fc200fea40ef3b935e.png)
使用场景如下:
购物车场景:
1.用户的id作为key
2.商品的id作为field(属性)
3.商品的数量作为value(属性值)
购物车操作:
1.用户添加购物车:hset cart:1001 20001 1 (id为1001的用户添加了一个id为20001的商品,数量为1个)
2.增加对应商品数量:hincrby cart:1001 20001 1
3.查询商品总数:hlen cart:1001
4.删除该用户的某个商品:hdel cart:1001 20001
5.查询该用户购物车信息:hgetall cart:1001
![](https://i-blog.csdnimg.cn/blog_migrate/e0c7bc02ace17e715421f7475fce9c7e.png)
5.6 通用操作
keys patten:获取所有与patten匹配的key,*表示任意字符,?表示一个字符。
del key1 key2....:删除指定的key。
exists key:判断该key是否存在,1表示存在,0表示不存在。
expire key second:为当前key设置过期时间(单位:秒)。
ttl key:查看当前key剩余过期时间。
flushall: 删除所有key(慎用)
flushdb: 删除所有key(慎用)
六、Jedis的使用【重点】
1.修改/usr/local/redis-6.2.6/bin目录下的redis.conf配置文件,然后启动redis服务端
![](https://i-blog.csdnimg.cn/blog_migrate/01c98093d84271607d54768845ce9421.png)
修改redis.conf文件中的bind为:0.0.0.0,然后把保护模式关掉
如需设置密码,可以使用以下两种方式:
方式一:通过修改 redis.conf 文件,设置Redis的密码校验
requirepass 密码
方式二:在不修改 redis.conf 文件的前提下,在第一次链接Redis时,输入命令:Config set requirepass 密码
后续连接redis客户端的时候,需要先 AUTH 做一下校验
127.0.0.1:6379> auth 密码
2.创建Maven工程,导入依赖
<dependencies>
<!-- jedis客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
User实体类
package com.gao.jedis;
import com.alibaba.fastjson.JSON;
import com.gao.poji.User;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @Date 2023/3/20 22:31
* @Author Gao
*/
public class JedisTest {
@Test
public void test1(){
//获取客户端对象
Jedis jedis = new Jedis("47.93.163.64",6379);
//保存数据
jedis.set("name","jack");
//保存数据
String name = jedis.get("name");
System.out.println(name);
//关闭
jedis.close();
}
@Test
public void test2(){
//获取客户端对象
Jedis jedis = new Jedis("47.93.163.64",6379);
//创建对象
User user = new User(1111,"张无忌");
//转化为Json
String jsonUser = JSON.toJSONString(user);
//保存数据
jedis.set("user",jsonUser);
//获取数据
String redis_jsonUser = jedis.get("user");
//类型转化
Object db_user = (User)JSON.parseObject(redis_jsonUser,User.class);
System.out.println(db_user);
//关闭
jedis.close();
}
}
JedisTest测试类
package com.gao.jedis;
import com.alibaba.fastjson.JSON;
import com.gao.poji.User;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @Date 2023/3/20 22:31
* @Author Gao
*/
public class JedisTest {
@Test
public void test1(){
//获取客户端对象
Jedis jedis = new Jedis("47.93.163.64",6379);
//保存数据
jedis.set("name","jack");
//保存数据
String name = jedis.get("name");
System.out.println(name);
//关闭
jedis.close();
}
@Test
public void test2(){
//获取客户端对象
Jedis jedis = new Jedis("47.93.163.64",6379);
//创建对象
User user = new User(1111,"张无忌");
//转化为Json
String jsonUser = JSON.toJSONString(user);
//保存数据
jedis.set("user",jsonUser);
//获取数据
String redis_jsonUser = jedis.get("user");
//类型转化
Object db_user = (User)JSON.parseObject(redis_jsonUser,User.class);
System.out.println(db_user);
//关闭
jedis.close();
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/5c6221cb57ab899e47e0338d9833595d.png)
然后我们可以在redis中查看user
![](https://i-blog.csdnimg.cn/blog_migrate/305df72b1e1c7114352b9ec3a1a9e50d.png)
id:1111 name因为是中文所以给转义了。但是没关系,后台数据是正常的
刚才是String类型,那么如果是其他类型呢?我们以list为例
![](https://i-blog.csdnimg.cn/blog_migrate/8306a1f7eaf9fd13775760642da6a566.png)
除此我们还可以用连接池,可以达到同样的效果
![](https://i-blog.csdnimg.cn/blog_migrate/2603d819e69a7b2cccc799cb9f53d332.png)
七、Redis中的事务
Redis中的事务和MySQL中的事务有本质的区别,Redis中的事务是一个单独的隔离操作,事务中所有的命令都会序列化,按照顺序执行,事务在执行的过程中,不会被其他客户端发来的命令所打断,因为Redis服务端是个单线程的架构,不同的Client虽然看似可以同时保持连接,但发出去的命令是序列化执行的,这在通常的数据库理论下是最高级别的隔离。
Redis中的事务的作用就是串联多个命令,防止别的命令插队。
常用命令:multi、exec、discard、watch、unwatch
当输入multi命令时,之后输入的命令都会被放在队列中,但不会执行,直到输入exec后,Redis会将队列中的命令依次执行,discard用来撤销Exec之前被暂存的命令,并不是回滚。
![](https://i-blog.csdnimg.cn/blog_migrate/10c1cb0a233b0c185ed21a47fb384472.png)
watch/unwatch
在执行multi之前,先执行watch key1 [key2...] ,watch提供的乐观锁功能(初始时一个版本号,exec之后会更新当前版本号),在你exec的那一刻,如果被watch的键发生过改动,则multi到exec之间的指令全部不执行。
watch表示监控,相当于加锁,但在执行完exec时就会解锁。
unwatch取消所有锁。
![](https://i-blog.csdnimg.cn/blog_migrate/a3c822fb5331a400ac90f67ddd2c9b06.png)
Redis中的事务的特性总结
1.单独的隔离操作
事务中的所有命令都会序列化,然后按顺序执行,在执行过程中,不会被其他客户端发送的命令打断。
2.没有隔离级别的概念
队列中的命令没有被提交之前都不会执行。
3.不能保证原子性
Redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,不会回滚
主要就是三个命令:multi开启、exec执行、discard撤销
八、Redis消息订阅与发布
subscribe channel 订阅频道 例如:subscribe cctv5
publish channel content 在指定频道中发布内容 例如:publish cctv5 basketball
同时打开两个客户端,一个订阅频道,一个在频道中发布内容,订阅频道的客户端会接收到消息。
![](https://i-blog.csdnimg.cn/blog_migrate/c76ce7ef2b08ebc41a215f0d83ae0bf5.png)
测试代码
//先启动订阅频道的方法,再测试发布的方法,然后去控制台查看信息
@Test
public void testSubscribe(){
//获取Jedis对象
Jedis jedis = new Jedis("47.93.163.94",6379);
//接收数据
jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("订阅频道:" + channel);
System.out.println("订阅消息:" + message);
}
},"cctv5");
//关闭
jedis.close();
}
//发布消息
@Test
public void testPublish(){
//获取Jedis对象
Jedis jedis = new Jedis("47.93.163.94",6379);
//发布数据
jedis.publish("cctv5","世界杯");
//关闭
jedis.close();
}
九、Redis图形化程序界面
市面上有各种各样的Redis图形化工具,好处就是便于我们操作
Redis中默认有16个库,可以在不同的库中存储数据,默认使用0号库存储数据,使用select 0-15可以选择不同的库。
![](https://i-blog.csdnimg.cn/blog_migrate/4ec90d538f0d423b692d65595eb31e14.png)
安装之前,需要修改redis.conf文件中的bind为:0.0.0.0(之前已经修改过了)
下载地址: https://github.com/lework/RedisDesktopManager-Windows
安装之后,输入IP地址登录即可看到如下界面:
![](https://i-blog.csdnimg.cn/blog_migrate/a0852f3d7965689f823236467f251ac3.png)
十、Redis中的持久化【重点】
Redis有两种持久化方式:RDB和AOF。
1.RDB(Redis DataBase)
将内存中的数据以快照(效率较高)的方式写入磁盘中,在redis.conf文件中,我们可以找到如下配置:
![](https://i-blog.csdnimg.cn/blog_migrate/fa71b8bd416f39fcacf8dc5b09dcdc58.png)
![](https://i-blog.csdnimg.cn/blog_migrate/aeaf7342c9bb53a98a5c8a7084685a35.png)
save 900 1
save 300 10
save 60 10000
配置含义:
900秒内,如果超过1个key被修改,则发起快照保存
300秒内,如果超过10个key被修改,则发起快照保存
60秒内,如果1万个key被修改,则发起快照保存
RDB方式存储的数据会在
dump.rdb文件中(在哪个目录启动redis服务端,该文件就会在对应目录下生成),该文件不能查看,如需备份,对Redis操作完成之后,只需拷贝该文件即可(Redis服务端启动时会自动加载该文件)
2.AOF(Append Of File)
AOF默认是不开启的,需要手动开启,同样是在redis.conf文件中开启,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/772b1a8d98585db09857fc79c7c7e9ff.png)
配置文件中的appendonly修改为yes,开启AOF持久化。开启后,启动redis服务端,发现多了一个appendonly.aof文件。
![](https://i-blog.csdnimg.cn/blog_migrate/fa33846f11f0bd676bbae5b8acfacc65.png)
之后任何的操作都会保存在appendonly.aof文件中,可以进行查看,Redis启动时会将appendonly.aof文件中的内容执行一遍。
如果AOF和RDB同时开启,系统会默认读取AOF的数据。
3.总结
RDB优点与缺点
优点
如果要进行大规模数据的恢复,RDB方式要比AOF方式恢复速度要快。
RDB是一个非常紧凑(compact)的文件,它保存了某个时间点的数据集,非常适合用作备份,同时也非常适合用作灾难性恢复,它只有一个文件,内容紧凑,通过备份原文件到本机外的其他主机上,一旦本机发生宕机,就能将备份文件复制到redis安装目录下,通过启用服务就能完成数据的恢复。
缺点
RDB这种持久化方式不太适应对数据完整性要求严格的情况,因为,尽管我们可以用过修改快照实现持久化的频率,但是要持久化的数据是一段时间内的整个数据集的状态,如果在还没有触发快照时,本机就宕机了,那么对数据库所做的写操作就随之而消失了并没有持久化本地dump.rdb文件中。
AOF优点与缺点
优点
AOF有着多种持久化策略:
appendfsync always:每修改同步,每一次发生数据变更都会持久化到磁盘上,性能较差,但数据完整性较好。
appendfsync everysec: 每秒同步,每秒内记录操作,异步操作,如果一秒内宕机,有数据丢失。
appendfsync no:不同步。
AOF文件是一个只进行追加操作的日志文件,对文件写入不需要进行seek,即使在追加的过程中,写入了不完整的命令(例如:磁盘已满),可以使用redis-check-aof工具可以修复这种问题
Redis可以在AOF文件变得过大时,会自动地在后台对AOF进行重写:重写后的新的AOF文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为Redis在创建AOF文件的过程中,会继续将命令追加到现有的AOF文件中,即使在重写的过程中发生宕机,现有的AOF文件也不会丢失。一旦新AOF文件创建完毕,Redis就会从旧的AOF文件切换到新的AOF文件,并对新的AOF文件进行追加操作。
缺点
对于相同的数据集来说,AOF文件要比RDB文件大。
根据所使用的持久化策略来说,AOF的速度要慢于RDB。一般情况下,每秒同步策略效果较好。不使用同步策略的情况下,AOF与RDB速度一样快。
十一、Redis的主从复制 【重点】
一台Redis可能满足不了我们的需求,因为访问量太大了,存的数据过多。如果这个Redis承受不了压力,因为某些问题宕机了,那么数据都会到MySQL里,这个时候如果MySQL也承受不了压力,那系统就完了,这个时候得用集群
集群分两种,一种就是现在说的主从复制,这里面有主机有从机(从机分担主机压力)
主机用来读和写,从机主要用来读。在实际开发中,查询操作是最多的,你去网站里面基本都是查东西,注册的话可能,增删改可能就是对自己资料进行修改,对当前用户的一个增加。网站里面基本上没什么删除功能,因为网站人家开发的,你也不能登录人家项目的后台,所以也没有什么删除的操作
主从复制是指将一台Redis服务器的数据,复制到其它的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点,且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
![](https://i-blog.csdnimg.cn/blog_migrate/f14b37875c0cd8c73d712a870e3fac97.png)
主从复制的作用:
1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2.故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复,但实际上是一种服务的冗余.
3.负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4.高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础.
进行伪代码演示,一台服务器演示3台。如果你有3台服务器,那就在不同服务器做对应操作。在公司搭建集群也是得有好几台才能搭建,跟现在演示的有区别(一台服务器上搭建100台也是一台的性能,要注意哦)因为没有多台服务器,所以这里进行模拟(正常一台服务器要买一台PC机)
配置步骤:
1.查看当前库的信息:
127.0.0.1:6379> info replication
![](https://i-blog.csdnimg.cn/blog_migrate/b5942e253cf2445ba53ca3c30ee3cf9e.png)
2.关闭当前运行的redis,打开四个链接,进行测试,拷贝三个redis.conf文件(改成6379,6380,6381)
![](https://i-blog.csdnimg.cn/blog_migrate/4784bf0a15aed4d6455240036174b5a7.png)
3.分别修改这三个文件信息,需要修改:端口,pid名字,log文件名字,dump.rdb名字
![](https://i-blog.csdnimg.cn/blog_migrate/339cd02e995e9cb8beb5e1ec6a7ce1c7.png)
port 6379
pidfile /var/run/redis_6379.pid
logfile "6379.log"
dbfilename dump6379.rdb
port 6380
pidfile /var/run/redis_63780pid
logfile "6380.log"
dbfilename dump6380.rdb
port 6381
pidfile /var/run/redis_6381.pid
logfile "6381.log"
dbfilename dump6381.rdb
4.分别在三个链接中启动6379,6380,6381三台redis-server,在第四个链接中查看
![](https://i-blog.csdnimg.cn/blog_migrate/885ef1771637d476e0ff55a5ad25395b.png)
查看状态
![](https://i-blog.csdnimg.cn/blog_migrate/aae2325a2ef18633c8805acf9124047c.png)
5.配置一主二从
默认情况下,每台Redis都是主节点,我们只需要配置从机即可,我们这里使用6379为主机,6380和6381为从机.
在对应6380的链接中进行配置:
[root@localhost bin]# ./redis-cli -p 6380
127.0.0.1:6380> slaveof 127.0.0.1 6379
![](https://i-blog.csdnimg.cn/blog_migrate/f19d9ee15a95fbf6ec767fd5f7a18387.png)
6380从机配置成功之后,可以去主机中查看对应信息
![](https://i-blog.csdnimg.cn/blog_migrate/77597766ab4ed91af8b4ba33fe9ab289.png)
6381从机配置方式和6380一样,配置成功之后可以再去看一下主机
![](https://i-blog.csdnimg.cn/blog_migrate/3e0fb27c7ef68a6c4ea951cb71267e7e.png)
6.由于这里使用的是命令进行配置,所以是暂时的,一般公司配置会在配置文件中进行配置,属于永久性配置,相当于一打开当前服务器,该服务器就是从机,一般主机可以写,从机不能写只能读,主机中的所有信息和数据都会被从机保存!
![](https://i-blog.csdnimg.cn/blog_migrate/ec435bb09ff49fa986e7649a29b4faf6.png)
即使主机断开链接(127.0.0.1:6379>shutdown),从机仍然可以连接到主机,如果使用的是命令行配置的从机,从机一旦断开链接后,就会变回主机了,如果再次变回从机,仍旧可以获取主机中的值.
如果主机断开链接,从机可以使用命令:127.0.0.1:6380>slaveof no one 使自己成为主机
7.主从复制原理
Slave启动成功连接到master后会发送一个sync同步命令,Master接到命令后,会启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕后,master将传送整个数据文件到salve,并完成一次完整的同步.
全量复制:salve服务在接收到数据库文件数据后,将其存盘并加载到内存中.
增量复制:master继续将新的所有收集到的修改命令依次传递给salve,完成同步.
如果是配置文件配置,可直接在对应的redis.conf文件中添加: slaveof 127.0.0.1 6379 (和命令一样)
十二、Redis的哨兵模式【重点】
当主服务器宕机后,并且我们并没有及时发现,这时候就可能会出现数据丢失或程序无法运行。此时,redis的哨兵模式就派上用场了,可以用它来做redis的高可用.
每个哨兵都是监控主节点,主节点宕机,哨兵不会宕机!
功能作用:
1.监控(monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
2.提醒(Notifation):当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
3.自动故障转移(Automatic failover): 最重要的功能 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
配置步骤:
1.创建哨兵配置文件
[root@localhost bin]# vim sentinel.conf
sentinel.conf文件内容如下:(格式:sentinel monitor 被监控名称 host port 1)
sentinel monitor myredis 127.0.0.1 6379 1
1表示集群数量,会以投票的方式认为是否宕机了
注意:需要修改redis.conf文件中的bind为:0.0.0.0(之前已经修改过了),如果是三台阿里云服务器,每个服务器都要设置sentinel.conf文件并启动哨兵,127.0.0.1改成主机的IP地址,(从机也是监控主机IP,相当于三台服务器的sentinel.conf的内容都是一样的:sentinel monitor myredis 主机IP地址 6379 1),后面的数字1表示有1个sentinel认为一个master失效时,master就算真正失效,slave会以投票的方式选举成为主机,一般设置的值为从机数量一半以上,比如说:三个从机就设置为:2
![](https://i-blog.csdnimg.cn/blog_migrate/fee0242469018b74329e6d91553e8859.png)
2.启动哨兵
[root@localhost bin]# ./redis-sentinel sentinel.conf
![](https://i-blog.csdnimg.cn/blog_migrate/13fe3aebaaff203bf20409036a8b13b2.png)
3.如果Master节点断开了(主机宕机了),过一会,会发送哨兵日志,并自动通过算法在其他两个从机中选择一个成为主机.
![](https://i-blog.csdnimg.cn/blog_migrate/6e4bbfde8def2b0cfe501a4658f2afdf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6ca94162e0ecb5c799ea60a606d1dee7.png)
宕机不会立马就发现,就像有人躺地上,我得先拿个树枝去捅几次,看动不动,确定是不是真不动了
他会在剩余的选手里面挑一个他认为不错的人来作为主机
如果之前的主机6379又重新启动了,过一会,哨兵检测到了之后,会把6379设置为从机!
哨兵模式的优缺点
优点
1.哨兵集群模式是基于主从模式的,所有主从的优点,哨兵模式同样具有。
2.主从可以切换,故障可以转移,系统可用性更好。
3.哨兵模式是主从模式的升级,系统更健壮,可用性更高。
缺点
1.Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
2.实现哨兵模式的配置也不简单,甚至可以说有些繁琐
注意:
在一主两从模式中,正常来说,三个redis中都写 sentinel.conf 文件,然后主机 redis 中 sentinel.conf 文件的内容是:sentinel monitor myredis 127.0.0.1 6379 2 ,其他两个从机的 redis 中的 sentinel.conf 文件的内容是:sentinel monitor myredis 主机ip地址 6379 2
十三、Redis缓存穿透,击穿,雪崩,倾斜
一般情况下我们都会避免这些问题出现,如果公司出现这些问题影响是非常大的
1.1 缓存穿透(查不到)
一个用户查不到还好,所有都查不到问题就大了,换句话来说Redis没有存,压力全来到了MySQL这边
概念:当用户去查询数据的时候,发现redis内存数据库中没有,于是向持久层数据库查询,发现也没有,于是查询失败,当用户过多时,缓存都没有查到,于是都去查持久层数据库,这会给持久层数据库造成很大的压力,此时相当于出现了缓存穿透。
![](https://i-blog.csdnimg.cn/blog_migrate/bddbf2c83ad71877e8d0456aadcda342.png)
查不到就是数据库没有:
第一个人查没有,说明数据库也没有,那我们直接返回一个空就行了。如果是单个对象那我们就返回一个空或者空对象,就是我对象有实例,但是对象里没有东西。如果是集合我们可以返回一个空的集合
要不就去比对一下数据库的id
举个例子:数据库里id是1到100,但是你查的是101,那我数据库肯定没有,那直接返回一个空就行了,你也别查我数据库了。在程序里就比对id,如果超过了,就继续往下运行,直接返回空就行了
遇到这些问题,首先要考虑的就是不让他过多的去访问数据库,去查MySQL,想办法在Redis这层就解决问题,不让他去数据库里查了
解决方案:
1.布隆过滤器:是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的压力,但是,布隆过滤器有误差,说myql 有该id ,不一定有,说myql 没有该id,一定没有
![](https://i-blog.csdnimg.cn/blog_migrate/239032f861ef78917f59f560476039d5.png)
2.缓存空对象:当存储层查不到时,即使返回的空对象也将其缓存起来,同时设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护后端数据.
![](https://i-blog.csdnimg.cn/blog_migrate/fced828c2f3913c4eea4f1b5f3dc8bd0.png)
但会有两个问题:
1.如果空值被缓存起来,就意味着需要更多的空间存储更多的键,会有很多空值的键.
2.即使对空值设置了过期时间,还是会存在 缓存层和存储层会有一段时间窗口不一致,这对于需要保持一致性的业务会有影响.
思考一下:如何保持Redis和MySQL数据一致性
有一些数据可能没存到缓存里面,也有可能存进去了,但是没人去查,这个key就过期了
你去查的时候你会去MySQL里面查,然后放到Redis里面
第一次查第二次查速度肯定不一样的
就会有这样的一个问题:
设置了一个过期时间,但是Redis里面数据有一段时间跟MySQL里面是不一样的
就跟MyBatis的一二级缓存类似
一级缓存的话 SqlSession级别的缓存,如果两次查询都一样,在这查询期间有人改了数据库数据,那么第二次查出来的就是一个脏数据
对于Redis来说也是一样的
共有十件商品,然后存到Redis里面。 紧接着我又进了一件货,这个时候后台就有11件,但是我们查的时候Redis里面是有问题的,只有10件,之前的key还没过期
这个时候得考虑一致性了
当再去添加的时候,可以给缓存删掉,然后再重新加入缓存 这样下次再查就是11件了
因为后台对对应的key进行操作删除,操作完再往Redis里面添加
还有呢,对业务要求不是很严格
举个例子:
某个网页更新了,但是页面功能基本是不变的,页面会有一些更新,可能是登录加了个验证码功能
第一次登录可能要输入新加的功能,也可能没有
第二次就需要了
不需要让Redis和MySQL立即保持一致,而是要求一段时间后保持一致
要想到Redis和MySQL短时间内数据不一致对自己的业务有没有影响
1.2 缓存击穿(访问量大,缓存过期)
穿透呢是查不到,击穿是热门商品
本来我们正常访问是没问题的,但是秃然一个商品爆火 ,大家都来访问这个商品
正常情况下我的并发量还是可以的,因为人数暴涨,导致我的服务器承受不了这个并发
导致直接宕机
访问人太多,卡死了
指对某一个key的频繁访问,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就会直接请求数据库,就像在一个屏障上凿开了一个洞,例如咱们熟悉的新浪微博(PHP写的,抗并发能力不强)由于某个热搜导致宕机.
其实就是:当某个key在过期的瞬间,有大量的请求并发访问,这类数据一段是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并回写缓存,导致数据库瞬间压力过大。
![](https://i-blog.csdnimg.cn/blog_migrate/a54669ac5bd3024c2bb6ec0d9c0a4901.png)
解决方案:
1.设置热点数据永不过期:从缓存层面上来说,不设置过期时间,就不会出现热点key过期后产生的问题(但是爆款千千万。不可能一直火,也会有新的爆款导致key越来越多。 确定当前数据不会变使用即可).
2.添加互斥锁:使用分布式锁,保证对每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁上,对分布式锁也是一种极大的考验(不推荐,因为影响效率).
1.3 缓存雪崩
击穿可能是一个热点商品过期,雪崩就相当于所有热点商品突然都过期了,这同样每一个热点商品都会有很多请求去访问,导致大面积的请求到了MySQL,这时候比较危险
指在某一个时间段,缓存集中过期失效或Redis宕机导致的,例如双十一抢购热门商品,这些商品都会放在缓存中,假设缓存时间为一个小时,一个小时之后,这些商品的缓存都过期了,访问压力瞬间都来到了数据库上,此时数据库会产生周期性的压力波峰,所有的请求都会到达存储层,存储层的调用量暴增,造成存储层挂掉的情况.
![](https://i-blog.csdnimg.cn/blog_migrate/3efd839645398194a1afb808c167081e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/49d2b441e0d8ce03578c83c14218cca0.png)
其实比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网,因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,此时的数据库还是可以顶住压力的,而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,有可能瞬间就把服务器压垮.
解决方案:
1.配置Redis的高可用:其实就是搭建集群环境,有更多的备用机.
2.限流降级:在缓存失效后,通过加锁或者队列来控制读服务器以及写缓存的线程数量,比如对某个key只允许一个线程查询数据和写缓存,其他线程等待( 同一时间访问的人很多,那就让一部人能访问到,一部人访问不到,同时可以给一个当前访问过多稍后访问的回馈).
3.数据预热:在项目正式部署之前,把可能用的数据预先访问一边,这样可以把一些数据加载到缓存中,在即将发生大并发访问之前手动触发加载缓存中不同的key,设置不同的过期时间,让缓存失效的时间尽量均衡( 自己提前先跑一遍,因为是一个人所以肯定没问题,提前把MySQL里面的东西存到Redis里,别人访问的都是Redis里的东西了).
雪崩是比较致命的问题,一般也不会让他出现避免这种情况。会导致多台服务器都宕机,你启动都启动不过来,这台Redis刚宕机,紧接着下一台也不行了,刚把这个启动了,又来访问它,就又不行了
1.4 缓存倾斜
配置了两台,轮询访问就行了,不要都只访问其中一台,所以在用户访问的时候,一定要设置轮询的操作或者是负载均衡
Nginx会帮我们解决这个问题,访问的时候不是直接访问服务器了,会先访问Nginx,Nginx会把请求分散到不同的服务器上面,不同的服务器连不同的Redis
指某一台redis服务器压力过大而导致该服务器宕机.
![](https://i-blog.csdnimg.cn/blog_migrate/d8f42c5fdee6350d9d3b30a7d332853a.png)
十四、Redis集群搭建(自己动手,丰衣足食)
![](https://i-blog.csdnimg.cn/blog_migrate/b18c45a30aff539fb9755bf4be06eff8.png)
十五、Redis其他常见问题
1.key的生存时间到了,Redis会立即删除吗?
不会立即删除
可能有成千上百到期的key,不可能到一个,删一个,这样效率太低了
可能设置了数量多少个之后一起删
1.1定期删除:Redis每隔一段时间就去会去查看Redis设置了过期时间的key,会再100ms的间隔中默认查看3个key。
1.2惰性删除:如果当你去查询一个已经过了生存时间的key时,Redis会先查看当前key的生存时间,是否已经到了,直接删除当前key,并且给用户返回一个空值。
2.Redis的淘汰机制
在Redis内存已经满的时候,添加了一个新的数据,执行淘汰机制。(redis.conf中配置)
2.1 volatile-lru:在内存不足时,Redis会在设置过了生存时间的key中干掉一个最近最少使用的key。
2.2 allkeys-lru:在内存不足时,Redis会在全部的key中干掉一个最近最少使用的key。
2.3 volatile-lfu:在内存不足时,Redis会在设置过了生存时间的key中干掉一个最近最少频次使用的key。
2.4 allkeys-lfu:在内存不足时,Redis会在全部的key中干掉一个最近最少频次使用的key。
2.5 volatile-random:在内存不足时,Redis会在设置过了生存时间的key中随机干掉一个。
2.6 allkeys-random:在内存不足时,Redis会在全部的key中随机干掉一个。
2.7 volatile-ttl:在内存不足时,Redis会在设置过了生存时间的key中干掉一个剩余生存时间最少的key。
2.8 noeviction:(默认)在内存不足时,直接报错。
方案:指定淘汰机制的方式:maxmemory-policy具体策略,设置Redis的最大内存:maxmemory 字节大小