Redis知识点

本文围绕Redis展开,介绍了持久化方案,包括Rdb和Aof方式及其优缺点;阐述主从复制机制、配置与优缺点;讲解哨兵机制运行流程与配置;介绍Redis集群的原理与容错;还提及分布式锁、Pipeline机制,以及bigkey的发现与删除方法。
摘要由CSDN通过智能技术生成

一、Redis持久化方案

1、Rdb方式

save 900 1
save 300 10
save 60 10000

save <指定时间间隔> <执行指定次数更新操作>,满足条件就将内存中的数据同步到硬盘中。官方出厂配置默认是 900秒内有1个更改,300秒内有10个更改以及60秒内有10000个更改,则将内存中的数据快照写入磁盘。

在redis.conf中可以指定持久化文件存储的目录:

296075ddf893a1443cfc98ddc06313f513e.jpg

RDB 的优缺点

优点:
1 适合大规模的数据恢复。
2 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。

缺点:
1、数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
2、备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。所以Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。

2、Aof方式

Redis默认是不使用该方式持久化的。Aof方式的持久化,是操作一次redis数据库,则将操作的记录存储到aof持久化文件中。

将redis.conf中的appendonly改为yes,即开启aof方式的持久化方案。

b56df65cf3724f0fcb086490bf2d4a9721c.jpg

Aof文件存储的目录和rdb方式的一样。

Aof文件存储的名称

4d60fc14ee48f4834b33ba57eae9347e7bd.jpg

AOF 的优缺点

优点:数据的完整性和一致性更高
缺点:因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。

在使用aof和rdb方式时,如果redis重启,则数据从aof文件加载。

二、Redis主从复制

1、概述

持久化保证了即使redis服务重启也不会丢失数据,因为redis服务重启后会将硬盘上持久化的数据恢复到内存中,但是当redis服务器的硬盘损坏了可能会导致数据丢失,如果通过redis的主从复制机制就可以避免这种单点故障,如下图:

f2fcecebe163d1c0beda1a1035ab3c212a8.jpg

说明:

  1. 主redis中的数据有两个副本(replication)即从redis1和从redis2,即使一台redis服务器宕机其它两台redis服务也可以继续提供服务。
  2. 主redis中的数据和从redis上的数据保持实时同步,当主redis写入数据时通过主从复制机制会复制到两个从redis服务上。
  3. 只有一个主redis,可以有多个从redis。
  4. 主从复制不会阻塞master,在同步数据时,master 可以继续处理client 请求
  5. 一个redis可以即是主又是从,如下图:

c6f36330ebacb15acf4a74d7ada7d14c40e.jpg

2、配置

第一步:复制出一个从机

cp bin/ bin2 –r

第二步:修改从机的redis.conf

语法:Slaveof masterip masterport

slaveof 192.168.242.137 6379

第三步:修改从机的port地址为6380

在redis.conf中修改

1da0bf8301da9049f2c54c4bdcd89bb28b6.jpg

第四步:清除从机中的持久化文件

rm -rf appendonly.aof dump.rdb

第五步:启动从机

./redis-server redis.conf

第六步:启动6380的客户端

./redis-cli -p 6380 

3、优缺点

优点:

  1. 对于读占比较高的场景,可以通过把一部分读流量分摊到从节点(slave)来减轻主节点(master)压力,同时需要注意永远只对主节点执行写操作;
  2. 作为主节点的一个备份,一旦主节点出了故障不可达的情况,从节点可以作为后备“顶”上来,并且保证数据尽量不丢失。

缺点:

  1. 一旦主节点故障,顶上来的从节点可读不可写,需要手动将一个从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令其他从节点去复制新的主节点,整个过程都需要人工干预;
  2. 主节点的写能力受到单机的限制;
  3. 主节点的存储能力受到单机的限制。

三、哨兵

哨兵机制的出现是为了解决主从复制的缺点的,即当主节点出现故障时,由Redis Sentinel自动完成故障发现和转移,并通知应用方,实现高可用性。

1、哨兵运行流程

1、多个哨兵节点监视一个主redis节点

e1f6464024a14674a394c0f5c5d0883247b.jpg

2、主节点故障,哨兵发现主节点不可达

e317688af35ec897b91e4c5d9c0c3f04b54.jpg

8ea09b34638da050519a85dc3db10112fe0.jpg

3、哨兵节点选举出一个领导者,对故障进行转移

6a64707e913b9c16c7ebb726b4e3910b838.jpg

4、故障转移的四个步骤

2aa83c4dfdd91817be4b7783e89639e9ca5.jpg

5、故障转移以后的拓扑图

7834c9c9be9b782ec3e53a92bd77878bcd0.jpg

2、哨兵配置方式

https://www.cnblogs.com/leeSmall/p/8398401.html

四、Redis集群

1、概述

d45309bcb942d63093f1cff425662996921.jpg

  • 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
  • 节点的fail是通过集群中超过半数的节点检测失效时才生效.
  • 客户端与redis节点直连,不需要中间proxy.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
  • redis-cluster把所有的物理节点映射到[0-16383]slot,cluster 负责维护node<->slot<->value

Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点

示例如下:

0148677015f1027ae2c04454a8d5037d01f.jpg

2、redis-cluster投票:容错

e96c03af3d27aa286777a903a44c0d36379.jpg

  1. 集群中所有master参与投票,如果半数以上master节点与其中一个master节点通信超时(cluster-node-timeout),认为该master节点挂掉.
  2. 什么时候整个集群不可用?
  • 如果集群任意master挂掉,且当前master没有slave,则集群进入fail状态。也可以理解成集群的[0-16383]slot映射不完全时进入fail状态。
  • 如果集群超过半数以上master挂掉,无论是否有slave,集群进入fail状态。

