Redis缓存知识以及常用架构模式详细

2021年4月7日 8点热度 0条评论 来源: 一路向北⁢

文章目录

Redis缓存

字符串(string)、散列(hash)、列表(lists、集合(set)、有序集合(sorted set)

redis持久化操作,第一种是 rdb 形式,一种是 aof 形式

rdb:属于全量数据备份,备份的是数据

aof:append only if , 增量 持久备份,备份的是指令 如 【set key , del key】

redis 作为缓存的使用,搭配数据库使用的两种方案:

1.jedis整合使用方案

第一层在缓存进行查询,如果查询到数据则直接返回,如果查不到数据则到数据库中查询,并且刷新添加缓存

2.作为mybatis/hibernate二级缓存使用方案

一级缓存: sqlSession 进程缓存,单次链接有效

一、String相关操作

String操作类型:
Set key value                        设置key,value
Get key                              获得值key对应的value的值
Strlen key                           获得key对应的value的长度
Incr key                             对key对应的value的值自增1
Incrby key 【数字】                   指定数字递增
Decr key                             对key对应的value的值自减1
Decrby key 【数字】                   指定数字递减
Getrange key 0 -1                    显示指定范围的key对应的value值(0到-1显示全部)
Setrange key 0 x                     从零开始,修改原来的值,将原来的修改;原来为“10”执行后为“x0”
Setex key 【过期时间秒】 value        设置值的同时给定过期时间
Setnx key value                      若key不存在,设置value值才会成功【可用于分布式锁】
Mset key value key value key value   一次性设置多个值
Mget key key key                     一次性获得多个值
Msetnx key value key value           一次性设置多个之前不存在的值,若其中有一个key存在的话就不会成功

二、list相关操作

list操作类:
Lpush key value value value                   存值,结果[先进后出]
Rpush key value value value                   存值,结果按顺序,[先进先出]
Lrange key 0 -1                               查看list中的数据(0到-1显示全部)
Lpop key                                      出栈,从左到右,每次出栈一个元素,相当于从list中删除
Rpop key                                      出栈,从右到左,每次出栈一个元素,相当于从list中删除
Lindex key 【下标】                            具体显示list中角标对应的值,从0开始计数
Lrem key 【数字】 【元素】                       删除list中几个相同的元素
Ltrim key 【下标1】【下标2】                     截取出保存下标1到下标2的元素,其他下标对应的元素被删除
Lset key 【下标】 【元素】                       修改list数字中对应下标的元素
Linsert key before/after 【元素1】【元素2】      在元素1的前面/后面 插入元素2,若元素1有多个,则只对最前面的那个匹配元素1的元素的前后进行操作
 
                                               r是对底部进行操作,l是对顶部进行操作

三、set相关操作

set操作:
Sadd key 【元素1】【元素2】【元素3】        一次性添加多个元素,会自动去重
Smembers key                              查看当前key中的所有元素
Scard key                                 获取元素个数
Srem key 【元素】【元素】【元素】             删除指定元素
Srandmember key 【数字】                   在指定的set中随机的显示三个数字
Spop key 【数字】                          随机出栈指定的个数个元素
Smove key1  key2 【key1中的元素】          将key1中的元素剪切到key2中
Sdiff  key1 key2                          显示出存在于set1中但不存在于set2中的元素
Sinter key1 key2                          显示出key1和key2共同拥有的元素      交集
Sunion key1 key2                          显示出key1和key2两者相加的所有元素  并集

四、hash相关操作

hash的结构类似于 Map< String, Map<Object, Object> >

hash操作:  键值对
Hset  hash key1 value1 key2 value2                 设置值,hash允许重复 当key重复的时候会覆盖前面出现的key
Hget hash key                                      获取value
Hgetall hash                                       获取所有的key和value
Hmset hash  key1  value1  key2  value2             一次存一个对象的多个不同的value
Hdel hash key                                      删除指定的key
Hlen hash                                          查看长度
Hexists hash key                                   判断hash中key是否存在
Hkeys  hash                                        获取所有的key值
Hvals hash                                         获取所有的value值
Hincrby hash key 【整数数字】                       指定的key按指定的数字递增
Hincrbyfloat hash key 【小数】                      指定的key按指定的小数递增
Hsetnx hash key value                              若key值在hash中不存在就添加

五、zset相关操作(有序集合)

zset操作:有序集合   key不允许重复  zset(key value)key值必须是数字(key为序号,依据此序号来排序)
Zadd zset key1 value1 key2 value2    一次性添加多个数据
Zrange zset 0 -1                    显示所有的value值,当范围为 0 到 -1 时显示全部,下标从0开始
Zrange zset 0 -1 withscores       现在所有的key和value,当范围为 0 到 -1 时显示全部,下标从0开始
Zrangebyscore zset 【key1】【key2】 显示key1到key2之间的key的值(包含数字1和数字2) 数字2比数字1大
Zrangebyscore zset 【(key1】【key2】 显示key1到key2之间的key的值(加了前括号就不包含改数字)
zrangebyscore zset 【key1】【key2】 limit【key3】【数字4】 从结果中截取key3开始截取 数字4 个元素
Zrem zset value1 value2 value3          删除指定的key中的value
Zcard zset                              统计zset中key的个数
Zcount zset 【key1】【key2】             统计key1到key2之间的key个数
Zrank zset value                        通过value的值value所在的下标
Zscore zset value                       获取key的值
Zrevrank zset value                     显示倒叙过来的下标
Zrevrange zset 0 -1                     倒叙显示所有value
Zrevrangbyscore zset [key1] [key2]      倒叙显示key1到key2的之间的value值
                     这里key1比key2大

六、redis相关操作

Redis 默认有16个数据库,即16个database
Select 【数字】       转到第几个数据库
Dbsize               查看当前数据库有多少个key
Keys *               当前库的key查询出来
Flushdb              清除当前库
Flushall             清除所有库
Auth 【密码】         认证密码
Exists【key的名字】   判断某个key是否存在
Move key 【db序号】   将当前库的key剪切到指定db序号
Expire key【时间秒】  为给定的key设置过期时间,过期后会移除系统
Ttl key              查看还有多少秒过期,-1表示永远不过期,-2表示已过期
Type key             查看你的key是什么类型
Del key              删除key

事务ACID特性:

原子性:事务作为一个整体被执行,包含在其中对数据的操作要么全部被执行,要么都不执行

一致性:事务应该确保数据库的状态从一个一致的状态转变成另一个一致的状态。

隔离性:多个事务并发执行时,一个事务的执行不应该影响其他事务的执行

持久性:已被提交的事务对数据库的修改应该永久被保存在数据库中

七、redis集群

tenxun-redis:0> info replication # 查看redis信息
"# Replication
role:master   # 角色 master
connected_slaves:0  # 没有从机
master_replid:6bddb5abc0f8067588d1d1bdba63cc7f2fbd1e88
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
"

master-slaver 一主二从

主机可以写,从机不能写只能读,主机中的所有信息和数据,都会自动被从机保存

master 配置文件:redis-6379.conf

port 6379
daemonize yes # 后台运行
logfile "6379.log" # 日志文件
dbfilename "dump-6379.rdb" # 持久化文件
dir "/opt/soft/redis/data"

pidfile /var/run/redis_6379.pid

slave-1 配置文件:redis-6380.conf

port 6380
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
dir "/opt/soft/redis/data"
# 关键配置:将这个 redis 指定为某个第一个 redis 的 slaver
slaveof 127.0.0.1 6379

slave-2 配置文件:redis-6381.conf

port 6381
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
dir "/opt/soft/redis/data"
# 关键配置:将这个 redis 指定为某个第一个 redis 的 slaver
slaveof 127.0.0.1 6379

启动redis:

redis-server redis-6379.conf

连接客户端:

redis-cli -p 6379

7.1sentinel集群

sentinel-26379.conf

#设置 sentinel 工作端口
port 26379
#后台运行 
daemonize yes
#日志文件名称
logfile "26379.log"
#设置当前 sentinel 监控的 redis ip 和 端口
sentinel monitor mymaster 127.0.0.1 6379 2
#设置判断 redis 节点宕机时间
sentinel down-after-milliseconds mymaster 60000
#设置自动故障转移超时
sentinel failover-timeout mymaster 180000
#设置同时故障转移个数
sentinel parallel-syncs mymaster 1

sentinel-26380.conf

#设置 sentinel 工作端口
port 26380
#后台运行 
daemonize yes
#日志文件名称
logfile "26380.log"
#设置当前 sentinel 监控的 redis ip 和 端口
sentinel monitor mymaster 127.0.0.1 6379 2
#设置判断 redis 节点宕机时间
sentinel down-after-milliseconds mymaster 60000
#设置自动故障转移超时
sentinel failover-timeout mymaster 180000
#设置同时故障转移个数
sentinel parallel-syncs mymaster 1

sentinel-26381.conf

#设置 sentinel 工作端口
port 26391
#后台运行 
daemonize yes
#日志文件名称
logfile "26381.log"
#设置当前 sentinel 监控的 redis ip 和 端口
sentinel monitor mymaster 127.0.0.1 6379 2
#设置判断 redis 节点宕机时间
sentinel down-after-milliseconds mymaster 60000
#设置自动故障转移超时
sentinel failover-timeout mymaster 180000
#设置同时故障转移个数
sentinel parallel-syncs mymaster 1

针对几个监控设置的配置做一下详细说明:

  • sentinel monitor [master-group-name] [ip] [port] [quorum]

    这个命令中【master-group-name】是 master redis 的名称;【ip】和【port】分别是其 ip 和端口,很好理解。最后一个参数【quorum】是”投票数“

    举个栗子,redis 集群中有3个 sentinel 实例,其中 master 挂掉了,如果这里的票数是2,表示有2个 sentinel 认为 master 挂掉啦,才能被认为是正真的挂掉啦。其中 sentinel 集群中各个 sentinel 之间通过 gossip 协议互相通信。
    具体怎样投票还涉及到 redis 集群中的【主观下线】和【客观下线】的概念,后面再详细介绍。

    • down-after-milliseconds

      sentinel 会向 master 发送心跳 PING 来确认 master 是否存活,如果 master 在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个 sentinel 会主观地认为这个 master 已经不可用了。而这个down-after-milliseconds 就是用来指定这个“一定时间范围”的,单位是毫秒。

    • failover-timeout

      这个参数 redis 官方文档中并未做详细说明,但是很好理解,就是 sentinel 对 redis 节点进行自动故障转移的超时设置,当 failover(故障转移)开始后,在此时间内仍然没有触发任何 failover 操作,当前sentinel 将会认为此次故障转移失败。

    • parallel-syncs

      当新master产生时,同时进行 slaveof 到新 master 并进行同步复制的 slave 个数,也就是同时几个 slave 进行同步。因为在 salve 执行 salveof 与新 master 同步时,将会终止客户端请求,因此这个值需要权衡。此值较大,意味着“集群”终止客户端请求的时间总和和较大,此值较小,意味着“集群”在故障转移期间,多个 salve 向客户端提供服务时仍然使用旧数据。

      我们配置三个 sentinel 几点组成一个 sentinel 集群,端口分别是 23679,23680,23681

然后就可以启动 sentinel 集群了

启动 sentinel 有两种方式:

  1. redis-sentinel /path/to/sentinel.conf
  2. redis-server /path/to/sentinel.conf --sentinel

7.2Redis内置高可用集群

每个redis的配置:

	daemonize yes		// redis后台运行
	prot 7000
	cluster-enabled yes   // 开启集群
	cluster-config-file nodes.conf //集群的配置,首次启动自动生成
	cluster-node-timeout 5000 // 请求超时(单位:毫秒)
	appendonly yes       // aof日志开启,它会操作日志
	bind 127.0.0.1 172.16.244.133   // (Redis安装这台的内网IP) 添加一个内网ip 不影响集群
  • 创建集群

    • 前面已经准备好了搭建集群的redis节点,接下来我们要把这些节点都串连起来搭建集群。官方提供了一个工具:redis-trib.rb(/usr/local/redis-4.0.6/src/redis-trib.rb) 看后缀就知道这鸟东西不能直接执 行,它是用ruby写的一个程序,所以我们还得安装ruby.
      yum -y install ruby ruby-devel rubygems rpm-build 
               
      gem install redis
    
  • 如果gem install redis发现报错

    curl -L get.rvm.io | bash -s stable 
    
    source /usr/local/rvm/scripts/rvm
    
    rvm list known
    
    rvm install 2.3.3
    
    rvm use 2.3.3
    
    ruby --version
    
    gem install redis
    
  • 开启集群工作

    cd /usr/local/redis-4.0.6/src
    ./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \
          127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
    
  • 测试集群是否正常

    ./redis-cli -c -p 7000 
    
  • 如果搭建失败,请用此命令将所有启动的redis server一个个关闭掉

    ./redis-cli -p 7000 shutdown
    

简介:Redis集群特点介绍

  • Redis 集群的数据分片

    • 概念:Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,

    • 举个例子,比如当前集群有3个节点,那么:

      • 节点 A 约包含 0 到 5500号哈希槽.
      • 节点 B 约包含5501 到 11000 号哈希槽.
      • 节点 C 约包含11001 到 16384号哈希槽.
    • 查看集群信息redis-cli -p 7000 cluster nodes | grep master

      • 这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.
    • 从Redis宕机讲解分布式锁执行的异常场景流程

    • 从Server服务宕机讲解分布式锁执行的异常场景流程

  • Redis 集群的主从复制模型

    • 为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品. 在我们例子中具有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用.Redis集群做主从备份解决了这个问题
  • Redis 一致性保证

    • 主节点对命令的复制工作发生在返回命令回复之后, 因为如果每次处理命令请求都需要等待复制操作完成的话, 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。 注意:Redis 集群可能会在将来提供同步写的方法。 Redis 集群另外一种可能会丢失命令的情况是集群出现了网络分区, 并且一个客户端与至少包括一个主节点在内的少数实例被孤立。
  • 手把手测试故障转移

    redis-cli -p 7000 debug segfault
    redis-cli -p 7001 cluster nodes | grep master
    

简介:玩转Redis集群节点分片重哈希

  • 采用SSH连接远程服务器

    • ssh命令安装过程:https://blog.csdn.net/DanielAntony/article/details/87997574
  • 集群重新分片

    手动处理solt节点槽重新分片

    • ./redis-trib.rb reshard 127.0.0.1:7000
    • 你想移动多少个槽( 从1 到 16384)? all
  • 添加一个新的主节点

    ./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
    
  • 添加一个新的从节点

    ./redis-trib.rb add-node --slave 127.0.0.1:7006 127.0.0.1:7000
    
  • 移除一个节点

    ./redis-trib.rb del-node 127.0.0.1:7000 <node-id> 第一个参数是任意一个节点的地址,第二个节点是你想要移除的节点地址。

    • 移除主节点【先确保节点里面没有slot】
      • 使用同样的方法移除主节点,不过在移除主节点前,需要确保这个主节点是空的. 如果不是空的,需要将这个节点的数据重新分片到其他主节点上.
      • 替代移除主节点的方法是手动执行故障恢复,被移除的主节点会作为一个从节点存在,不过这种情况下不会减少集群节点的数量,也需要重新分片数据.
    • 移除从节点 直接移除成功

简介:一起来进行Redis集群探索

  • 图解探索【是不是只学习master cluster就行了】

  • 分析cluster集群方式原理

    • 水平切分于垂直切分相比,相对来说稍微复杂一些。因为要将同一个表中的不同数据拆分到不同的数据库中。 分片是一种基于数据库分成若干片段的传统概念扩容技术,它将数据库分割成多个碎片并将这些碎片放置在不同的服务器上。

    • 垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非常低,相互影响很小,业务逻辑非常清晰的系统。按照业务维度将不同数据放入不同的表

  • 故障转移

    • 通过gossip节点信息同步实现,实现slave作为master的备份节点,并实现故障剔除master节点采用slave替换,并在替换完之后将结果通知到其他节点。
  • Twitter推特公司twemproxy服务端分片和客户端分片集群解决方案

八、缓存穿透

  • 什么是缓存穿透?你有什么解决方案来防止缓存穿透?
    • 缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候, 在缓存中找不到对应key的value,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次 无用的查询)。这样请求就绕过缓存直接查数据库
  • 你有什么解决方案来防止缓存穿透?
    • 采用布隆过滤器BloomFilter
      • 将所有可能存在的数据哈 希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
    • 缓存空值
      • 如果一个查询返回的数据为空(不管是数据不 存在,还是系统故障)我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库

九、缓存雪崩

什么是缓存雪崩?你有什么解决方案来防止缓存雪崩?

  • 如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。 由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU 和内存造成巨大压力,严重的会造成数据库宕机
  • 你有什么解决方案来防止缓存雪崩?
    • 加锁排队
      • key: whiltList value:1000w个uid 指定setNx whiltList value nullValue mutex互斥锁解决,Redis的SETNX去set一个mutex key, 当操作返回成功时,再进行load db的操作并回设缓存; 否则,就重试整个get缓存的方法
    • 数据预热
      • 缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key
    • 双层缓存策略
      • C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期。
    • 定时更新缓存策略
      • 失效性要求不高的缓存,容器启动初始化加载,采用定时任务更新或移除缓存
        的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。 由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU 和内存造成巨大压力,严重的会造成数据库宕机
  • 你有什么解决方案来防止缓存雪崩?
    • 加锁排队
      • key: whiltList value:1000w个uid 指定setNx whiltList value nullValue mutex互斥锁解决,Redis的SETNX去set一个mutex key, 当操作返回成功时,再进行load db的操作并回设缓存; 否则,就重试整个get缓存的方法
    • 数据预热
      • 缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key
    • 双层缓存策略
      • C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期。
    • 定时更新缓存策略
      • 失效性要求不高的缓存,容器启动初始化加载,采用定时任务更新或移除缓存
    • 设置不同的过期时间,让缓存失效的时间点尽量均匀
    原文作者:一路向北⁢
    原文地址: https://blog.csdn.net/suprezheng/article/details/110694570
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。