Redis 基本特性
Remote Dictionary Service
内存存储,NoSQL。基于内存来存储数据。无需IO,效率高。提供高可用方案。哨兵模式,分布式数据存储。
关系型数据与非关系型数据库对比
SQL
1、行存储,二维
2、结构化,Schema
3、表与表之间关联,Relationship
4、SQL语法,Structure Query Lanagurage
5、ACID Atomic consistency isolation constancy
基于磁盘存储,扩容困难。增加硬件、或者使用其他的技术(分库分表)。
数据结构固定(存储数据格式受限)。
并发量大情况下读写压力大(IO)。
NOSQL
1、非结构化数据
2、数据没有关联
3、遵循BASE最终一致性
4、支持海量数据存储,高效数据读写
5、支持分布式分片存储。
BASE介绍
Basically Available(基本可用)
Soft-State(软状态)
Eventually Consistent(最终一致性)
NOSQL类型
KV存储(Redis)
文档存储(MongoDB)
列存储HBase
图存储Graph
对象存储
XML存储
......
Redis客户端
基于TCP通讯,有固定的通讯格式(序列化协议)
*3\r\n9\r\nfishbones$4\r\nhaha\r\n
SET fishbones haha
*3表示一共几个参数 set (1) fishbones(2) haha(3)
\r\n 回车换行,可理解为表示空格
$3 第一个参数的(SET)长度
$9第二个参数的(fishbones)长度
......
目前常用的(官方推荐)客户端有
Jedis 小巧轻量的客户端。
lettuce (SpringBoot)高级客户端。线程安全支持同步异步、哨兵、分片、管道......
Redisson
Jedis
Redis高级特性基本都支持。但是在多线程使用同一个Jedis连接时会出现数据干扰问题。要解决这一问题需要用到连接池,为每个线程创建一个专属的连接(Apache commonPool)。
lettuce
线程安全的客户端。使用Netty实现,多个线程可以共用一个实例,无需考虑并发问题。SpringBoot 2.0 后默认客户端,2.0前 是Jedis。
Redisson
(有中文文档)区别于传统的客户端,主要是基础Redis提供一些分布式、可扩展的Java数据结构(SET,LIST,MAP,QUEUE)。
Redis 数据类型
String
介绍
默认数据类型。包含int/float。
常用命令
set key value 设置值
get key 获取值
set key value NX 值不存在才会成功
set key value EX 设置过期时间
mset mget 批量设置 获取
strlen key 获取值的长度
append key value 追加字符(末尾)
getrange key start end 截取字符 (0开始 -1表示最后 -2 表示倒数第一个字符)
incr key 增加1 (int值可用 默认1)
incrby key value 增加value
decr , decrby value, 减少
set key 2.6 可以设置浮点数
incrbyfloat key value 增加浮点值
位相关操作(字符在Redis中以8位二进制存储)
setbit key index value 将key对应的value值的第index位修改为value。
getbit key index 获取key对应位的值
bitcount key 获取二进制存储 一共有多少个1
bitpos key 0/1 返回二进制存储 第一个0或者1的下标位置。
bitop and resultKey key1 key2 ...... 对一个或多个key求逻辑与,结果保存到resultKey中。(只有相对位结果都是1 才是 1)
bitiop or ...... 逻辑或 (对应位有一个1 就是1)
bitop xor ...... 逻辑异或 (对应值不同的时候才是1,相同就是0)
bitop not resultKey key 对制定Key进行逻辑非操作,保存到resultKey中。 (1就是0,0就是1)
String类型应用
- 缓存
- 分布式Session。Spring-session-data-redis
- set key value NX EX 分布式锁。(del key)
- incr 全局ID、计数器、限流
- 位操作 统计(每天有没有访问)
Hash
介绍
主要为了解决String类型,存储对象数据不方便问题。Hash不支持嵌套Hash。
优点:
节省空间。
减少Key冲突。
减少资源消耗(一次就能获取到资源,共享一个Key。利用:这种需要访问很多KEY)。
缺点:
过期时间无法针对某个字段单独设置
没有位操作
分片根据Key来进行存储分片。如果对象过大无法做到平衡。
RedisKey支持使用":"分级。通过这种方式可以做到存储一张表的内容,(Table:ID:Field)但是这种方式会消耗大量内存。
命令
hset key field value 设置 key下某个字段的值
hmset key filed1 value1 field2 value2 ...... 批量设置
hget、hmset 获取、批量获取值
hincby key field value 自增 通String incby
hexists key field 判断是否存在(1 是 0 否)
hdel key field 删除某个字段
hlen key 返回元素个数
应用场景
购物车等需要对象
List
介绍
有序的数据结构。存储的是字符串,元素允许重复,L表示左,R表示右。
基础命令
lpush key v1 v2...... 向List队列左侧增加一个或多个元素
rpush key value1 value2 ...... 向队列右侧增加一个或多个元素
lpop key 从队列左侧弹出第一个元素
blpop key timeout 阻塞弹出(没有元素则一直等待,超过timeout时间后不再阻塞等待)
lindex key 0 获取队列左侧开始某个下标的元素
lrange key 0 -1 获取队列左侧开始到队尾全部元素
应用
- 简单的消息队列。rpush + lpop (blpop)
- 时间线操作。
- 抢红包存储红包、秒杀存储库存
Set
介绍
String类型的无序集合,集合中元素不可重复。
基础命令
sadd key v1 v2 v3 v4 ...... 像一个Set集合添加 一个或多个元素。
smembers key 返回Set集合所有元素
scard key 返回集合元素个数
srandmember key v1 从Set集合中返回v1个随机元素(默认1)
spop key 返回并删除元素
srem key v1 v2 v3...... 移除元素
sismember key v1 判断v1是否是Set集合中的元素
sdiff key1 key2 取差集(第一个集合有,第二个没有)
sinter key1 key2 取交集
sunion key1 key2 取并集(合并后去重)
场景
spop 抽奖。(随机且移除,相对公平)
-
微博点赞、关注、签到、打卡等。
- 用like:WeiboId作为Key作为一个用户点赞Set集合列表。
- 点赞后使用: sadd like:WeiboId user1
- 去掉点赞: srem like:WeiboId user1
- 是否点赞:sismember like:WeiboId user1
- 点赞的所有用户: smembers like:WeiboId
- 点赞数:scard like:WeiboId
维护商品所有标签。(标签检索商品,商品显示标签)
- 相互关注、共同关注、可能认识......
ZSet
介绍
有序的集合。利用key和分值排序(优先使用分值)。
基础命令
zadd key score1 v1 score2 v2 ...... 向集合中增加元素并设置元素分值,默认分值小的排在前边
zrange key 0 -1 withscores 返回集合中元素同时返回分值
zrevrange key 0 -1 withscores 返回集合中元素同时返回分值(rev翻转 分值大的排在前边)
zrangebyscore key score1 score2 获取集合中某分值区间的元素
zrem key v1 v2 删除集合中指定元素
zcard key 返回集合中元素个数
zincrby key score v1 为集合中指定元素增加指定分值
zrank key v1 获取指定元素在集合中的下标位置
zscore key v1 获取集合中指定元素的分值
应用场景
点击排行榜。
Geo
介绍
保存地理信息的数据类型,提供了地理信息的基础操作。
基础命令
geoadd key log1 lat1 v1 log2 lon2 v2 向集合中增加两个元素及其对应的经纬度坐标
geopos key v1 获取集合中指定元素的经纬度坐标
getdist key v1 v2 km 获取集合中指定两个元素的距离,用km表示。
georadius key log1 lat1 r1 km 返回集合中在指定经纬度周边r1km距离内的元素。
georadiusbymember key v1 r1 km 返回集合中在指定元素v1周边r1 km距离的元素。
HyperLogLogs
介绍
基于基数的统计方法。可以用很少的内存计算很大的基数。缺点是有误差。
命令
pfadd key v1 v2 v3 ...... 向集合中添加多个元素
pfcount key 返回元素数量
pfmerge result key1 key2...... 合并多个集合返回result
应用
可以用来统计访问量等,不需要太精确的信息。
Redis发布订阅
简介
实现消息的订阅发布,保证消息的实时性,减少性能消耗(相对lpop),支持模糊匹配、广播。
订阅发布模式中,发送者和订阅者是没有任何关系的(解耦)。发布者发送的消息是不会被持久化的,所以订阅者只能接收到从订阅频道开始之后的消息。
基础命令
subscribe key1 key2 订阅多个频道
publish key msg 向一个频道发送消息(返回结果是改频道订阅者数量)
psbuscribe news.* tweet.* 订阅一个或者多个符合给定模式的频道
unsubscribe key1 key2 取消订阅
punsbcribe new.* weather.* 去掉订阅符合给定模式的频道
pubsub channels 返回活跃的频道数量(至少有一个订阅者的频道)
Redis事务
介绍
保证多个命令之间的原子性(无法彻底保证)。
Redis会按照顺序执行命令。
在事务执行过程中,不会受到其他客户端的干扰。
基础命令
multi 开启事务
exec 执行事务
discard 取消事务
watch 乐观锁实现
unwatch 取消监视
watch 提供了乐观锁行为,如果有多个线程更新同一个值时,会跟开启事务之前的值进行对比,如果不一致则取消事务。(事务开启前 使用watch监控一个或多个key。如果不使用会影响最终结果)
<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="xml" cid="n261" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"> 127.0.0.1:6379> INCR int
(integer) 5
127.0.0.1:6379> multi
OK
127.0.0.1:6379> INCR int
QUEUED
127.0.0.1:6379> exec
- (integer) 106</pre>
在执行事务之前出现的错误会导致事务取消。
比如 事务执行中 输入 hset key fishbone (执行检查不通过)
在执行事务之后出现的错误不会导致事务取消。出错的命令不会执行,但是不会影响其他命令的执行(不满足原子性)。
Redis Lua 脚本
介绍
是一种轻量级的编程语言,基于C语言编写,与数据库存储过程类似。
能够批量执行命令
Redis 会把Lua脚本当做一个整体来执行。彻底保证执行的原子性。
复杂操作的组合命令可以复用(提取公共方法让其他系统执行)。
基础命令
eval lua-script key-num [key1 key2 key3......] [value1 value2 value3]
lua-script 表示脚本内容
key-num 表示参数中有多少个key, 注意 从1 开始。如果没有则写0。
[key1 key2 key3 ......] 参数的key
[value1 value2 value3 ......] 参数的value
redis.call(command,key [param1,param2......])
commond 是命令 get、set
eval "redis.call( 'set' , keys[1] , argv[1] )" 1 user fishbone
127.0.0.1:6379> eval "redis.call( 'set' , KEYS[1] , ARGV[1] )" 1 user fishbone
(nil)
127.0.0.1:6379> get user
"fishbone"
127.0.0.1:6379>
编写、执行Lua脚本文件
创建fishbone.lua文件,并写入如下内容
redis.call('set','fishbone','666666')
return redis.call('get','fishbone')
执行命令
[root@localhost src]# ./redis-cli --eval fishbone.lua 0 "666666"
[root@localhost src]#
脚本示例
local count = redis.call('incr',KEYS[1])
if tonumber(count) == 1 then
redis.call('expire',KEYS[1],ARGV[1])
return 1
elseif tonumber(count) > tonumber(ARGV[2]) then
return 0
else
return 1
end
# 执行命令
./redis.cli --eval "ip_limit.lua" ip:limit:127.0.0.1 , 6 10</pre>
脚本缓存
为什么要缓存 . (lua 脚本内容过多)
如何缓存 在Redise客户端执行 script load "脚本内容" 命令生成摘要。 使用evalsha 摘要编号 来执行
执行超时怎么办(死循环) 如果没有设置值的操作,可以使用 Script Kill 命令。
如果有使用 shuntdown nosave 强行停止Redis服务。
Redis 数据淘汰与内存回收
Redis 内存满了怎么办?
在某些情况下需要对Redis内存进行回收,回收分为两类。
Key过期
maxmemory 达到设置内存上限
Key过期淘汰策略
定时过期:到了key的过期时间,立即删除。(创建监视器)
惰性过期:在key被访问到的时候才判断是否过期,过期删除。
定期过期:每隔一段时间,清楚一定数量的过期Key。
如果Key没有设置过期时间,则按照淘汰策略来进行数据清理。
Redis 默认不淘汰数据 noevication
LRU Lest Recently Used 最近最少使用
LFU Lest Frequently Used 最少用到(一定时间内的使用频率)
volatile 设置了过期时间的Key
allKeys 全部数据
volatile-lru
allkeys-lru
volatile-lfu
allkeys-lfu
volatile-random (随机清理)
allkeys-random
volatile-ttl (清除掉快过期的数据)
noevication
LRU 算法
随机采样,找到热度最低的key。
maxmemory-samples 5
Key中包含LRU字段,LRU属性字段包含时间戳的值(取全局时钟的值做更新)。
Servercron 会定时更新全局时钟。
LFU
1、Key最后被访问的时间
2、Key访问的频次
lfu-log-factor 10 配置递增参数
lfu-decay-time 1 频次衰减 几分钟没有访问就递减
Redis 持久化
RDB: Redis DataBase
AOF: Append Only File
RDB
默认持久化方案,自动存储到RDB文件,存储到磁盘。
触发方案
配置规则自动触发/shutdown触发/flushall触发
手动触发: save /bgsave
配置规则
save 900 1 900秒内 有1个Key被修改
save 300 10 300秒内
save 60 10000 60秒内
rdbcompression yes 开启会使用LZF算法做压缩 节省空间 消耗性能
rdbchecksum yes 开启后自动做 完整性校验
dbfilename dump.rdb db文件名称
dir ./ 表示存储在哪里 ./表示根目录
RDB存储特点
内容紧凑。保存了某一时间节点全部的数据
不影响主进程。保存数据时 不会影响主进程的工作
恢复大数据集时速度快
同步频率问题(同步时间是不确定的,无法做到秒级的持久化)
AOF
默认不开启,解决RDB存储频率问题。
用日志的形式记录操作命令,写入日志文件。每次执行修改、写入命令都会被写入日志。
开启AOF后重启服务会优先使用AOF文件做数据恢复。(注意:首次开启AOF后文件为null,重启前需要先手动做一个备份)
配置规则
appendonly no 默认不开启
appendfilename "appendonly.aof" 文件名称
auto-aof-rewrite-percentage 100 达到上次执行的百分百后 自动执行
auto-aof-rewrite-min-size 64mb 到达aof的最小空间大小(到达64mb会触发重写。如果到达百分之百但是 没有达到64mb不会重写)
appendfsync always 每一次 写入 修改Key都会同步
appendfsync everysec 每一秒钟执行一次 存储 默认使用
appendfsync no 都不会同步
开启后需要重启
AOF文件体积问题
Redis提供了一种重写机制,当大小超过阈值后,会自动进行一次压缩。
在客户端执行 bgrewriteaof 会手动重写。
AOF特点
同步频率快
文件较大
性能消耗较高(高并发下AOF要好于Redis)
备注:
AOF与RDB可以同时开启。
Redis Pipeline
批量执行,效率高。没有连接、释放耗时操作。
通过队列将命令缓存起来,一次性的在一个连接中发送给服务器执行。
因为会缓存命令,所以不要设置太大。
Jedis限值缓存命令大小为8192字节(受TCP包的大小限制不一定能达到),到达后会自动发送,收到返回后会存储在客户端的接收缓冲区,客户端缓冲区满之后,会放在服务器的输出缓冲区。
Redis 主从复制配置
主从配置原因。
性能
扩展 水平扩容
可用性 防止单点故障
通过配置文件来配置
slaveof IP PORT 在子节点配置文件 配置主节点 (主节点无需配置)
replicaof IP PORT (哨兵末日切换主从会自动修改为 replicaof )
通过客户端指令来配置
./redis-server --slaveof ip port
slaveof no one 取消主从配置
info replication 查看节点详情
特点
主节点才能执行修改,写入命令。从节点只能执行读取。
原理
链接阶段
子节点会自动保存主节点的IP端口,有定时任务去查看有没有新的主节点需要链接,有的话会建立Socket网络链接。连接成功后,会创建文件事件处理器,来执行数据同步。
数据同步阶段(初始化同步)
主节点会通过bgsave 命令生成RDB快照文件,并将文件发送给子节点做数据同步。
如果链接主节点前,Slave节点有数据,会自动清空。
如果Redis主节点数据量比较大,生成RDB文件时间较长,在这个过程中有其他命令执行。主节点会将生成RDB文件期间产生的数据命令缓存起来,等子节点保存完RDB文件数据后再传给子节点。
命令传播阶段
如果因为网络导致子节点与主节点断开一段时间,这段时间的数据如何处理?
Redis提供了偏移量来记录、确定数据同步的阶段,重新连接后把相差阶段
总结
主从复制可以解决性能,和数据备份以及高可用问题。但是还有一定的不足。
当主节点数据量很大时,同步比较耗时
无法自动切换主从节点,当主节点挂掉后,因为从节点无法写入,修改数据,会影响到服务使用(只能通过手动切换)。
Redis Sentinel
运行一个监控程序(哨兵节点),监控Redis主从节点状态。
客户端先连接Sentinel拿到master的ip,端口再进行数据操作。
配置解析
daemonize yes
port 26379
protected-mode no
dir "/usr/local/soft/redis-6.0.9/sentinel-tmp"
sentinel monitor redis-master 192.168.44.186 6379 2
sentinel down-after-milliseconds redis-master 30000
同一个master两次failover 的时间间隔
sentinel failover-timeout redis-master 180000
sentinel parallel-syncs redis-master 1
down-after-milliseconds 超过多久没有收到回复 则判定为主观下线
发生故障转移时同时有几个slave 同时对master进行同步
如何知道服务下限
判断方式分为两种,主观下限与客观下限
每一秒钟ping一下客户端,如果超过 一定时间未回复就判定为主观下线。如果一个master节点被标记为主观下线,会发起一个询问。如果超过一半的哨兵节点认为下线,则判定为客观下线。启动故障转移。
下限处理
Sentinel 选举出Leader(Raft算法,先到先得,少数服从多数)执行故障转移。
如何选取从节点成为主节点受几个因素影响。
端口连接的时长(如果某节点与哨兵节点断开的时长较长则失去资格)
优先级(可以在redis.conf进行配置 replica-priority 100)
offset(偏移量) 从主节点复制数据的完整性
进程id最小的