Redis知识总结

1、redis数据类型

1.1 String

string类型是redis的基本类型,string类型是二进制安全的,可以包含任何数据,比如jpg图片或者序列号对象。string类型的值最大能存储512M

常用命令:

SET key value, 设置指定的key值
GET key,获取指定key的值
GETRANGE key start end,返回key中字符串值的子字符串
GETSET key value,将给定key的值设为value,并返回key的旧值。返回给定 key 的旧值。 当 key 没有旧值时,即 key 不存在时,返回 nil 。
GETBIT key offset,对key所存储的字符串值,获取指定偏移量上的位(bit)。
SETBIT key offset value,对key所存储的字符串值,设置或清除指定偏移量上的位(bit).
MGET key1[key2...],获取所有(一个或多个)给定key的值。
MSET key value [key value ....],同时设置一个或多个key-value值对
SETEX key seconds value,将值value关联到key,并将key的过期时间设置为多少seconds
PSETEX key millseconds value,设置过期时间为毫秒的key-value值对
SETNX key value,只有在key不存在的情况下设置key的值
STRLEN key,返回key所存储的字符串值的长度。
INCR key,将key中存储的数字值增一
INCRBY key increment,将key所存储的值加上给定的增量值 (increment)。
DECR key,将key中存储的值减一
DECRBY key decrement,将key所存储的值减去戈丁的减量值(decrement)。
APPEND key value,将value值追加到key所对应的value的末尾。

1.2 Hash

redis hash 是一个string类型的field(字段)和value(值)的映射表,hash特别适合于存储对象。

适用场景:

电商购物车实现场景可以通过hset 添加商品 hincrby 添加商品数量 hlen获取商品总数 hdel删除商品 hgetall获取购物车所有商品

可以做单点登录存放用户信息

常用命令:

HDEL key field1[field2],删除一个或多个哈希表字段
HEXISTS key field,查看哈希表key中,指定的字段是否存在
HGET key filed,获取存储在哈希表中指定的字段的值
HGETALL key,获取在哈希表中指定key的所有字段和值
HINCRBY key field increment,为指定字段的整数值加上增量increment
HKEYS key,获取所有哈希表中的字段
HLEN key,获取哈希表中字段的数量
HMGET key field1[field2],获取所有给定字段的值
HMSET key field value[field2 value2],同时将多个field-value对设置到哈希表key中
HSET key field value,将哈希表key中的字段field的值设为value
HSETNX key field value,只有在字段field不存在时,设置哈希表字段的值。
HVALS key,获取哈希表中所有的值
HSCAN key cursor[MATCH pattern][COUNT count],迭代哈希表中的键值对

1.3 List

redis中的列表是简单的字符串列表,按照插入的顺序排序,可以添加一个元素到列表的头部或尾部。

常用命令:

BLPOP key timeout,移出并获取列表中的第一个元素,如果列表中没有元素会阻塞列表直到等待超时或发现有元素可弹出为止。
BRPOP key timeout,移出并获取列表中的最后一个元素,如果列表中没有元素会阻塞列表直到等待超时或发现有元素可弹出为止。
LINDEX key index,通过索引获取列表中的元素
LLEN key,获取列表的长度
LPOP key, 移出并获取列表的第一个元素
LPUSH key value,将一个值插入到列表头部
LPUSHX key value,将一个值插入到已存在的列表头部
LRANGE key start stop,获取列表指定范围内的元素
LREM key count value,移除列表元素
LSET key index value,通过索引设置列表元素的值
LTRIM key start stop,让列表只保留指定区间内的元素,不在区间内的元素都将被删除。
RPOP key,移除列表的最后一个元素,返回值为移除的元素
RPUSH key value,将一个值插入到列表的尾部
RPUSHX key value,将一个值插入到已存在列表的尾部
...

1.4 Set

Set是string类型的无序集合,集合中的成员不可重复。

常用命令:

SADD key memeber[meember1],向集合中添加一个或多个元素
SCARD key,获取集合的成员数量
SDIFF key1 [key2],返回第一个集合与其他集合的差异,可以认为是第一个集合中独有的元素
SDIFFSTORE destination key1[key2],将差异的值报错到destination中
SINTER key1[key2],返回给定集合的交集
SINTER destination key1[key2],返回给定集合的交集并保存到destination中
SISMEMBER key member,判断集合中是否存在member元素
SMEMBERS key,返回集合中的所有元素
SMOVE source destination member,将元素member从source集合中移动到destination集合中
SPOP key,移除并返回集合中的一个随机元素
SRANDMEMBER key[count],返回集合中一个或多个随机成员
SUNION key1[key2],返回给定集合的并集
SUNIONSTORE destination key1[key2],将给定集合的并集保存到destination中
SSCAN key cursor [MATCH pattern][COUNT count],迭代集合中的元素

2、缓存

使用redis设计一个缓存系统,需要考虑以下问题:缓存穿透、缓存击穿、缓存雪崩

2.1 缓存穿透

key对应的值在数据库中不存在,每次针对此key的请求从缓存中获取不到,请求就会发起数据库请求,从而可能压垮数据库。

