redis学习笔记

redis基础知识

  • 什么是redis

    是一种基于内存的key-value数据库,用作数据库和缓存。基础的数据类型只有5种;string,list,hash,set和SortedSet(有序集合)。额外还支持HypeLogLog(统计redis的基数,使用很小的内存就可以实现很大数据的统计,会有误差。实际是一种算法的支持),Geo(Redis的地理数据的存储,可以两地的经纬度计算距离和搜索一点范围的其他地名,也是一种算法支持)等。使用场景,缓存,抽奖,发布式session,排行榜,计数,附近地理等。

  • redis的安装(在线安装)

  1. redis是c语言编写的所以需要c环境
yum install -y gcc-c++
  1. 在线下载redis的tar包(进入你要保存我文件的目录)
wget http://download.redis.io/releases/redis-5.0.3.tar.gz
  1. 解压(解压到你需要的目录,这里是当前目前)
tar -zxvf redis-5.0.3.tar.gz
  1. 编译安装(进入解压后的目前,PREFIX是你要安装的目前录自定义)
make install PREFIX=/usr/softOPT/redis
  1. 启动redis 使用bin目录下的 (在解压目录下)
 bin/redis-server ../redis.conf
  • redis的数据结构

    redis中的字符串是二进制安全的。使用的是c语言的char类似但是这个char会吧 ‘\0’认为是结束符,所以不能存储比如图形吗,视频等。但是redis使用的是sds(simple dynamic string)。不会将 ‘\0’读作结束符号。

雪崩,击穿和穿透

  • 雪崩
    在系统流量高峰期的时候大面积的key同时失效,使得大量的流量直接打在DB上,瞬间将DB打崩。此时问题比较严重,重启数据库也会被瞬间打崩。此时最主要的是卷铺盖了哈哈哈。
    解决方式也比较简单:所有的key失效时间随机
 redisTemplate.opsForValue().set("name","小J", (long) Math.ceil(1000L + Math.random() * 1000),TimeUnit.SECONDS);
  • 击穿
    与雪崩有一点像,就是有一个热点key正在被大量的请求,此时哈,这个热点key突然地失效掉。哦豁,大量的流量又直接打在DB上,(额,为什么我要说又?)。
    解决方法也比较粗暴:设置热点key用不过期,或者加互斥锁
  • 穿透
    这个穿透有点恶意访问的味道,就是会一种请求一个不可能存在数据,每次都可以透过redis直接打在DB上。
    解决方法:
    1. 第一次访问不存在后也将这个key设置进redis中value为null,但是过期时间短一些比如30秒,不然正常的请求也过不来了哈哈哈。
    2. 后台直接拦截不合理的key。
    3. 使用布隆过滤器(BoolmFilter)【使用一种算法和数据结构快速的判断这个key是否存在】

redis的持久化

	持久化就是将内存中的数据写入磁盘,如果有aof方式,优先使用aof恢复。
  1. RDB方式(适合做冷备)
    快照方式,将某一运行时刻redis的内存数据持久化到磁盘,具有周期性。生成快照的方式有两种:save和gbsave。默认是bgsave。

save:是同步执行,会阻塞其他客户端命令,但不会消耗内存资源。bgsave是异步执行,会生成fork子进程器执行持久化,不会阻塞客户端其他命令,但是会消耗内存资源。
根据配置redis.conf的策略进行持久化;如
redis学习笔记_第1张图片

优点:
	1. 文件比较小
	2. 对redis的性能影响相对小
	3. 恢复快
缺点:
	1. 数据不全面
	2. 如果生成的快照比较大,会使得客户端稍微有卡顿。因为有fork子进程去做持久化
  1. AOF方式(适合热备)
    即append-only的缩写,使用日志追加的形式将redis客户端的命令写入到aof文件中。

