redis相关知识

数据类型

string 字符串
list 字符组链表 ziplist -> quicklist
set 去重的字符串集合
hash 存储对象 类似map
zset 有序的字符串集合,底层跳跃表

扩展类型

Bitmaps 位存储
HyperLogLog count基数类型
Geospatial 坐标类型

事务-打包命令一起执行

multi 开始打包命令
exec 开始执行命令
discard 取消打包命令
在组队时有任何命令错误,最终都不会执行
在组队完成之后有错误的命令不被执行,正确的命令会执行,不存在回滚
类比:编译报错都不执行,运行时报错正确部分执行

乐观锁

在执行multi之前,先执行 WATCH key1 [key2] 可以监视一个或多个key,如果在事务执行之前这些key被其他命令改动,那么事务直接被打断。
UNWATCH 取消监视

redis事务三特性

单独的隔离操作
事务中的所有命令都会序列化、按顺序的执行。事务在执行过程中不会被其他客户端发送来的命令所打断。
没有隔离级别的概念
队列中的命令没有提交之前都不会被实际执行,因为事务提交前任何指令都不会被实际执行
不保证原子性
事务中exec提交之后,如果有一套命令执行失败,其他命令仍然会被执行,没有回滚

乐观锁导致库存遗留问题 -> lua脚本语言

LUA将复杂的或者多步的redis操作写成一个脚本,一次提交给redis执行
LUA脚本类似redis事务功能,具有一定的原子性,打包一组命令进行执行,不会被其他命令插队,可以完成一些redis事务性操作。
仅在2.6版本以上才可使用。
通过lua脚本解决争抢问题,实际上redis利用其单线程特点,用任务队列的方式解决多任务并发问题。

redis持久化操作 RDB和AOF

RDB(redis database)

RDB默认开启
在指定的时间间隔将内存中的数据集快照写入磁盘。*时间间隔 *快照
save会阻塞,bgsave不阻塞,可以再配置文件中设置备份触发条件
fork一个子进程进行快照备份
缺点:①最后一次持久化数据可能丢失②临时文件,空间浪费③性能不好
写时复制技术:先把内存数据写到一个临时文件,完成后再替换dump.rdb,如果直接写到dump.rdb,写的过程中宕机,会导致数据丢失。

RDB配置项

stop-writes-on-bgsave-error yes 当写入硬盘出错时是否关闭RDB
rdbchecksum 默认yes 校验rdb文件准确性,影响10%左右性能
rdbcompression 默认yes 是否压缩rdb文件,影响部分性能
dbfilename

RDB恢复

将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可,redis就会自动加载文件数据至内存了。Redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。
获取 redis 的安装目录可以使用 config get dir 命令

AOF(Append Only File)

以日志的形式来记录每个写操作(增量保存),将redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不改写文件,redis启动之初会读取文件重新构建数据。
appendonly no AOF默认不开启
appendfilename “appendonly.aof”
AOF同步频率设置
appendfsync always 每次命令都会同步,性能较差,完整性好
appendfsync everysec 每秒同步,如果宕机,本秒数据可能丢失
appendfsync no 不主动同步,把同步时机交给操作系统
重写压缩操作 Rewrite 4.0版本后
redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
重写时也用到了临时文件,写时复制技术

AOF和RDB同时开启时,redis只会读取AOF中的数据

AOF恢复

修改appendonly 为 yes
如果遇到AOF文件损坏,通过 bin/redis-check-aof --fix appendonly.aof进行恢复

AOF优劣势

优势:
备份机制更稳健,丢失数据概率更低
可读的日志文本,可处理误操作
劣势:
比起RDB占用更多的磁盘空间
回复备份速度比RDB慢
每次命令都同步的话有性能压力

持久化小总结

官方推荐两个都启用
如果对数据不敏感可单独选用RDB
不建议单独使用AOF,可能有bug
如果只做缓存,可以都不用

redis一主多从复制

master/slave
① 读写分离,主负责写,从服务器负责读
② 容灾快速恢复
info replication查看运行状态
配从库不用配主库
slaveof 将本redis设置为目标redis的从服务器

当某台从服务器挂掉后,再重新启动,会变成主服务器,需要重新执行slaveof命令,会全量复制

当主服务器挂掉后,从服务器依旧是从服务器,没有影响。等主服务器重启后依旧是主服务器。

主从复制流程:
1 当从服务器启动或重新连接上主服务器后,从服务器向主服务器发送进行数据同步的消息
2 主服务器收到消息后,会把主服务器的数据进行持久化rdb文件,把rdb文件发送给从服务器,从服务器拿到rdb后进行读取
3 每次主服务器进行写操作之后,会和在线的从服务器进行数据同步
薪火相传
从服务器slaveof–>从服务器slaveof–>主服务器
反客为主
slaveof no on命令将从机变为主机

哨兵模式