例如,对于系统A,假设一秒 5000 个请求,结果其中 4000 个请求是黑客发出的恶意攻击。黑客发出的那 4000 个攻击,缓存中查不到,每次你去数据库里查,也查不到。这种恶意攻击场景的缓存穿透就会直接把数据库给打死。

解决办法:

2.1.1 bloomFilter

采用布隆过滤器,将所有可能存在的数据哈 希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。

redis的布隆过滤器,不是原生自带的,而是通过module加载进去。redis在4.0版本中加入了module的功能。

布隆过滤器主要有两个命令:

bf.add添加元素到布隆过滤器中,bf.add strs xy

bf.exists判断某个元素是否在过滤器中,bf.exists strs xy

2.1.2 缓存空值

如果一个查询返回的数据为空(不管是数据不 存在,还是系统故障)我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库

2.1.3 接口限流与熔断降级

接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制

2.2 缓存击穿

缓存击穿,指的是一个key非常热点,大量并发集中对这个点进行访问,当这个key在失效的瞬间,持续的大并发直接落到了数据库上,就在这个key的点上击穿了缓存。

解决方法:

2.2.1 永不过期

设置该热点数据永不过期

2.2.2 互斥锁

业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。

伪代码如下:

public String get(key) {
      String value = redis.get(key);
      if (value == null) { //代表缓存值过期
          //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
      if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  //代表设置成功
               value = db.get(key);
                      redis.set(key, value, expire_secs);
                      redis.del(key_mutex);
              } else {  //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
                      sleep(50);
                      get(key);  //重试
              }
          } else {
              return value;      
          }
 }

2.3 缓存雪崩

缓存雪崩是指,设置的缓存采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到数据库,数据库瞬间压力过重导致雪崩

解决方法:

2.3.1 不同过期时间

针对不同的缓存key,设置不同的过期时间,让缓存失效的时间点尽量均匀

2.3.2 双层缓存策略

C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期。

可以同时使用redsi和memcache缓存,请求->redis->memcache->db;

2.3.3 永不失效

设置热点数据key永不失效

2.3.4 加锁

加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。

/伪代码
public object GetProductListNew() {
    int cacheTime = 30;
    String cacheKey = "product_list";
    String lockKey = cacheKey;

    String cacheValue = CacheHelper.get(cacheKey);
    if (cacheValue != null) {
        return cacheValue;
    } else {
        synchronized(lockKey) {
            cacheValue = CacheHelper.get(cacheKey);
            if (cacheValue != null) {
                return cacheValue;
            } else {
              //这里一般是sql查询数据
                cacheValue = GetProductListFromDB(); 
                CacheHelper.Add(cacheKey, cacheValue, cacheTime);
            }
        }
        return cacheValue;
    }
}

3. 哨兵机制

Redis Sentinel是一个分布式系统,为Redis提供高可用性解决方案:由一个或多个sentinel实例组成,可以监视任意多个主服务器,以及这些服务器属性的所有从服务器,并在被监视主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RarSCjj1-1645667352736)(images/image-20210413151239600.png)]

当server1掉线后,首先察觉主服务器下线,然后通过选举算法找出新的主服务器。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qVIzCfFg-1645667352738)(images/image-20210413151322275.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ux58fXQp-1645667352740)(images/image-20210413151335563.png)]

3.1 主要功能:

1).集群监控,负责监控redis master和slave进程是否正常工作。

2).消息通知,如果某个redis实例有故障,负责发送消息通知给管理员。

3).故障转移,如果master node挂掉,会自动转移到slave node上。

4).配置中心,如果故障转移发生了,通知client客户端新的master地址

故障转移时,判断一个master node宕机,需要大部分哨兵都同意才行。

哨兵至少需要三个实例,来保证自己的健壮性。

哨兵+redis主从的部署架构,不会保证数据零丢失的,只能保证redis集群的高可用性。

3.2 定时监控

1、每个哨兵节点每10秒会向主节点和从节点发送info命令获取最新拓扑结构图,哨兵配置时只要配置对主节点的监控即可,通过向主节点发送info,获取从节点的信息,并当有新的从节点加入时,可以马上感知到。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uXljpj7O-1645667352742)(images/image-20210413153352437.png)]

2、每个哨兵节点每隔2秒会向redis数据节点的指定频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其他哨兵节点的信息及对主节点的判断,其实就是通过消息publish和subscriber来完成。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ycuDI8xU-1645667352745)(images/image-20210413153545475.png)]

3、每隔1秒,每个哨兵会向主节点、从节点及其余哨兵节点发送一次ping命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o50XAss7-1645667352747)(images/image-20210413153714722.png)]

3.3 主观下线

sentinel会以每秒一次的频率向所有与其建立了命令连接的实例(master,从服务,其他sentinel)发ping命令,通过判断ping回复是有效回复,还是无效回复来判断实例时候在线(对该sentinel来说是“主观在线”)。

所谓的主观下线,指的是单个sentinel认为某个服务下线。

