Redis

概述

官网:https://redis.io/
中文网:http://www.redis.cn/
推荐都是在Linux环境下安装
Redis是什么?
Redis [Remote Dictionary Server] 即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API.免费且开源,是当下最热门的NoSQL技术之一,也被称为结构化数据库

Redis能干嘛?
1,内存存储,持久化(包含RDB和AOF)
2,效率高,可以用于告诉缓存
3,可以做简单的发布订阅系统
4,地图信息分析
5,计时器,计数器(浏览量…)
6…

特性
1,多样的数据类型
2,持久化
3,集群
4,事务

安装Redis

windows版本

Github链接:https://github.com/tporadowski/redis/releases
压缩包很小,解压之后也只有46M,完整目录如下:
在这里插入图片描述
打开服务端之后界面如下:
/**********************
Redis默认端口号6379,保持服务端开启状态下再打开Redis-cli.exe客户端,客户端是一个类似的cmd命令窗口,输入ping后出现pong代表连接成功:
在这里插入图片描述
通过上面简单的set/get测试不难看出,这种存储就像Map集合中的key/value键值对的模式,但是Redis可存储的明显不可能只是这种字段.
另外关于windows版本的Linux官网有这么一段原话:

Redis 使用 ANSI C 编写并且能在绝大Linux系统上运行,基于BSD协议,对OS X没有外部依赖. 我们支持Linux 和 OS X两种系统的开发和测试,我们推荐使用Linux部署. Redis 可以像SmartOS一样运行在Solaris系统中, 但是我们会最大力度的支持它. 官方不支持Windos版本的Redis,但微软开发和维护着支持win-64 的Redis版本.

Linux版本

直接官网或者中文网下载就好,中文网版本可能低一点.有宝塔直接跳过就好,大致也就是下载个tar.gz文件然后上传到服务器,然后tar -zxvf [压缩包],选中然后解压即可.解压完毕之后还要安装c++环境才可以运行,毕竟Redis是c写的

1.yum install gcc-c++
2.make #自动配置相关文件(第一次需要几分钟时间),如果时间短,说明已经配好了
3.make install #确认是否配置完成

注意,使用宝塔安装时以上配置都已自动生成,解压文件一般默认放在/www/server目录下
在这里插入图片描述

宝塔安装下Redis运行及停止命令:

/etc/init.d/redis start
/etc/init.d/redis stop

在这里插入图片描述
Redis默认客户端安装位置:/usr/local/bin
在这里插入图片描述
宝塔默认安装位置: /www/server/redis/src

如果不使用宝塔可以新建一个配置文件,并将原来默认的配置文件复制到新文件中,然后就可以使用新的配置文件进行启动,原来的配置文件保留即可.
vim打开配置文件redis.conf首先修改为默认后台启动,(宝塔已经修改好了)然后:wq!退出
在这里插入图片描述
使用宝塔安装时客户端和服务端启动在/www/server/redis/src目录里面
并且要使用./redis-server来进行启动 客户端一样没有./启动不了
没有宝塔也可以使用配置文件启动服务端 /usr/local/bin redis-server
在这里插入图片描述
开启服务端之后就不用动了,运行期间会自动打印操作日志.接下来是redis-cli客户端
在这里插入图片描述
可检查进程查看是否开启:
在这里插入图片描述
然后接下来就是关闭redis了,shutdown关闭客户端的连接,显示未连接后输入exit退出

再次查看进程确认是否关闭
在这里插入图片描述

redis-benchmark性能测试

redis-benchmark是官方自带的性能测试工具,菜鸟教程上的操作命令如下:
在这里插入图片描述

redis基本知识

redis默认有16个数据库,可以在redis配置文件中看到并修改,默认使用第0个,数据库之间相互独立存储.

select [0~15] #切换到指定数据库
DBSIZE #查看数据库大小

还可以输入指令flushdb清空当前数据库,FLUSHALL清空所有数据库
在这里插入图片描述
注意:Redis对网络 IO 和数据读写的操作采用单线程操作
redis的持久化,异步删除和集群数据同步采用的额外线程
redis是基于内存操作的,CPU并不是Redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽.
使用单线程可以避免多线程开发的并发控制问题.cpu中多线程的上下文切换[指一个进程从执行到暂停再到执行的过程]操作耗时,单线程效率是最高的
为什么redis单线程还那么快?
redis大部分操作都在内存上+高效的数据结构
redis采用多路复用机制,在网络中处理大量客户端请求,实现高吞吐率

五大数据类型

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库缓存消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

Redis-key基本命令

keys* 查看所有键名
EXISTS+[键名] 判断是否存在键值
在这里插入图片描述
del+[键名] 从当前数据库删除键值对
move+[键名]+[数据库] 移动到指定键值对(1代表指定数据库)(不能原地tp)
在这里插入图片描述
expire+[键名]+[秒数] 为指定键值对设置过期时间
(ttl+[键名]可查看指定键值对剩余过期时间)
在这里插入图片描述
type+[键名] 查看key对应的value类型
在这里插入图片描述

String字符串类型

append+[key]+[字符串] 在value后面添加指定字符串(双引号可有可无)
注意:如果key不存在,就相当于创建了一个新的键值对
在这里插入图片描述
incr+[键名] 监控指定键值对自增
decr+[键名] 监控指定键值对自减
在这里插入图片描述
还可以设定每次自增自减的步数
在这里插入图片描述
还有关于字符串相关操作的getrange
getrange+[key]+[初始下标]+[结尾下标] 截取字符串(负数代表倒数第几个)
setrange+[key]+[下标]+[指定字符串] 将下标位置的值替换为指定字符串
在这里插入图片描述
setex(set with expire) 设置过期时间(可用于设置分布式锁)
setnx(set with not expire) 不存在再设置

setex+[key]+[过期时间] 设置一个值并指定过期时间
setnx+[key]+[value] 如果key不存在则创建key并指定value

在这里插入图片描述
mset/mget批量设置/获取键值对 (具有原子性,要么一起成功,要么一起gg)
可以和nx搭配使用,但是单个键值对执行出错时会全部执行失败
在这里插入图片描述
同时还可以使用redis进行对json字符串对象的巧妙储存
在这里插入图片描述
这一段一开始还没想到有什么特别的,看了一个弹幕才发现这样一来只需要传递一个特定属性就可以查看需要的内容,不需要传递一整个json对象
getset组合命令
getset+[key]+[value] 获取key对应的value,获取之后再更改为指定value
类似于java基础运算中的+=和=+,可以用在更新的操作上面
在这里插入图片描述
string数据类型一般可以用在计数器或者多单位数量统计,还有对象缓存存储等多种应用场景中

List类型

list列表类似于一个双向的管子,可以从左右两边往里面输入不同/相同的数据,先放的就在最里面,也就只能最后拿到
只有push和pop以"L"或者"R"开头的,代表从左边或者从右边进行插入/删除,其他的命令中L只代表list
lpush/rpush+[列表名]+[数据] 从左侧或者右侧插入数据
lrange+[列表名]+[首字符下标]+[结尾下标] 查询特定范围内数据
在这里插入图片描述
lpop/rpop+[列表名]+[数字] 删除列表中从左/右开始数的几个值,2就是删除从一侧数的前两个值
执行之后会自动显示删除了哪几个值
在这里插入图片描述
lindex+[列表名]+[下标] 获取指定下标的值(注意,这里没有什么rindex)
llen+[列表名] 获取数组长度
在这里插入图片描述

lrem+[列表名]+[指定个数]+[指定值] 删除列表中指定个数的值
在这里插入图片描述
LTRIM+[列表名]+[首元素下标]+[结尾字符下标] 截取指定范围内容
列表只保留指定区间内的元素,不在指定取件内的元素都将被删除
在这里插入图片描述
组合命令
rpoplpush+[列表1]+[列表2] 移除列表1的最右侧元素并将其添加到列表2最左侧(可以表1和表2可以为同一个)
在这里插入图片描述
lset+[列表名]+[下标]+[元素] 将指定列表按照指定下标替换(更新)元素
注意:如果没有当前列表,或者当前列表下标值为空则无法执行

linsert+[列表名]+before/after+[元素A]+[元素B] 在指定列表的指定元素A前面或者后面添加元素B
在这里插入图片描述
list是一个双向链表,根据使用方法的不同,从左边进来右边出去就可以看做是一个消息队列,从左边进来左边出去就是一个栈

Set(集合)

set为无序不重复集合
sadd+[列表名]+[元素] 为指定set列表添加元素(添加多个元素用空格隔开)
smembers+[列表名] 展示set列表
smembers+[列表名]+[元素] 查看列表内是否有该元素,有就是1
在这里插入图片描述
scard+[列表名] 返回列表内元素个数
srem+[列表名]+[元素] 移除set集合中的指定元素
在这里插入图片描述
srandmember+[列表名] 在set列表中随机抽一个元素
srandmember+[列表名]+[数字] 在set列表中随机抽取指定个数的元素
注意,计算机的随机都是伪随机
在这里插入图片描述
spop+[列表名]+[数字] 随时移除指定个数的元素
在这里插入图片描述
smove+[列表1]+[列表2]+[元素]将列表1中的指定元素剪切复制到列表2
在这里插入图片描述
sdiff+[列表A]+[列表B] 返回表A不同于表B的元素
在这里插入图片描述
sinter+[列表A]+[列表B] 返回AB之间的相同元素(交集)
可以使用此命令来实现显示共同关注之类的功能
在这里插入图片描述
sunion+[列表A]+[列表B] 返回AB之间的所有元素,重复不显示(并集)
在这里插入图片描述

Hash

可以看做是一个Map集合,但是它的key对应的value本身也可以视为一个Map集合,也可以储存数据也就是key-map[key-value]
并且和上面一样hash的指令是以H开头的
hset+[表名]+[字段名]+[字段值] 为指定的表添加数据
hget+[表名]+[字段名] 在根据指定表中的字段获取数据
另外还有批量添加hmset,批量获取hmget,用空格间隔开
在这里插入图片描述在这里插入图片描述
hdel+[表名]+[字段名] 删除hash表中的指定字段
hgetall+[表名] 获取hash表中的所有字段和字段值
hlen+[表名] 获取hash表中的字段数(长度)
hexists+[表名]+[字段名] 判断表中是否存在该字段
hkeys+[表名] 返回表中所有的字段
hvals+[表名] 返回表中所有的字段值在这里插入图片描述
hincrby+[表名]+[字段名]+[数字] 为表中字段增加指定数字(可以为负数)
在这里插入图片描述
hsetnx+[表名]+[字段名]+[字段值] 检查表中指定字段是否存在,不存在就创建
在这里插入图片描述
hash用的最多的场景就是用来变更数据,尤其是用户信息之类
相对于string,hash更适合于对象的存储,string更加适合字符串的存储

Zset(有序集合)

其实就是在set的基础上增加了一个值,
set集合 set k1 v1
Zset集合 zset k1 score1 v1
zrange+[集合名]+[首字符下标]+[末字符下标] 输出指定范围的元素
zrangebyscore+[集合名]+[特定最小值]+[特定最大值] 将指定集合按照从小到大排列
注意,这里一般取负无穷(-inf)到正无穷(+inf)
zrevrangebyscore+[集合名]+[特定最大值]+[特定最小值] 从大到小排列
还可以在结尾加上withscores来输出具体的分数

127.0.0.1:6379> zadd salary 2500 A
(integer) 1
127.0.0.1:6379> zadd salary 5000 B
(integer) 1
127.0.0.1:6379> zadd salary 200 C
(integer) 1
127.0.0.1:6379> zrange salary 0 -1 #按照指定下标范围输出,默认从小到大
1) "C"
2) "A"
3) "B"
127.0.0.1:6379> zrevrange salary 0 -1 #按照指定下标范围输出,默认从小到大
1) "C"
2) "A"
3) "B"
127.0.0.1:6379> zrangebyscore salary -inf +inf  #按照分数从小到大输出
1) "C"
2) "A"
3) "B"
127.0.0.1:6379> zrevrangebyscore salary +inf -inf #按照分数从大到小输出
1) "B"
2) "A"
3) "C"
127.0.0.1:6379> zrangebyscore salary -inf 4000 #指定范围输出
1) "C"
2) "A"
127.0.0.1:6379> zrangebyscore salary -inf 4000 withscores
1) "C"
2) "200"
3) "A"
4) "2500"
127.0.0.1:6379> zrem salary C #删除指定元素
(integer) 1
127.0.0.1:6379> zcard salary #查看元素个数
(integer) 2

zrem+[集合名]+[元素名] 删除指定元素
zcard+[集合名] 查看集合内元素个数
zcount+[集合名]+[最小值]+[最大值] 查看集合指定区间内的成员数量

127.0.0.1:6379> zadd myset 1 A
(integer) 1
127.0.0.1:6379> zadd myset 2 B
(integer) 1
127.0.0.1:6379> zadd myset 3 C
(integer) 1
127.0.0.1:6379> zcount myset 1 3
(integer) 3

可以用于排行榜应用实现,或者班级成绩表,工资表等

三种特殊数据类型

geospatial 地理位置

redis的Geo在redis3.2版本就已经推出了,这个功能可以推算出地理位置的信息, 两地之间的距离,特定范围内的人
geoadd+[集合名]+[经度]+[纬度]+[位置名称] 在集合中添加指定经纬度的地理位置
注意: 地球两极无法直接添加,一般会下载城市数据,通过java程序一次性导入
有效经度从-180~180
有效纬度从-85.05112878~85.05112878
超过范围就报错

127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2

geopos+[集合名]+[位置名称] 获取指定位置的经纬度坐标
返回的值与存的值不一样是因为浮点数在计算机的表示中会有误差

127.0.0.1:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city chongqing
1) 1) "106.49999767541885376"
   2) "29.52999957900659211"
127.0.0.1:6379> geopos china:city nanjing
1) (nil)

geodist+[集合名]+[地址1]+[地址2] 返回两地之间的距离

127.0.0.1:6379> geodist china:city beijing shanghai
"1067378.7564"

georadius+[集合名]+[经度]+[纬度]+[数字]+[距离单位] 以给定的经纬度为中心,找出某一半径内集合包含的元素
单位可以是米/(m)千米(km)/英里(mi)/英尺(ft)
同时可以添加附加指令
withcoord 并返回坐标
withdist 返回直线距离
count 限定数量

127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqing"
2) "xian"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "xian"
   2) 1) "108.96000176668167114"
      2) "34.25999964418929977"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist
1) 1) "chongqing"
   2) "341.9374"
2) 1) "xian"
   2) "483.8340"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist count 1
1) 1) "chongqing"
   2) "341.9374"

上面那个指令是给定具体坐标,接下来是给定集合内元素,然后返回元素特定范围内集合包含的元素
georadiusbymember+[集合名]+[元素]+[数字]+[单位]

127.0.0.1:6379> georadiusbymember china:city beijing 1200 km
1) "beijing"
2) "hangzhou"
3) "shanghai"
4) "xian"

geohash+[集合名]+[元素] 返回集合内给定元素坐标的geohash字符串
用处比较少

geohash china:city shanghai
1) "wtw3sj5zbj0"

geo底层实现原理其实就是Zset,我们可以使用Zset命令来操作geo

127.0.0.1:6379> zrange china:city 0 -1
1) "AAA"
2) "chongqing"
3) "xian"
4) "shenzhen"
5) "hangzhou"
6) "shanghai"
7) "beijing"
127.0.0.1:6379> zrem china:city AAA
(integer) 1

Hyperloglog 基数统计

基数就是集合内不重复的元素的个数
Hyperloglog就是用来做基数统计的算法
比如网页的访问量(UV),一个人访问同一网站多次,还是算作一个访问量
传统是使用set保存用户的id,重复访问覆盖数据,然后计算set元素数量,但是这样会保存大量id,保存起来浪费资源,
而使用hyperloglog占用的内存是固定的,2^64不同元素只需要12KB内存
hyperloglog有0.81%的错误率,但是在访问量这种没有精确需求的应用上面是可以接受的,如果不允许容错可以使用set或者其他特定数据类型
pfadd+[集合名]+[元素] 创建一个集合并添加元素
pfcount+[集合名] 返回集合内基数数量
pfmerge+[新建集合]+[集合1]+[集合2] 将集合1和集合2的元素(重复的只统计一个)合并至新建集合

127.0.0.1:6379> pfadd mykey a b c d e f g 
(integer) 1
127.0.0.1:6379> pfcount mykey
(integer) 7
127.0.0.1:6379> pfadd mykey2 f g k h i s s a 
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 7
127.0.0.1:6379> pfmerge mykey3 mykey mykey2
OK
127.0.0.1:6379> pfcount mykey3
(integer) 11

Bitmaps 位图场景

位存储,类似于二进制一样,只有0和1两个状态,可以用来储存类似打卡之类的状态数据,
setbit+[表格名]+[数字批号]+[状态] 为表中指定次数批号设置状态
注意:批号只能是数字,状态也只有0和1

127.0.0.1:6379> setbit sign A 1
(error) ERR bit offset is not an integer or out of range
127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 2
(error) ERR bit is not an integer or out of range

getbit+[表格名]+[数字批号] 查看指定批次状态(当天是否打卡)
bitcount+[表格名]+[开始批次]+[结束批次] 查看在指定批次时间端内记录多少次(查看指定时间段内总共打了几天卡)

不加开始和结束批次就默认查看所有的打卡天数

127.0.0.1:6379> getbit sign 2
(integer) 0
127.0.0.1:6379> getbit sign 1
(integer) 1
127.0.0.1:6379> bitcount sign 
(integer) 1
127.0.0.1:6379> bitcount sign 1 4
(integer) 0

使用场景还有统计用户信息,例如不登录就为0登录了就为1,如果时间长了就设置为不活跃用户,当然,最多的还是打卡

事务(命令集)

首先回顾一个概念,原子性,要么同时成功要么同时失败.
redis中单条命令是原子性的,但是redis事务不保证原子性
redis事务本质就是一组命令的集合,事务中的所有命令都会被序列化,在事务执行的过程中会按照顺序依次执行,并且不允许干扰
具有一次性,顺序性,排他性
redis中没有隔离级别的概念
所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会被执行.命令为 Exec
redis事务的三个阶段:
开启事务(multi)
命令入队(自动入队…)
执行事务(exec)

正常执行事务流程:

127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379(TX)> set k1 v1 #添加命令
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec #执行事务
1) OK
2) OK
3) "v2"
4) OK

可以看到,redis事务中,命令入队的时候不会执行,开始执行事务命令的时候才会统一按照顺序执行
discard放弃事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> discard #取消事务
OK
127.0.0.1:6379> get k1
(nil)

取消事务之后,事务中的所有命令都不会被执行

事务异常处理
redis事务异常与java异常类似,分为两种

编译型异常(写错命令),事务中的所有命令都不会被执行

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> decrdy money 10 #decrby故意拼错
(error) ERR unknown command `decrdy`, with args beginning with: `money`, `10`, 
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
#翻译:由于以前的错误,已放弃Executor事务。

运行时异常,如果事务队列中存在逻辑型错误,那么执行命令的时候,其他命令可以正常执行

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1 #这是一个字符串
QUEUED
127.0.0.1:6379(TX)> incr k1 #故意字符串自增1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) "v1"

redis监控实现乐观锁

悲观锁:
默认随时出问题,直接全部加锁
乐观锁:
默认绝对不会出问题,所以不会上锁!更新数据的时候判断一下,在此期间是否有人改动过数据,在MySQL中是通过添加了一个version字段来判断的.
首先获取version,更新的时候比较version版本即可
在redis中是添加了一个watch监视器,作用类似

redis监视测试:

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监视money对象
OK
127.0.0.1:6379> multi #这里事务正常结束,期间对象没有发生变动
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20

事务执行之后,无论成功失败监控变量都会解锁
但是存在这么一种情况,在事务开启之后执行之前,监控变量改变了,这个时候事务执行之后会直接返回空(nil),如下

127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby money 20
QUEUED
127.0.0.1:6379(TX)> get money
QUEUED
127.0.0.1:6379(TX)> exec
(nil)

通过jedis操作redis

jedis是redis官方推荐的一个java连接开发工具,是使用java操作redis的一个中间件.
首先创建一个项目并导入依赖:

 <!--首先需要导入jedis的包-->
 <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
 <dependency>
     <groupId>redis.clients</groupId>
     <artifactId>jedis</artifactId>
     <version>4.2.2</version>
 </dependency>
<!--        fastjson-->
 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>fastjson</artifactId>
     <version>2.0.2</version>
 </dependency>

然后编码测试
连接redis数据库
写入操作命令
结束连接
这边使用windows连接.首先redis配置文件默认端口号127.0.0.1
在这里插入图片描述
然后就使用最简单的ping来测试一下Redis的windows版本连接:

//1,new一个jedis对象
Jedis jedis = new Jedis("127.0.0.1",6379);
//jedis包含了redis的所有指令
System.out.println(jedis.ping()); #out:pong

接下来是Linux的服务器远程连接了:
连接Linux服务器的Redis首先需要服务器开放6379端口,然后设置Redis密码,
可以直接在宝塔的配置文件中添加requirepass [你的密码],并重启redis
然后就比windows多了一个使用密码的方法

Jedis jedis = new Jedis("47.106.213.157",6379);
jedis.auth("自己设置的密码");
//jedis包含了redis的所有指令
System.out.println(jedis.ping());

命令在上面讲的都有,这里就不一一重复演示了

jedis测试事务

直接上事务故障代码:

    Jedis jedis = new Jedis("47.106.213.157",6379);
    jedis.auth("981216");
    jedis.flushDB();
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("hello", "world");
    String s = jsonObject.toString();
    Transaction multi = jedis.multi();
    try {
        multi.set("K1",s);
        multi.set("K2",s);
        int a=1/0;
        multi.exec();
    } catch (Exception e) {
        multi.discard();
        e.printStackTrace();
    } finally {
        System.out.println(jedis.get("K1"));
        System.out.println(jedis.get("K2"));
        jedis.close(); //关闭连接
    }

报错如下:
在这里插入图片描述

SpringBoot整合Redis

SpringBoot操作数据都是在SpringData中处理的

这里直接新建一个springboot项目,并勾选依赖
在这里插入图片描述
注意,在springboot2.X之后,原来使用的jedis被替换成了lettuce
jedis底层采用的直连,多线程操作的话不安全,如果想要避免就需要使用jedis pool连接池,类似BIO模式
lettuce底层采用的netty,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数量,更像NIO模式
PS:绝了,这俩模式好像没听过
打开redis自动装配的源码:

@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {}
    @Bean
    @ConditionalOnMissingBean(name = {"redisTemplate"})
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        //注意,这里是空参,也就是说默认的redis模板没有过多设置
        //这里的两个泛型都是Object类型,以后使用需要强制转换<String,Object>
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
    @Bean
    @ConditionalOnMissingBean//由于String类型是最常使用的类型,所以单独提出来了一个Bean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }
}

其中@ConditionalOnMissingBean(name = {“redisTemplate”})代表如果指定名称的Bean不存在,使用注解的这个类就生效,也就是说可以自定义一个redis模板来替换掉默认设置
查看springboot中自动装配的redisproperties配置文件也可以看到一些默认信息:

private int database = 0;
    private String url;
    private String host = "localhost";
    private String username;
    private String password;
    private int port = 6379;
    private boolean ssl;
    private Duration timeout;
    private Duration connectTimeout;
    private String clientName;
    private ClientType clientType;
    private Sentinel sentinel;
    private Cluster cluster;
    private final Jedis jedis = new Jedis();
    private final Lettuce lettuce = new Lettuce();

源码看完直接开冲:
首先依赖在创建的时候最好直接勾选:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

然后就是配置redis的连接配置文件:
需要注意的是如果要使用连接池需要使用lettuce的连接池,因为jedis的配置已经不生效了

# springboot所有的配置类都有一个自动配置类,RedisAutoConfiguration
# 自动配置类都会绑定一个properties配置文件,RedisProperties

#配置redis
spring:
  redis:
    host: 47.106.213.157
    port: 6379
    password: XXXXXX

然后就是测试
测试首先要注入redistemplate,然后才能对其进行操作,操作方法也有很多:
opsforXXX可以翻译为对XXX的操作.
在这里插入图片描述
可以看到之前学习的数据类型在上面都有,并且连操作方法也收录在里面了:
在这里插入图片描述

简单测试一下:

 @Autowired
    RedisTemplate redisTemplate;
    @Test
    void contextLoads() {
        //获取redis的连接对象
        //RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        //connection.flushDb();
        //connection.shutdown();
        //connection.close();

        //获取redisTemplate之后就可以操作不同的数据类型,方法接口和学的指令是一样的
        //redisTemplate.opsForSet();
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("name","祖国人AAA");
        System.out.println(ops.get("name"));
    }

运行之前确认密码和redis是否开启

自定义RedisTemplate

注意,如果使用默认配置文件来对redis进行操作的话有时候会出现乱码的情况:

127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04name"

这是因为redis默认序列化的原因,找到redisTemplate.class文件,可以发现里面有四个序列化配置(Serializer翻译为序列化)
在这里插入图片描述
除此之外可以看到默认序列化(defaultSerializer)如果为空就使用默认jdk序列化配置
在这里插入图片描述
用JdkSerializationRedisSerializer序列化的话,被序列化的对象必须实现Serializable接口
所以我们可以创建自定义的配置类来进行序列化操作.
直接代码演示:

    @Test
    public void test() throws JsonProcessingException {
        User user = new User("xg", 24);
        //进行序列化
        String jsonUser = new ObjectMapper().writeValueAsString(user);
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("user",jsonUser);
        System.out.println(ops.get("user")); //{"name":"xg","age":24}
    }

上面可以直接输出user对象的json字符串,只要这里的程序或者user实体类进行了序列化操作,就没问题,如果没有序列化就会报错

接下来就是自定义一个redistemplate模板了,我按照老秦的视频导入jackson的依赖结果一直都是序列化失败,然后我在网上找了fastjson的序列化,直接一波成功:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用 GenericFastJsonRedisSerializer 替换默认序列化
        GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
        // 设置key和value的序列化规则
        redisTemplate.setKeySerializer(new GenericToStringSerializer<>(Object.class));
        redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
        // 设置hashKey和hashValue的序列化规则
        redisTemplate.setHashKeySerializer(new GenericToStringSerializer<>(Object.class));
        redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
        // 设置支持事物
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

另外一般来说都会创建一个redisUtils类来进行操作,代码太长找一下就能找到很多,这里就只附上一个链接:

https://blog.csdn.net/weixin_45397785/article/details/123749855

操作的时候就更简单了,还能有自定义的附加操作:

@Autowired
private RedisUtils redisUtils;
@Test
public void test1(){
    redisUtils.set("color","yellow");
    System.out.println(redisUtils.get("color"));
}

Redis.conf讲解

这里服务器的redis.conf配置文件在/www/server/redis/redis.conf,直接在Xshell中使用vim编辑器打开就可以看到并修改
注意,我们之前因为需要idea连接服务器redis所以修改了两个配置,一个是密码,一个是默认访问ip
那么:
在这里插入图片描述
类似于spring中的配置文件中import标签,可以导入其他配置文件,同时生效
网络配置:

bind 127.0.0.1 #这里注销了
protected-mode no  #保护模式  这里关闭了idea才能连接上服务器的redis
port 6379

通用配置[GENERAL]:

daemonize yes #以守护进程的方式运行,默认为no,按照默认退出的时候进程就结束了,windows不支持
supervised no #管理守护进程的

pidfile /www/server/redis/redis.pid #这个比较重要,如果以后台的方式运行,就需要指定一个pid进程文件

#日志
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice  #日志输出级别
logfile "/www/server/redis/redis.log" #日志输出地址

databases 16 #默认数据库数量

always-show-logo yes #开启redis-server时出来的logo,选择是否展示

快照SNAPSHOTTING
持久化,在规定的时间内执行了多少次操作才会进行持久化到文件, 文件有**.rdb格式文件和.aof**文件
redis是内存数据库,如果没有持久化,内存数据断电即失


save 900 1 #900秒内至少有一个key进行了修改,就进行持久化操作
save 300 10 #300秒内有10个key进行了修改,就进行持久化
save 60 10000 #20秒内有10000个key进行了修改就持久化, 都可以自定义

stop-writes-on-bgsave-error yes #持久化出错是否继续工作
rdbcompression yes #是否压缩持久化的rdb文件,需要消耗资源
rdbchecksum yes #保存持久化rdb文件的时候是否进行错误校验
dbfilename dump.rdb #文件名
dir /www/server/redis/ #持久化文件的保存位置

主从复制REPLICATION
暂时跳过,下面会讲
安全SECURITY

requirepass #密码 默认为空 设置之后需要重启

客户端CLIENTS

maxclients 10000 #设置redis连接的客户端最大数量

内存MEMORY MANAGEMENT

maxmemory <bytes> #最大内存容量
maxmemory-policy noeviction #内存到达上限之后的处理方法
volatile-lru #对只设置了过期时间的key进行LRU(默认值)
allkeys-lru #删除LRU算法的key
volatile-random #随机删除激将过期的key
allkeys-random #随机删除
volatile-ttl #删除激将过期的
noeviction  #永不过期,直接返回错误            

APPEND ONLY MODE模式 AOF配置

appendonly no #默认不开启aof模式,按照rdb的方式进行持久化操作,一般情况RDB够用
appendfilename "appendonly.aof" # 持久化文件的名字

appendfsync always   #每次修改都会执行一次sync,消耗性能
appendfsync everysec #每秒执行同步一次sync ,但是可能会丢失这一秒的数据
appendfsync no       #不执行同步sync,这个时候操作系统自己同步数据,速度最快

Redis持久化

内存型数据断电即失,部分数据还是需要持久化操作

RDB(Redis DataBase)

在这里插入图片描述
会在指定的时间内将内存中的数据集体快照(sanpshot)写进磁盘,恢复的时候直接把快照文件读取到内存中

fork是linux系统生成一个子线程的函数名称
Redis会单独创建一个(fork)子进程来进行持久化,会将数据写入到一个临时文件中,等到持久化过程都结束了,在用这个临时文件替换上次持久化好的文件,整个过程中主进程是不会进行任何的IO操作,这就确保了极高的性能,如果需要进行大规模数据的回复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效,RDB的缺点是最后一次持久化后到重新读取之间这段时间产生的IO数据可能会丢失
rdb保存的文件是dump.rdb 上面配置文件中有说明
类似于打游戏打到第三关存储游戏进度,但是打到第四关的时候突然断电,就只能从第三关再进行读取了
这里简单测试一下,先把默认持久化条件改一下:

#save 900 1
#save 300 10 
#save 60 10000
save 60 5

保存配置文件之后,先找到dump.rdb文件地址,删掉.
在这里插入图片描述
然后按照条件一分钟连续写入五个数据,触发持久化条件,之后再查看就会发现又有了一个dump.rdb文件
在这里插入图片描述
持久化触发机制

  • save的规则满足的情况下,会自动触发rdb规则
  • 执行flushAll命令,也会触发rdb规则
  • 退出redis的时候(shutdown),也会产生rdb文件

如何恢复rdb文件?
只需要将rdb文件放在redis的启动目录下即可,redis启动的时候会自动检查dump.rdb文件,恢复其中的数据
查看需要存在的位置:

127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin"

fork进程的时候,会占用一定的内存空间

AOF(Append Only File)

意思是将所有的修改命令都记录下来,history,恢复的时候就把个文件上面的命令全部都执行一遍
在这里插入图片描述
以日志的形式记录每一个写操作(读不会影响数据,不记录),只允许追加文件,但不可以改写文件,redis启动之初就会读取该文件并重新构建数据.
下面是中文官方文档原画
在这里插入图片描述
注意,只修改配置文件不行,没有aof文件,还需要执行命令

config set appendonly yes
config set save ""

aof文件长这样:(写了一句set K11 V1)
在这里插入图片描述
aof文件可以被修改(污染),修改之后自然也会影响读取,这里人为地在V11的那个位置瞎输入了一些字符,然后重启再进来就get不到了
在这里插入图片描述
有个bin目录下有个插件可以修复,我试了,不行,并且把原本的内容都没整没了:
在这里插入图片描述
命令如下:

redis-check-aof --fix appendonly.aof 

看了弹幕才知道说是修复,其实是截断,只保留错误语句之前的数据,把污染的命令直接扣了,绝绝子,我说呢

另外AOF由于每秒都会记录,文件最终超过默认的64M的时候,fork会创建一个新的进程(文件)来将原文件进行重写,但是会进行命令压缩,只保留可以恢复数据状态的最小指令集合,比如将早中晚三顿饭直接一口气全吃了,他只需要保证你已经吃过三顿饭就行怎么吃的无所谓

两者优缺点

官方文档:http://www.redis.cn/topics/persistence.html
以下是官网原话:
RDB的优点

  • RDB是一个非常紧凑的文件;,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
  • RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复.。RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他lO操作,所以RDB持久化方式可以最大化redis的性能.
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.
    RDB的缺点
  • 如果你希望在redis意外停止工作(例电源中断)的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.
  • RDB需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是可以调节重写日志文件的频率来提高数据集的耐久度.
    AOF优点
  • 使用AOF 会让你的Redis更加耐久:
    你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.
  • AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子,如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的FLUSHALL 命令, 并重启 Redis,就可以将数据集恢复到FLUSHALL 执行之前的状态。
    AOF缺点
  • 对于相同的数据集来说,AOF文件的体积通常要大于RDB文件的体积
  • 根据所使用的 fsync策略,AOF的速度可能会慢于RDB。在一般情况下,每秒fsync的性能依然非常高,而关闭fsync可以让AOF的速度和RDB一样快,即使在高负荷之下也是如此。不过在处理巨大的写入载入时,RDB可以提供更有保证的最大延迟时间(latency)
    翻译成人话
    RDB就是游戏状态存档,触发条件才会存,消耗资源少,但是由于存档不及时,导致有时会丢失不少进度,读档的时候直接得到最近的状态
    AOF就是每秒都在自动记录游戏行为,消耗资源多,因为记录及时,所以丢失的进度极少,但是要想恢复状态,需要把存档里做过的事都再做一遍才行
    并且RDB存档会卡顿,影响游戏体验(运行性能差点),AOF是有另外的人帮你记录,你只管玩游戏就行(运行性能好)
    读取的时候就费事了,RDB记录的数据状态可以直接读取,AOF记录的行为需要一件一件做才能恢复到最后的数据状态

Redis实现简单发布订阅

redis本身也自带了消息队列系统,比较简单,实现简单的发布订阅还是可以的,这里只做了解
首先创建一个消息队列:
subscribe+[通道名] 订阅一个或者多个通道的信息

127.0.0.1:6379> subscribe home
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "home"
3) (integer) 1

然后在另外一个客户端往通道内发送数据:
publish+[通道名]+[信息] 往指定通道发送信息

127.0.0.1:6379> publish home "welcome to home"
(integer) 1
127.0.0.1:6379> publish home "HELLO"
(integer) 1

然后通道那边就可以接受到消息并显示具体消息类型,通过什么通道,发送了什么东西:
在这里插入图片描述
更多的命令有:
pubsub subcommand [argument] 查看订阅与发布系统状态
punsubscribe [pattern] 退订所有给定模式的频道
unsubscribe [channel] 只退订指定的频道

一般来说实现分发订阅等模式的实现使用mp消息中间件就好了,比如之前的RabbitMQ,不过也可以用于进行实时聊天之类的

Redis主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的redis服务器上,前者称为主节点(master/leader),后者称为从节点(slave/follower);
数据的复制是单向的,只能由主节点到从节点,Master以写为主,slave以读为主
默认情况下,每台redis服务器都是主节点,且一个主节点可以有多个从节点(或没有),但是一个从节点只能有一个主节点.
主从复制的作用主要包括:
数据冗余:主从复制实现了数据的热备份,是持久化的一种数据冗余方式
故障恢复:当主节点出现问题是,可以由从节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从结点提供读服务,分担单个服务器的负载,尤其是在写少读多的情况下,通过多个从节点分担读负载,可以大大提高redis服务器的并发量
高可用基石:主从复制除了上面这些,还是哨兵和集群能够实施的基础,因此主从复制也是redis实现高可用的基础

实现redis集群必须要有三台redis服务器,一台主机两台从机,如果只有一台从机的话,主机故障,从机选举策略就会有问题,然后还需要一台客户端,也就是四台

PS: redis客户端使用info replication可以打印复制信息

127.0.0.1:6379> info replication
# Replication
role:master #角色
connected_slaves:0  #从机数量
master_failover_state:no-failover  
master_replid:1e13d1ea9cccb6f720c186516354d959d888ff55
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

动手搭建单机多服务环境~
首先找到redis.conf配置文件,然后复制四份
在这里插入图片描述
然后进去修改端口号,pid文件名,日志文件名和RDB的dump.rdb文件名
修改完之后使用 redis-server+[指定配置文件地址] 来进行启动:
比如,宝塔安装redis之后就是 redis-server /www/server/redis/redis79.conf
如果在配置文件地址中发现新的日志文件说明就启动成功了:
在这里插入图片描述
进程也可以看到:

[root@xg redis]# ps -ef|grep redis
root     10233     1  0 00:03 ?        00:00:00 redis-server *:6379
root     10936 10297  0 00:08 pts/3    00:00:00 grep --color=auto redis

三个都启动起来就算环境搭建成功了

主从配置
服务器创建完毕之后就可以设置主机了
slaveof+[设备IP]+[端口号] 使当前服务端成为指定主机的从机
注意,这里是站在从机的角度认主机当老大,而不是直接设置当前服务端为主机,从机都有谁谁谁
如下:
在这里插入图片描述
而主机这边:
在这里插入图片描述
注意,这里使用命令设置的都只是暂时的主从关系,想要永久就需要在配置文件中进行修改:
在这里插入图片描述
设置完主从之后就可以进行测试了

主机主写,从机只能读主机中的所有数据都会被从机保存
6379端口主机读写无障碍
在这里插入图片描述
从机可以读到主机的数据,但是无法写入:
在这里插入图片描述
没有其他配置的情况下主机突然宕机,从机依然是从机,主机宕机回来之后依然是其他从机的主机,
并且如果这个时候从机宕机,重启之后会变成主机(因为没有在配置文件中设置),但是重启之后又设置了6379为它的主机,那么主机的所有数据就可以立马同步到从机里

如果主机有密码,可以在从机中设置masterauth"XXX"用于连接主机
如果主机断开了连接,并且没有配置文件配置哨兵模式,可以使用slaveof no one 让自己成为主机

数据同步原理:
slave启动成功连接到master后会发送一个sync同步命令
master接到命令后启动后台的存盘进程,同时手机所有接收到的用于修改数据集命令,在后台执行完毕之后,master会传送整个数据文件到slave,并完成一次完全同步
全量复制:而salve服务在接收到数据库文件后,将其存盘并加载到内存中
增量复制:等从机将全量复制的数据存盘并加载完之后,master继续将接下来产生的新的收集到的修改命令依次传给salve,完成同步
只要重新连接到master,一次完全同步(全量复制)将被自动执行

哨兵模式

Sentinel(哨兵)是用于监控Redis集群中Master状态的工具,是Redis高可用解决方案.哨兵可以监视一个或者多个redis master服务,以及这些master服务的所有从服务。 某个master服务宕机后,会把这个master下的某个从服务升级为master来替代已宕机的master继续工作。即使原来的master服务重启也是作为从机,为新的master服务

一般单机哨兵有两个作用:
通过发送命令,让redis服务器返回监控其运行状态,包括主服务器和从服务器.
当哨兵监测到master宕机,会自动将slave切换成master,然后通过自带的发布订阅模式通知其他的从服务器,修改配置文件,让他们切换主机

那么万一这个哨兵进程本身出问题怎么办呢?
所以我们可以使用多个哨兵进行监控,各个哨兵之间也会互相进行监控,这样就形成了多哨兵模式.
在这里插入图片描述

假设主服务器宕机,哨兵1首先检测到这个结果,系统并不会马上进行failover(故障转移)过程,仅仅是哨兵1主观认为master服务器不可用,这个现象称为主观下机.当后面的哨兵也检测到主服务器不可用,并且数量达到一定值之后,哨兵之间也会进行一次投票,投票的结果由一个哨兵发起,进行failover操作.切换成功之后,就会通过发布订阅模式,让各个哨兵吧自己监控的从服务器实现切换主机,这个过程称为客观下机.

redis解压目录下就有专门的哨兵配置文件 sentinel.conf
sentinel monitor mymaster(被监控名称) 127.0.0.1(ip) 6379(port) 2(哨兵检测阈值)
代表当有两个哨兵检测到指定ip和端口的主redis服务异常就判定主服务死亡

另外redis默认安装的src目录下还有哨兵进程的启动文件,为了方便,我直接使用mv命令包括之前客户端服务端都给他移动到/www/server/redis目录下了
在这里插入图片描述
启动哨兵进程之前,需要确定是一主两从的模式
接着启动: ./redis-sentinel sentinel.conf
注意需要带上配置文件 sentinel.conf
在这里插入图片描述
如果出现哨兵模式报错failover-abort-no-good-slave master,意为没有良好从机,可以进入sentinel.conf配置文件,为其配置密码:
sentinel auth-pass mymaster XXXXXX
密码默认是被注释掉的

然后master服务器手动shutdown关机,等哨兵进程检测到主机无响应时会有专门的算法对从机进行投票选举,选举期间服务器会关闭连接选举到新的master服务器之后才会恢复
在这里插入图片描述
原来的主服务器在选举结束后重连也会变成从服务器
在这里插入图片描述

优点:
哨兵集群是基于主从复制模式,所有的主从配置优点他也有
主从可以切换,故障可以转移,系统的可用性会更好,
哨兵模式就是主从模式的升级,手动到自动,更加健壮.
缺点:
redis不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦
实现哨兵模式的配置很麻烦,里面有很多需要选择,如果是多哨兵模式,还需要复制并修改不同的配置文件,再一一启动

Redis缓存穿透和雪崩

缓存穿透(查不到)
首先说明一下redis数据查询机制,redis收到查询请求之后会首先到缓存里面去找,如果缓存中没有的话,就会到数据库中进行查找.如果数据库中也没有才会返回查询失败的结果.
在这里插入图片描述

当用户很多的时候,每次查询都没有命中缓存,于是都去请求了持久层数据库,这会给持久层数据库造成很大的压力,这时候就相当于出现缓存穿透
缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端的意义

缓存穿透解决方案:
布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式储存,在控制层先进行效验,不符合则丢弃,从而避免了对底层存储系统的压力
在这里插入图片描述
缓存空对象
当存储层不命中后,即时返回的空对象也会将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中直接获取,保护后端数据源
在这里插入图片描述
但是这种方法会有新的问题,
如果空值能够被缓存起来,就意味着需要更多的空间存储更多的键,因为这当中可能会有更多的空值的键.
即使对空值设置了过期时间,还是会存在特定时间内缓存层和存储层的数据不一致,对于需要保持一致性的业务会有影响

缓存击穿(查太多)
缓存击穿是指一个key非常热点,经常被访问,在不停地扛着大并发,当这个key失效的瞬间持续的大并发就会穿破缓存,直接请求数据库.当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般都是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,然后回写缓存,导致数据库压力瞬间增大

解决方案:
设置用不过期
根据情况设置热点数据永不过期
加互斥锁
使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可.这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
可以理解为有很多人想要一个女生微信,拜托了一个中间人去要,要完之后给他们.这样女生就不会因为人多觉得烦,只有那个苦逼中间人会觉得烦

缓存雪崩
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机.
比如双十一等特定时间活动到期,活动时的数据大批量丢失,用户访问时全都访问不到,就集体访问到数据库,导致数据库崩溃宕机

解决方案:
提升Redis高可用性,也就是多搭建一些Redis集群
限流降级,在缓存失效之后通过加锁或者队列来控制读数据库写缓存的线程数量.比如对某个key只允许一个线程查询数据和写缓存,其他线程等待
数据预热,在正式部署之前,把可能的数据先访问一遍,这样大部分访问的数据就会加载到缓存中.在即将发生大并发访问之前手动触发加载缓存不同的key,设置不同的过期时间.让缓存失效的时间点尽量均匀.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值