反客为主的主动版
创建sentinel.conf
sentinel monitor mymaster 127.0.0.1 6379 1
其中mymaster为监控对象起的服务名称,1为至少有多少个哨兵同意迁移
当主服务器挂掉后,其中一台从服务器会登基成为新皇帝,此时老皇帝再重启也会变成臣子

选举时的规则:
①选择优先级靠前的:slave-priority 100 值越小优先级越高,5.0后配置项改为 replica-priority
②选择偏移量最大的:偏移量指的是获得原主机数据最全的
③选择runid最小的:redis实例启动后随机生成的一个id

集群模式(无中心化集群)3.0以后

任何一台服务器都可以访问其他服务器
实际搭建:三主6379 6380 6381 三从6389 6390 6391
cluster-enabled yes 打开集群模式
cluster-config-file nodes-6379.conf 设置节点配置文件名
cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒)
,集群自动进行主从切换
将六个节点合成一个集群 老版本需要装ruby环境,新版本不用再装ruby
cd /opt/redis-6.2.1/src 执行
redis-cli --cluster create --cluster-replicas 1 192.168.0.130:6379 192.168.0.130:6380 192.168.0.130:6381 192.168.0.130:6389 192.168.0.130:6390 192.168.0.130:6391
会弹出是否接受分配方式
16384 slots covered
一个redis集群包含16384个插槽(hsah slots)
每个键都属于某一个插槽
插槽平均分配到主节点上
当准备存key的时候,先计算key属于哪个插槽,再存到相应的库中
不能同时添加多个键
如果想加多个键,需要引入组的概念,根据组名来计算分配到哪个插槽
例:
mset name wangyue age 26 错误,不能同时添加多个键
mset name[user] wangyue age[user] 26 正确,根据组名来计算插槽
计算solt命令 :
cluster keysolt key值 返回整数,插槽值
统计某个solt中有多少个键:
cluster countkeysinslot solt值
查看某个solt中的key值,指定数量
cluster getkeysinslot solt值 数量

一个集群至少有三个主节点,分配原则:尽量保证每个主数据库运行在不同的ip地址上,每个从库和主库不在同一个ip地址上
–cluster-replicas 1 集群类型简单模式

集群方式连接:
redis-cli -c -p 6379 连接
cluster nodes 查看节点信息
如果主节点下线,从节点在配置的超时时间后会晋升为主节点(注意超时过程),当主节点恢复后,将变为原来主机的从机。
如果所有某一段插槽的主从节点全部宕机,redis服务是否还能继续?
根据配置项 cluster-require-full-coverage yes(全部挂掉) no(该段插槽将不能再使用,其他正常)

集群优缺点:
优点:实现扩容、分摊压力、无中心化配置相对简单
缺点:多键操作不被支持、多键的事务不被支持、lua脚本不支持
由于集群方案出现较晚,很多公司已经采用其他集群方案,迁移改造过大存在兼容性问题。

内存回收策略(可指定只回收过期 volatile)

LRU 尝试回收最近最少使用的键(最常用)
LFU 最不经常使用的键
RADOM 随机
TTL 优先回收存活时间较短的键
FIFO 先进先出(redis不支持)

缓存穿透、缓存击穿、缓存雪崩

无论是那种情况,其本质就是缓存不存在,大量并发请求打到mysql数据库
因此我们再设计架构时就应该首先对热点数据使用限流互斥锁
只放行一个请求到数据库读取数据并进行缓存,其他请求在代码段自旋,等待缓存到redis

缓存穿透

应用服务器压力突然变大,大量访问、redis命中率降低、一直查询数据库中没有的数据,出现很多非正常URL访问
① 对空值进行缓存
如果查询一个返回数据为空(不管数据是否存在)我们仍然把这个空结果进行缓存,设置空结果过期时间要短,最长不超过五分钟
②设置可访问的白名单
使用bitmaps类型,定义一个可以访问的白名单,名单id作为bitmaps的偏移量,每次访问和bitmaps里面的id进行比较,如果不在bitmaps中,则进行拦截,不允许访问
③采用布隆过滤器
Bloom Filter是1970年由布隆提出的,它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数),布隆过滤器可以用于检索一个元素是否在一个集合中,它的优点是空间效率和查询时间都远远超过一般算法,缺点就是有一定的误识别率和删除困难。
将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
④进行实时监控
当发现redis的命中率开始急速下降时,需要排查访问对象和访问数据,和运维人员配合,可以设置黑名单限制服务。

缓存击穿(热点击穿)

表现:
①数据库访问压力瞬时增加
②redis里面并没有出现大量key过期
③redis正常运行
条件:
redis某个key过期了,大量访问使用到这个key
解决方案:
①预先设置热门数据
在redis高峰访问之前,把一些热门数据提前存入到redis中,加大过期时间
②实时调整
现场监控热门数据,实时调整过期时长
③使用锁
在缓存失效的时候,不立即去load db,
先使用缓存工具某些带成功操作返回值的操作(比如SETNX)去set一个mutex key
当操作返回成功时候,再进行load db操作,并回设缓存,最后删除mutex key
当操作返回失败,证明有线程正在load db,当前线程睡眠一段时间再重试get缓存方法

