数据存储类型介绍
业务数据的特殊性
作为缓存使用
- 原始业务功能设计
- 秒杀
- 618活动
- 双11活动
- 12306
附加功能
- 系统功能优化升级
- 单服务器升级集群
- session管理
- Token管理
Redis数据类型(5种常用)
- string string
- hash HashMap
- list LinkedList
- set HashSet
- sorted_set TreeSet
string
- get key
- set key
- del [key]
- mset key1 value1 key2 value2
- mget key1 key2
- strlen key
- append key value
单数据操作与多数据操作的选择之或?
- 比较发送与执行时间消耗时间对比
string数据类型的扩展操作
- 设置数值数据新增加指定范围的值,可以使用负号作为递减操作
-
incr key incrby key increment incrbyfloat key increment
- 设置数值数据新减去指定范围的值
-
decr key decrby key increment
- 设置数值有效期 解决时效性控制的操作
-
setex key second value 秒 psetex key milliseconds value 毫秒
string类型数据操作时的注意事项
- 设置数值有效期 解决时效性控制的操作
- 数据未获取到
-
(nil)等于 null
- 数据最大存储量
512m
- 数据计算最大范围 (java中的long的最大值)
9223372036854775807
- 数据最大存储量
string类型应用场景
- 在redis中存储用户信息
- user🆔333:fans -> 11111 数值
- user🆔333 -> {id:333,fans:333} json
- redis应用于各种结构性和非结构性高热度数据访问的加速
- key的设置约定 表名:主键名:主键值:字段名
hash 底层使用哈希表结构存储
底层使用哈希表结构存储
hash存储结构优化
- 如果field字段数量少,使用数据数组进行存储
- 如果field字段数量多,存储结构使用hashmap
hash类型数据的基本操作
- 添加/修改数据
-
hset key field value
- 获取数据
-
hget key field hgetall key
- 删除数据
-
hdel key field [field2]
- 添加/修改多个数据
-
hmset key field1 value1 field2 value2
- 获取多个数据
-
hmget key filed1 field2
- 获取hash表中字段的数量
-
hlen key
- 获取hash表中是否存在指定字段
-
hexists key field
hash数据类型的扩展操作
- 获取hash表中所有的字段名和值
-
hkeys key hvalues key
- 设置值自增
-
hincrby key increment hincrbyfloat key increment
- 设置值字符,存在则设置失败,不存在则设置成功
-
setnx key field value
hash数据类型注意事项
- hash类型value只能存字符串
- 每个hash可以存储2^32-1个键值对
- hash可以灵活操作属性,虽然可以作为对象使用,但是不能滥用
- hgetall key 获取hash内的键值对
hash数据类型应用场景
- 购物车
list
list类型数据的基本操作 链表
- 添加/修改数据
-
lpush key value [value] rpush key value [value]
- 获取数据
-
lrange key start stop (0,-1获取全部) lindex key index llen key
- 获取并移除数据
-
lpop key rpop key
- 规定时间内获取并移除数据
-
blpop key [key] timeout (单位为秒) brpop key [key] timeout (单位为秒)
业务场景
- 删除指定元素
-
lrem key count value
list数据类型注意事项
- list数据保存的数据都是string类型,最多2^32-1个元素
- list常被用作队列
- list结束索引为-1
- list可以进行数据分页操作 第一页来自list,第二页及更多来自数据库
业务场景
- 信息管理
- 多路信息汇总
- 最新消息
set 无序
set类型数据的基本操作
- 添加数据
-
sadd key memeber1 [member]
- 获取全部数据
-
smembers key
- 删除数据
-
srem key member1 [member]
set类型数据的扩展操作
- 随机获取几个元素
-
srandmember key count
- 随机获取几个元素 并删除
-
spop key count
- 获取元素个数
-
scard key
- 求两个集合的交并差
-
sinter key key2 sunion key1 key2 sdiff key1 key2
- 求两个集合的交并差 并存到指定集合
-
sinterstore desc key key2 sunionstore desc key1 key2 sdiffstore desc key1 key2
- 将指定数据从原始集合移动到目标集合中
-
smove source desc member
- 查看数据是否存在
-
sismember key memeber
业务场景
- 随机推荐和抽奖等
zset 有序
zset类型数据的基本操作
- 添加数据
-
zadd key score memeber1
- 查询
-
zrange key start stop [withscores]
- 查询 反序
-
zrevrange key start stop [withscores]
- 删除
-
zrem key memeber [member]
数据类型实战
- 限流 如果限流为100 则把最大值-100,利用异常做限制,不需要每次进行判断
通用命令
- 删除key del key
- key的类型 type key
- key是否存在 exists key
- 为key设置有效期
-
expire key seconds pexpire key millseconds expireeat key timestamp pexpireeat key mill-timestamp
- 获取key有效时间
-
ttl key pttl key
- 切换key从临时转为永久
-
persist key
- 查询模式
-
keys * 匹配所有 keys ?lezai 匹配单个字符 keys a[abc] 匹配aa ab ac
- key重命名
-
rename key new key renamenx key newkey 新key不存在则成功
- 对所有key 里面的内容排序
-
sort key
- 切换数据库
-
select dbindex ping pong
- 数据库移动
-
mv key dbindex flushdb 删除当前库数据 flushall 清除所有数据
Jedis
- 客户端连接redis
-
连接 Jedis jedis =new Jedis("127.0.0.1",6379) 设置 jedis.set("name",123) 获取 jedis.get("name")
Linux安装redis
- 下载 wget http://download.redis.io/releases/redis-4.0.10.tar.gz
- 解压 tar-xvzf redis-4.0.10.tar.gz
- 编译 make
- 安装make install
- 指定端口启动 redis-server --port 9999
- 指定端口连接 redis-cli -p 9999
- 指定配置文件启动 redis-server conf/redis.conf
redis服务端基本配置
- daemonize yes 以守护进程方式启动
- port 6379 设置启动端口
- dir “/redis/data” 设置当前服务文件保存位置
- logfile “744.log” 设置日志文件名
持久化
RDB
- save 手动执行保存一次快照信息
- 相关配置
-
dbfilename dump.rdb 设置本地数据库文件 默认为dump.rdb 一般设置为 dump-端口号.rdb rdbcompression yes 是否启动压缩 rdbchecksum yes 是否对库文件进行校验
- bgsave 使用fork函数生成一个子进程,在子进程中执行
- rdb自动启动定时保存
-
配置信息 save second changenum save 900 1 900 秒内有一个key变化 save 60 3 60秒内有3个key变化 save 10 6
AOF
写数据的三种策略
- always 每次操作都同步写入到aof中,数据零误差 性能低
- everysec 每秒 将缓冲区的指令同步到aof中,系统宕机只会丢失一秒的数据
- no 系统控制 不可控
AOF功能开启
- appendonly yes | no 开启持久化 默认不开启
- appendfsync always | everysec | no AOF策略
- appendfilename fielname
AOF重写 重写规则
- 进程内已超时的数据不再重写
- 忽略无效指令
- 对同一数据的多条写合并为一条
- bgrewriteaof 重写aof指令
AOF 与RDB 选择
- 数据很敏感 使用aof
- 数据间断性呈现 RDB
- 综合方案 同时开启rdb+aof
Redis高级操作
Redis事务
事务的基本操作
- 开启事务
-
multi 设定事务的开启 执行指令后,全都加入到事务中
- 执行事务
-
exec 设定事务的结束,同时执行事务,与multi成对出现
- 取消事务
-
discard 取消事务
- 注意
-
事务出错不能回滚,产生的数据不会回滚
锁
- watch key1 key2 …
-
监控某个key,需要在事务外面使用,如果key被外部修改了,则终止事务的执行
- unwatch
-
取消对事务的监控
分布式锁
- setnx key value
解决死锁 添加时效性
Redis删除策略
定时删除
- 创建一个定时器,当key设置有过期时间,且过期时间到达,由定时器执行对键的删除操作
- 牺牲CPU保证内存空间 CPU空闲 内存紧张
惰性删除
- 在获取数据的时候,如果未过期,返回数据,过期了,删除,返回不存在
- 牺牲存储保证CPU 存储空间足 CPU紧张
定期删除
定期删除策略是怎么实现的?通过activeExpireCycle函数,serverCron函数执行时,activeExpireCycle函数就会被调用,规定的时间里面分多次遍历服务器的expires字典随机检查一部分key的过期时间,并删除其中的过期key
例如Redis每秒处理:
- 测试随机的20个keys进行相关过期检测。
- 删除所有已经过期的keys。
- 如果有多于25%的keys过期,重复步奏1.
Redis逐出算法
- 在执行每一个命令前,都调用freememoryifneed,内存不足,需要临时删除一些数据清理空间
- maxmemory 最大内存
- maxmemory-samples 随机选择
- maxmemory-policy
- 有效期的数据
-
volatile-lru 最近最少使用
-
volatile-lfu 最近使用次数最少
-
volatile-ttl 挑选将要过期的数据
-
volatile-random 任意选择数据淘汰
- 所有数据
-
allkeys-lru 最近最少使用
-
allkeys-lfu 最近使用次数最少
-
allkeys-random 任意选择数据淘汰
- no-enviction
高级数据类型
bitmaps
- setbits key offset value
- getbits key offset
- bitop [and or not xor] destkey key1 key2 对多个key组合操作,保存到destkey中
- bitcount key [start end] 统计为1的数量
HyperLogLog
- pfadd key element …
- pfcount key
- pfmerge destkey sourcekey1 sourcekey2
- 相关说明
-
用于基数统计,不是集合,不保存数据,只记录数量而不是具体数据
-
核心是基数估算,最终数值存在一定误差
-
误差范围:0.81%
-
占用12k
-
pfadd 不是一次性分配12看,而是随基数增大而增加空间
Geo 距离计算
- geoadd key longitude latitude member … 添加坐标
- geopos key member … 获取坐标
- geodist key member1 member2 unit 获取坐标点距离
- georadius key longitude latitude radius unit 根据坐标求范围内的数据
- georadiusbymember key memeber radius unit 根据点求范围内的数据
Redis集群
主从复制简介
互联网“三高”
- 高性能
- 高并发
- 高可用 5个9 99.999%
工作流程
- 建立连接
- 数据同步(复制数据)
- 反复同步(命令传播)
- 建立连接阶段工作流程
-
1. 发送指令 slaveof ip port
-
2. 接收到指令,响应对方
-
3.保存master的ip与端口 masterip masterport
-
4.根据连接保存的信息创建与master的socket
-
5.向master周期发送ping,master相应pong
-
6.slave 发送author password
-
7.验证授权
-
8.发送指令replyconfiglistening-port port-num
-
9.保存slave端口号
- 连接的三种方式
-
1.slaveof if ip port
-
2.slave启动时 带参数 --slaveof masterip masterport
-
3.服务器配置 slaveof if ip port
- 数据同步阶段
-
全量复制开始
-
①发送指令 psync2 请求同步数据
-
②master执行bgsave
-
③第一个slave建立连接,创建命令缓冲区
-
④生成rdb,通过socket发给slave
-
⑤vslave接受rdb,清空数据,执行rdb文件恢复
-
⑥发送命令,告知已经恢复完成
-
全量复制结束
-
部分复制开始
-
⑦发送复制缓冲区指令信息
-
⑧接受指令,执行bgrewriteeaof,恢复数据
-
部分复制结束
- 命令传播阶段
-
复制缓冲区 偏移量 字节值
-
工作原理: 通过offset区分不同的slave当前数据传播的差异,maste记录已发送的信息对应的offset,slave记录已经接受的offset
数据同步(全)
- slave 发送指令 psync ?-1
- master收到发现没有偏移量,则使用bgsave生成rdb文件,记录当前的复制偏移量offset
- 发送fullresync runid offset,通过socket发送给slave,期间收到客户端命令,offset发生变化
- 收到 + fullresync 保存master的runid和offset,清空当前数据,接受rdb文件,执行恢复
- 发送命令 psync2 runid offset
- master接受到命令,判定runid是否匹配,判定offset是否在缓冲中
- 如果runid和offset不匹配,则执行全量复制
- 如果runid和master校验通过,offset和offset相同,忽略
- 如果runid和master校验通过,offset和offset不相同, 发送continue offset,通过socket发送缓冲区中offset到offset的数据
- slave接收到continue,保存master的offset,接受信息后,执行bgrewriteaof,恢复数据
- 主从复制参数注意
-
① slave-server-stable-data yes|no 同步过程,开启或关闭对外写操作
-
②repl-backlog-size 1mb 设置master指令缓冲区大小,
-
主从复制常见问题
频繁全量复制
-
①缓冲区太小 导致offset不匹配 解决:缓冲区设置大点
-
频繁网络中断②maset宕机重启,导致runid和offset丢失,全量复制 master执行shutdown 时,将runid和offset进行保存,重启时进行读取
数据不一致① 执行命令一直阻塞耗时 设置超时时间
哨兵简介
哨兵是一个分布式系统,用于对主从结构中的每台服务器做监控,当出现故障通过投票随机选择新的master并将所有的slave连接到信息master
作用
- 监控
- 通知
- 自动故障转移
启用哨兵模式
- 启动哨兵
-
redis-sentinel redis-sentinel.conf
-
配置文件中配置 主redis节点
哨兵工作原理
监控阶段
- 用于同步各个节点的状态信息
- 获取sentinel的状态(是否在线)
- 获取master状态
- 获取所有slave状态
- master保存sentinel信息,作用是让其他sentinel来发现其他的sentinel,建立订阅通道
通知阶段
- 每个sentinel进行互通
故障转移阶段
- sentinel发现master关挂了,标记master为主观下线,然后发送命令给奇特哨兵,其他哨兵也会去看看状态,如果一半以上的哨兵认为master挂了,那就直接标记为客观下线,执行故障转移操作
- sentinel通过竞选,然后获得此次处理master的权利,成为领头sentinel
- 去除不在线的
- 去除响应慢的
- 与原master断开时间久的
- 优先原则
- 优先级
- offset
- runid
- 发送指令
- 向新的master发送slave no one指令
- 向其他slave发送slaveof新的ip端口
集群简介
数据存储设计
- 通过算法设计,计算出key应该保存的位置
- 将所有的存储空间分割成16384份,每台主机保存一部分代表的是一个存储空间,不是一个key的保存空间
- 将key按照计算的结果放到对应的存储空间
集群内部通讯设计
- 各个节点相互通信,保存各个库中槽的编号数据
- 一次命中,直接返回
- 一次未命中,告知具体位置
环境搭建
- cluster-enabled yes
- cluster-config-file node-6379.conf
- cluster-node-timeout 10000
- 在src下执行
-
./redis-trib.rb create --replicas 1(一个master,一个slave,2 一个master两个slave) 主[...] 从[...]
- 连接集群客户端
-
redis-cli -c
企业级解决方案
缓存预热
系统启动前,提前将相关的缓存数据直接加载到缓存系统,避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的数据
缓存雪崩
缓存击穿
缓存穿透
解决方案
- 缓存null
- key加密
- 实时监控
- 白名单策略 bitmaps(效率低)