sentinel配置文件中的down-after-millseconds设置了判断主观下线的时间长度,如果节点在down-after-millseconds毫秒内,返回的都是无效回复,那么该sentinel任务该实例已主观下线。

3.5 客观下线

当主观下线的节点是主节点时,此时该哨兵3节点通过指令sentinel is-masterdown-by-addr寻求其他哨兵节点对该主节点的判断,如果其他哨兵也任务该主节点主观下线了,则当认为主观下线的票数超过了(quorum)选举个数,此时哨兵节点则认为该主节点确实有问题。也就是大部分哨兵认为该主节点主观下线了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m6NYf6HI-1645667352749)(images/image-20210413154611216.png)]

3.6 哨兵leader选举

如果主节点被判断为客观下线后,需要选出一个哨兵节点来完成故障转移工作,选举流程如下:

1、每个哨兵都可以成为领导者,比如哨兵3,当它确认主节点客观下线后,会向其他的哨兵发送is-masterdown-by-addr命令,征求判断并将自己设置为领导者。

2、当其他哨兵收到此命令时,可以同意或决绝其成为领导者

3、如果哨兵3发现自己在选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,则循环继续选举。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-giVGdOuP-1645667352750)(images/image-20210413155810054.png)]

3.6 故障转移

自动故障转移机制,是sentinel领导者在从节点中选择新的主节点的过程。

1、过滤掉主观下线的节点

2、选择slave-priority最高的节点,如果有,则返回,如果没有继续选择

3、选择出复制偏移量最大的节点,因为复制偏移量越大则数据复制越完整,如果有则返回,没有继续选择

4、选择run_id最小的节点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltEX2A1b-1645667352751)(images/image-20210413160426660.png)]

更新主从状态:

通过slaveof no one命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点。

将已下线的主节点设置成新的主节点的从节点,当其回复正常时,复制新的主节点,变成新的主节点的从节点

同理,当已下线的服务重新上线时,sentinel会向其发送slaveof命令,让其成为新主的从节点

3.7 springboot集成

springboot如何集成哨兵模式的redis集群?

依赖:

<!--spirngboot版本为2.x-->
<!-- 加载spring boot redis包,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池,这里使用jedis连接池 -->
<!-- 加载spring boot redis包 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
	<!-- 排除lettuce包,使用jedis代替-->
	<exclusions>
		<exclusion>
			<groupId>io.lettuce</groupId>
			<artifactId>lettuce-core</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
	<version>2.9.0</version>
</dependency>

application.yml配置文件:

##配置redis的连接信息
#spring.redis.host=192.168.184.135
#spring.redis.port=6379
#spring.redis.password=123456
#连接超时时间
#spring.redis.timeout=6000ms
##Redis数据库索引(默认为0)
#spring.redis.database=0
## 连接池配置,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池
##连接池最大连接数(使用负值表示没有限制)
#spring.redis.jedis.pool.max-active=8
##连接池最大阻塞等待时间(使用负值表示没有限制)
#spring.redis.jedis.pool.max-wait=-1s
##连接池中的最大空闲连接
#spring.redis.jedis.pool.max-idle=8
##接池中的最小空闲连接
#spring.redis.jedis.pool.min-idle=0
 
##################################
 
#哨兵模式redis集群配置,就是为了通过redis找主节点,做到无感切换
#spring.redis.password=123456
#spring.redis.sentinel.master=mymaster
#spring.redis.sentinel.nodes=192.168.184.133:26379,192.168.184.135:26379,192.168.184.136:26379
##连接超时时间
#spring.redis.timeout=6000ms
##Redis数据库索引(默认为0)
#spring.redis.database=0
## 连接池配置,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池
##连接池最大连接数(使用负值表示没有限制)
#spring.redis.jedis.pool.max-active=8
##连接池最大阻塞等待时间(使用负值表示没有限制)
#spring.redis.jedis.pool.max-wait=-1s
##连接池中的最大空闲连接
#spring.redis.jedis.pool.max-idle=8
##接池中的最小空闲连接
#spring.redis.jedis.pool.min-idle=0
 
 
#############################
 
#连接超时时间
spring.redis.cluster.nodes=192.168.184.133:7000,192.168.184.133:7001,192.168.184.133:7002,192.168.184.133:7003,192.168.184.133:7004,192.168.184.133:7005
spring.redis.password=123456
spring.redis.timeout=6000ms
#Redis数据库索引(默认为0)
spring.redis.database=0
# 连接池配置,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池
#连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
#连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1s
#连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
#接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0

java客户端调用:

public static void testSentinel() throws Exception   {
 
         String   masterName = "mymaster";
 
         Set<String>   sentinels = new HashSet<>();
 
         sentinels.add("192.168.92.128:26379");
 
         sentinels.add("192.168.92.128:26380");
 
         sentinels.add("192.168.92.128:26381");
 
 
 
         JedisSentinelPool   pool = new JedisSentinelPool(masterName, sentinels); 
 
         Jedis   jedis = pool.getResource();
 
         jedis.set("key1", "value1");
 
         pool.close();
 
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值