有三种追加方式
appendfsync always 每次有新命令就追加到aof中,最慢但是最安全。
appendfsync everysec 将在一秒内执行的命令追加到aof文件中。和RDB速度差不多,但是在发生故障后只丢失一秒的数据(默认
appendfsync no 从不进行fync,交给操作系统,更快但是也更不安全

默认是第二种方式:
redis学习笔记_第2张图片

  优点:
  	1. 数据比较全,最多也就丢失1秒的数据
  	2. 追加的方式去写,省去磁盘寻址,写入性能高
  	3. 使用非常可读的形式追加数据,比较适合性数据误删的紧急恢复了
  缺点:
  	1. 文件大
  	2. 开启aof方式后,redis的QPS要比只RBD支持时要低,因为每秒都要去异步刷新一次日志

redis主从复制(master-slave)【垂直扩展】

由于redis实现的pub/sub模式,所以任何一台从服务器可以订阅任何主服务器的频道,实现同步复制。而且从服务器可以订阅任何从服务器的主服务器所以redis可以实现单层树结构的复制。

原理

  • 全量复制第一次建立socket连接的时候进行

      1. slave跟master建立socket长连接,并发送psync【见下面的注意点】命令同步数据
      2. master收到psync命令后,执行bgsave命令,生产最新的RBD文件,并发送给slave。随之发送replid_id和offset
      3. master将生成RDB文件期间的命令写入复制积压缓冲区(replication backlog buffer)并发送给slave
      4. slave将master发送过来的buffer和RDB生成新的RDB文件并加载到内存中
      5. slave初始化完成
    

其中 replid是这个 master 的复制 ID,对于master来说和4.0以前的run_id是一样的意义。但对于slave来说,它保存的复制 ID(即 replid) 表示当前正在同步的 master 的复制 ID 。master_replid 2 则表示前一个 master 的复制 ID(如果它之前没复制过其他的 master,那这个字段没用),这个在主从角色发生改变的时候会用到 。而 offset 则是 master 当前的复制偏移量,slave 会将这个值作为自己的初始化偏移量
示意图:
redis学习笔记_第3张图片
部分复制
部分复制是4.0版本后出现的为了优化slave由于网络等原因断开重新连接之后减轻master复制压力的方案。
在slave重新连接到master之后,将offset和run_id也发送给master,master对比。如果run_id一致说明断开重新连接的slaver,如果offset一致,则进行进行部分复制。只要其中两个一个不一致则进行全量复制。

  1. slave跟master建立socket长连接,并发送psync【见下面的注意点】命令同步数据,并发送replid_id和offset
  2. master对比replid_id和offset
  3. 一致则将rb_buffer中的命令发送给slave。不一致将进行一次全量复制

示意图:参考上图

  • 增量复制socket稳定后,主服务器将写的命令持续发送到从服务器
    增量复制是slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程,增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

搭建主从

master的配置文件不需要修改
修改slave的配置文件(4.0以前好像是slaveof)

replicaof 192.168.160.146 6379 #master的ip port
replica-serve-stale-data yes #yes跟master断开后继续响应,no是除去指定命令外返回一个错误信息
replica-read-only yes #设置slave是否只读

直接启动slave和master就可以了
注意点

2.8 版本之前 Redis 复制采用 sync 命令,无论是第一次主从复制还是断线重连后再进行复制都采用全量同步,成本高
2.8 ~ 4.0 之间复制采用 psync 命令,这一特性主要添加了 Redis 在断线重连时候可通过 offset 信息使用部分同步
4.0 版本之后也采用 psync,相比于 2.8 版本的 psync 优化了增量复制,这里我们称为 psync2,2.8 版本的 psync 可以称为 psync1

redis多master集群【水平扩展】

redis集群是由多个master-slave组成的,具有高可用,复制和分片(数据没有采用一致的hash,而是使用hash slot)等特性。

示意图:
redis学习笔记_第4张图片

原理:

	redisCluster可以挂载n多个master节点,并且每个master节点上可以挂载n多个slave节点的集群模式。
  1. redis实例的分派方法
    redis没有采用一致性的hash,而是使用了hash slot的方式。hash slot是有限的在0-16383之间。在一个redis集群中,所有的slot都会完全分派给所有的master集群。比如有3个mastermaster,则每个master节点管理5461个slot。【也可以使用 cluster addslots 0 将0slot分派给当前的实例】。

  2. redis的key映射方式
    当一个要被存储在redis集群的时候,先将其发送给任意的master节点,然后使用 HASH_SLOT = CRC16(key) mod 16384算法得到这个slot如果这个master正好在维护这个slot则就地处理这个key,否则返回一个重定向的命令让去正确的实例去操作。

    	一般情况下,在本地会缓存一个slot的维护列表,直接在本地进行计算然后直接去正确的redis实例操
    作。如果还是错误会重新计算并且刷新本地缓存。
    
  3. 数据分片
    redis会将所有的数据分派不同的redis上,即只保留一份数据。

  4. 高可用
    使用主从复制模型保证master节点不可用的时候,可以快速将从节点升级为主节点,保证系统的高可用。换句话说,redisCluster集成了主从复制和哨兵模式

Slot机制还有一个很明显的优势,就是在处理并发的场景,因为它将数据集进行了分割,实际上减小了锁的粒度,从而扩大了并发度。Java中的ConcurrentHashMap容器是应用这种机制来实现并发的典型的例子

搭建集群(三主三从)

  1. 复制bin/redis-server 和redis.conf 六份。两两放在三个文件夹中
    在这里插入图片描述
  2. 修改主节点的redis.conf

修改下面那个配置内容

cluster-enabled yes
cluster-node-timeout 15000 #集群超时时间
cluster-config-file nodes-port.conf 【port是这个实例对应的端口号。如果配置文件中没有这个选项可以不加,可以自己生成好像】

  1. 修改从节点的配置(和主从复制一样,指定是哪一个的slave即可)
  2. 启动所有的redis实例
  3. 创建cluster

使用任意实例的bin/redis-cli命令执行: redis-cli --cluster create 192.168.43.129:6380 192.168.43.129:6381 192.168.43.129:6382 192.168.43.129:63800 192.168.43.129:63801 192.168.43.129:6382 --cluster-replicas 1

具体的redis-cli的命令参考文章redis-cli命令参考
6. 验证cluster

进入任意一个redis实例
cluster info(查看集群信息)
cluster nodes(查看节点列表)
(3)进行数据操作验证
redisCluseter01:0>set name 小J
OK

redisCluseter02:0>get name
小J

(4)关闭集群则需要逐个进行关闭,使用命令: /usr/local/redis-5.0.2/bin/redis-cli -c -h 192.168.43.129 -p 638* shutdown

  1. 坑点

在创建集群的时候不要使用127.0.0.1这个ip否则在外部访问不了

哨兵模式【保证redis主从模式的高可用】

  1. 什么是redis-sentinel
    Redis-Sentinel是官方推荐的高可用解决方案,当redis在做master-slave的高可用方案时,假如master宕机了,redis本身(以及其很多客户端)都没有实现自动进行主备切换,而redis-sentinel本身也是独立运行的进程,可以部署在其他与redis集群可通讯的机器中监控redis集群。

很显然,单个哨兵会存在自己挂掉而无法监控整个集群的问题,所以哨兵也是支持集群的,我们通常用三台哨兵机器来监控一组redis集群。

  1. sentinel的组件功能
    集群监控:负责监控 Redis master 和 slave 进程是否正常工作。
    消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
    故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
    配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

  2. 搭建redis-sentinel(经典的三个哨兵)

  • 依然拷贝三份bin 和redis.conf
  • 修改配置,三份一样

#sentinel监控的IP 端口号 sentinel通过投票后认为mater宕机的数量,此处为至少2个
sentinel monitor mymaster 192.168.132.128 7000 2
#20秒ping不通主节点的信息,主观认为master宕机
sentinel down-after-milliseconds mymaster 30000
#故障转移后重新主从复制,1表示串行,>1并行
sentinel parallel-syncs mymaster 1
#故障转移开始,60秒内没有完成,则认为转移失败
sentinel failover-timeout mymaster 600000

  • 分别启动三个redis-sentinel使用下面的命令

redis-sentinel redis.conf

springboot2.X整合redis【单机版和Cluster版】

在springboot2.x中集成了jedis,不需要大量的配置。对jedis进行了大量的封装形成RedisTemplate类,使我们更加分别的操作redis。
加入pom依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

单机版

  1. 配置文件
spring.redis.host=192.168.43.129
spring.redis.port=6379
spring.redis.client-name=testRedis
spring.redis.password=''"
spring.redis.timeout=5000
spring.redis.jedis.pool.max-wait=0
spring.redis.jedis.pool.max-active=8
spring.redis.database=1
  1. 使用的时候直接注入就可以
	@Autowired
    private RedisTemplate<String, Object> redisTemplate;

Cluster版

  1. 配置文件(有三种方式,推荐使用lettuce连接池的方式)
spring:
  redis:
    timeout: 6000ms
    password:
    cluster:
      max-redirects: 3  # 获取失败 最大重定向次数
      nodes: 192.168.43.129:6380,192.168.43.129:6381, 192.168.43.129:6382
    lettuce:
      pool:
        max-active: 1000  #连接池最大连接数(使用负值表示没有限制)
        max-idle: 10 # 连接池中的最大空闲连接
        min-idle: 5 # 连接池中的最小空闲连接
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
  1. 配置类(RedisTemplate默认使用的是Jedis连接池)
package com.wdg.redis;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author wdg
 * @date 2020/5/18 16:43
 * @Description
 */
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

  1. 使用的时候直接注入就可以
	@Autowired
    private RedisTemplate<String, Object> redisTemplate;

最后

redis由于直接在内存中运行,所以其处理速度是特别快的,和memcached相比,redis丰富的数据类型和可持久化的模式都大大的增加了redis的实用性,作为传统关系型数据库补充很好的解决了现代互联网的巨大流量的对系统的冲击, 理论上导致redis性能瓶颈不在是硬件而是网络IO。
在然后选择集群还是主从复制的模式的时候,一般如果数据量只有几十G的时候,搭建master就可以了,因为一般服务器的内存大概在32G。如果数据量超出32G则考虑搭建Cluster,如有100G的数据量可以搭建5台master。
未完待续。。。

你可能感兴趣的:(java开发笔记)