五、分布式锁

锁的获取:利用 SETNX key value 这一原子性操作:只在键 key 不存在的情况下, 将键 key 的值设置为 value ;若键 key 已经存在, 则 SETNX 命令不做任何动作。SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

    /**
     * 加锁
     * @param lockName        锁的key
     * @param acquire_timeout 锁获取超时时间
     * @param timeout         锁过期时间(毫秒)
     * @return 锁标识
     */
    public String lockWithTimeout(String lockName, long acquire_timeout, long timeout) {
        Jedis jedis = null;
        String retIdentifier = null;
        try {
            // 获取连接
            jedis=pool.getResource();
            // 随机生成一个value
            String identifier = UUID.randomUUID().toString();
            // 锁名,即key值
            String lockKey = "lock:" + lockName;
            int lockExpire = (int) (timeout / 1000);
            long end = System.currentTimeMillis() + acquire_timeout;

            while (System.currentTimeMillis() < end) {
                //setnx不存在才set。set成功时获取锁,失败时锁被占用
                if (jedis.setnx(lockKey, identifier) == 1) {
                    //获得了锁
                    jedis.expire(lockKey, lockExpire);
                    // 返回value值,用于释放锁时间确认
                    retIdentifier = identifier;
                    return retIdentifier;
                }
                //如果没有设置超时时间,则设置
                if (jedis.ttl(lockKey) == -1) {
                    jedis.expire(lockKey, lockExpire);
                }
                //没有获取到锁,一秒后重试
                try{
                    Thread.sleep(100);
                }catch (InterruptedException e){
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }

            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        //没有获得锁
        return retIdentifier;
    }

锁的释放:为了防止删除锁的key时,key自动过期,且又有新锁添加,而导致误删,需要利用看门狗(watch)实现CAS锁。

    /**
     * 释放锁
     * @param lockName   锁的key
     * @param identifier 释放锁的标识
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis jedis = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            jedis = pool.getResource();
            while (true) {
                /*
                 watch相当于CAS锁,若期间lockKey对应的value发生改变,会阻止下一次事务的执行,
                 所以会和multi配合使用。
                  */
                //在这之前锁过期,会因为equals失败而防止删掉别的锁
                jedis.watch(lockKey);
                // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁
                if (identifier.equals(jedis.get(lockKey))) {
                    Transaction transaction = jedis.multi();
                    transaction.del(lockKey);
                    //在这之前锁过期,会因为exec失败而防止删掉别的锁
                    List<Object> results = transaction.exec();
                    if (results != null) {
                        retFlag = true;
                    }
                    //释放失败
//                    continue;

                }
                jedis.unwatch();
                break;
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return retFlag;
    }

五、Pipeline

Redis客户端执行一条命令分为如下四个过程:

  1. 发送命令
  2. 命令排队
  3. 命令执行
  4. 返回结果

其中1)+4)称为Round Trip Time(RTT,往返时间)。

Pipeline(流水线)机制能将一组Redis命令进行组装,通过一次RTT传输给Redis,再将这组Redis命令的执行结果按顺序返回给客户端,下图为没有使用Pipeline执行了n条命令,整个过程需要n次RTT:

dbb30f312a6aebe18865ecf6ec90d4abf16.jpg

下图为使用Pipeline执行了n次命令,整个过程需要1次RTT。

a0771a2c7b01db1da2c012c4c044f148e7d.jpg

六、Bigkey

bigkey是指key对应的value所占的内存空间比较大:

  • 字符串类型:体现在单个value值很大,一般认为超过10KB就是bigkey,但这个值和具体的OPS相关。
  • 非字符串类型:哈希、列表、集合、有序集合,体现在元素个数过多。

bigkey无论是空间复杂度和时间复杂度都不太友好,主要有以下三个方面:

  • 内存空间不均匀(平衡):例如在Redis Cluster中,bigkey会造成节点的内存空间使用不均匀。
  • 超时阻塞:由于Redis单线程的特性,操作bigkey比较耗时,也就意味着阻塞Redis可能性增大。
  • 网络拥塞:每次获取bigkey产生的网络流量较大,假设一个bigkey为1MB,每秒访问量为1000,那么每秒产生1000MB的流量,对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾,而且一般服务器会采用单机多实例的方式来部署,也就是说一个bigkey可能会对其他实例造成影响,其后果不堪设想。

如何发现

redis-cli--bigkeys:可以命令统计bigkey的分布;

debug object:判断一个key是否为bigkey,只需要执行debug object key查看serializedlength属性即可,它表示key对应的value序列化之后的字节数;

scan+debug object:如果怀疑存在bigkey,可以使用scan命令渐进的扫描出所有的key,分别计算每个key的serializedlength,找到对应bigkey进行相应的处理和报警

  • 如果键值个数比较多,scan+debug object会比较慢,可以利用Pipeline机制完成。
  • 对于元素个数较多的数据结构,debug object执行速度比较慢,存在阻塞Redis的可能。
  • 如果有从节点,可以考虑在从节点上执行。

如何删除

用del删除bigkey通常来说会阻塞Redis服务。

string:对于string类型使用del命令一般不会产生阻塞;

hash、list、set、sorted set

以hash为例子,使用hscan命令,每次获取部分(例如100个)fieldvalue,再利用hdel删除每个field(为了快速可以使用Pipeline)

转载于:https://my.oschina.net/u/2286010/blog/3072447

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值