缓存雪崩

在极少时间段内,查询大量key集中过期的情况
在这里插入图片描述
解决方案:
①构建多级缓存架构(nginx缓存+redis缓存+其他缓存如ehcache)
②使用锁或者队列:
用加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用于高并发的情况
③设置过期标志更新缓存
记录缓存数据是否过期(设置提前量),如果过期会出发通知另外的线程在后台去更新实际key的缓存,“永不过期”,发现要过期时,重开一个线程去load db 更新缓存,这里的“永远不过期”包含两层意思:从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期,从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。
④将缓存失效时间分散开
比如我们可以在原有的失效时间基础上增加一个随机值。比如1-5分钟随机,这样每一个缓存过期的过期时间重复率就会降低,就很难引发集体失效的雪崩事件

分布式锁

跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题
分布式锁主流实现方案:
① 基于数据库实现分布式锁,主键唯一性互斥
② 基于缓存(redis)等
③ 基于zookeeper,顺序节点 临时节点
性能最高redis、可靠性最高zookeeper、最易于实现数据库锁

redis 分布式锁
SETNX 不存在则设置,返回值0或者1 只有del之后 返回值才能是1
存在问题:当SETNX没有过期时间,且一直没有del,会导致阻塞,可以采用 expire 设置过期时间来自动释放锁。
如果上锁之后突然出现异常,无法设置过期时间,使用组合命令解决:
set user wangyue nx ex 10

说明:a卡顿、锁被自动释放、a回归将b的锁给手动释放了
在这里插入图片描述
解决方案:使用UUID防止误删
① set lock uuid nx ex 10 / watch dog机制(当达到过期时间还未执行完,需要续期)
② 释放锁的时候判断是不是自己的锁,防止误删别人的锁
说明:a在校验完uuid之后,锁自动到期,锁被b拿到,此时a线程继续删,会将b的锁删掉
在这里插入图片描述
解决方案:使用lua脚本保证原子性
String script = “if redis.call(‘get’,KEYS[1]==ARGV[1]) then return redis.call(‘del’,KEYS[1] else return 0 end)”

分布式锁总结

为了保证分布式锁可用,我们至少确保锁实现的同时满足以下四个条件:
互斥性,在任意时刻只有一个客户端能够持有锁。NX
不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证其他客户端能加锁。EX
解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端不能把别人加的锁给释放了。UUID
加锁和解锁必须具有原子性LUA
死锁:加锁未释放 -> 使用过期时间
有现成的锁机制框架 Redisson

Redis6.0新功能

ACL访问控制链表

该功能允许根据可以执行的命令和可以访问的键来限制某些链接。
在redis5之前,redis安全规则只有密码控制,和高危命令控制flushdb、keys*、shutdown等。
redis6则提供ACL的功能对用户进行更细粒度的权限控制。
① 接入权限:用户名和密码
② 可以执行的命令
③ 可以操作的key
命令:
使用 acl list命令展现用户权限列表,显示是否启用、能操作的命令、能操作的key
使用acl cat acl cat string查看支持的命令
acl还可以创建用户授权等等功能

IO多线程

IO多线程其实指的是客户端交互部分的网络IO交互处理模块多线程,而非执行命令多线程。redis6执行命令依旧是单线程。
默认也是不开启的,如需开启:
io-threads-do-reads yes
io-threads 4

工具支持cluster

集群时不再需要ruby环境

在这里插入图片描述

开启慢查询日志

慢查询功能可以有效的帮助我们找到Redis可能存在的瓶颈,但在实际使用过程中要注意以下几点:
(1)slowlog-max-len配置建议:线上建议调大慢查询列表,记录慢查询时Redis会对长命令做截断操作,并不会占用大量内存。
  增大慢查询列表可以减缓慢查询被剔除的可能。
(2)slowlog-log-slower-than配置建议:默认值超过10毫秒判定为慢查询,需要根据Redis并发量调整该值。
  由于Redis采用单线程响应命令,对于高流量的场景,如果命令执行时间在1毫秒以上,那么Redis最多可以支撑OPS不到1000,因此对于高OPS的场景的Redis建议设置1毫秒。

(3)慢查询只记录命令执行时间,并不包括命令排队和网络传输时间。因此客户端执行命令的时间会大于命令实际执行的时间。
  因为命令执行排队机制,慢查询会导致其他命令级联阻塞,因此当客户端出现请求超时,
  需要检查该时间点是否有对应的慢查询,从而分析出是否为慢查询导致的命令级联阻塞。
(4)由于慢查询日志是一个先进先出的队列,也就是说如果慢查询比较多的情况下,可能会丢失部分慢查询命令,
  为了防止这种情况发生,可以定期执行slowlog get命令将慢查询日志持久化到其他存储中(例如,MySQL),
  然后可以制作出可视化界面进行查询。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值