Redis

>  这篇主要会对redis安装,基本概念和一些引用。

安装

下载

tar 解压

make (make前需要安装Gcc,用yum安装最简单了 yum -y install gcc gcc-c++ kernel-devel //安装gcc、c++编译器以及内核文件)

make test(test 之前需要安装tcl

可以格外创建一个目录用来安装 make redis

make install PREFIX=/usr/data/redis

安装好后,到redis-3.2.8 下拷贝一个文件但redis目录下:cp redis.conf ../redis/redis.conf

安装完后,源码就可以删了

 

  • 启动 要指定conf: ./redis-server ../redis.conf

  • 要在后台运行的话,需要改配置 redis.conf,修改daemonize yes

  • 关闭redis, ./redis-cli shutdown

  • 查看 ps -ef|grep redis

  • 进入客户端本地 ./redis-cli ,访问远程 ./redis-cli -h 192.168.146.11 -p 6379

  • 关闭 在客户端中shutdown即可,外面就 ./redis-cli shutdown, shutdown nosave命令强行终止,不要用kill

 

基本概念

多数据库支持

默认支持16个数据库,可以理解为一个命名空间

跟其他关系型数据库不一样的地方有

1:redis不支持自定义数据库名词

2:每个数据库不能单独设置授权

3:每个数据库之间并不是完全隔离的,可以通过flushall命令清空redis实例面的所有数据库中的数据

可通过 select dbid 去选择不同的数据空命名空间 id范围为 0-15

 

基本操作

1:获取一个符合匹配规则的键名列表

keys parttern [? * /]

2:判断一个键是否存在

exits key

3:type key 获得这个key的数据结构类型

 

五大数据类型

有必要先了解一下redis中每个的村粗结构

ypedef struct redisObject {
    //类型
    unsigned type:4;
    //编码
    unsigned encoding:4;
    //对象最后一次被命令程序访问的时间
    unsigned lru:LRU_BITS;
    //引用计数器
    int refcount;
    //执行底层实现数据结构的指针
    void *ptr;
} robj;

对象的ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的encoding属性决定。encoding属性记录了对象所使用的编码,也即是说这个对象使用了什么数据结构作为对象的底层实现,每种类型的对象都至少使用了两种不同的编码

 

  • 1:字符类型

一个字符类型的key默认存储的最大容量是512M

赋值和取值 SET key value GET key

递增数字(原子递增):incr key incryby key increment 自定义递增大小

原子递减 decr key

字符串追加 append key -value

获得多个key的value mget key1 key2 key3 同时设置多个key 的value mset key2 value2 key1 value1

 

编码:

字符串类型对象的编码可以是int、raw或者embstr。

int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转换为raw编码的字符串对象。

 

  • 2: 列表类型

list:可以存储一个有序的字符串列表

lpush/rpush key val...... 左右添加数据 lpop/rpop key 取出并移除数据

llen key 获得列表长度

lrang/rrange key start stop(-1表示最大) 获取区间中的数据

lrem key count value 删除列表值为value 数量为count

lset key index value 设置对应索引的值

应用场景:可以用来做分布式队列

 

编码:

列表对象的编码可以是ziplist或者linkedlist。

ziplist编码的列表对象使用压缩列表作为底层实现,每个压缩列表节点(entry)保存了一个列表元素。

注意,linkedlist编码的列表对象在底层的双端链表结构中包含了多个字符串对象,这种嵌套字符串对象的行为 在的哈希对象、集合对象和有序集合对象中都会出现,字符串对象是Redis五种类型的对象中唯一一种会被其他 四种类型对象嵌套的对象。

  • 3: 散列类型

hash: key value 形式 不支持数据类型的嵌套

比较适合存储对象

比如:person:{age,sex,name}

hset key filed value , hset person person.age 20 hset person person.name try

hget key filed, hget person person.age

同样有hset key filed value filed2 value ....,hmget

hgetall key,获取hash的所有信息,包括key value

hexits key filed 返回 0 1,判断字段是否存在

hdel key filed1 filed2.....

 

编码:

哈希对象的编码可以是ziplist或者hashtable。

ziplist编码的哈希对象使用压缩列表作为底层实现,每当有新的键值对要加入到哈希对象时,程序会先将保存了 键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾

hashtable编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值对来保存,

  • 4:集合类型

set 和 list不同的是,不能存在重复的数据,且有序

sadd key member [member....] 增加数据,如果value存在,则会直接覆盖,并返回成功加入后的元素数量

srem key member

smembers key 获取所有数据

sdiff key1 key2.... 差集运算

sunion key1 key2... 并集运算

 

编码:

集合对象的编码可以是intset或者hashtable。

intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合里面。

hashtable编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含 了一个集合元素,而字典的值则全部被设置为NULL

 

  • 5:有序集合

zadd key score member key2 score memeber (会根据score 排序)

zrange key start stop [withscores] 获取元素

 

编码:

有序集合的编码可以是ziplist或者skiplist。

ziplist编码的压缩列表对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来 保 存,第一个节点保存元素的成员(member),而第二个元素则保存元素的分值(score)。

压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的方向,而分值较大的 元素则被放置在靠近表尾的方向。

 

skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。

跳跃表节点的object属性保存了元素的成员,而跳跃表节点的score属性则保存了元素的分值。通过这个跳跃 表,程序可以对有序集合进行范围型操作,比如ZRANK、ZRANGE等命令就是基于跳跃表API来实现的。

 

内存回收

因为C语言并不具备自动内存回收功能,所以Redis在自己的对象系统中构建了一个引用计数(reference counting)技术实现的内存回收机制,通过这一机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。

 

内存共享

有没有想过这个问题,redis中是如何实现内存共享的,就比如在java中的常量池,这是一块共享内存。

其实redis肯定也是做了处理的,比如一个对象,其值为100,如果需要创建一千个,不可能会去分配1000个内存来分别存储该对象的,而是指向同一个对象。但共享的时候,这个被共享对象的引用计数器会加一。其实Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器就会使用这些共享对象,而不是新创建对象。

 

那么问题来了,为什么redis不共享包含字符串的对象?

 

redis数据库

在初始化服务器时,程序会根据服务器状态的dbnum属性来决定应该创建多少个数据库,dbnum属性的值由服务器配置的database选项决定,默认情况下,该选项的值为16,所以Redis服务器默认会创建16个数据库。

每个Redis客户端都有自己的目标数据库,每当客户端执行数据库写命令或者数据库读命令的时候,目标数据库就会成为这些命令的操作对象。默认情况下,Redis客户端的目标数据库为0号数据库,但客户端可以通过执行SELECT命令来切换目标数据库。

要注意一个点:好像没有客户端命令来查看当前客户端使用的是第几号数据库。当然如果你是在使用redis-cli客户端的时候,可以在控制台输入行前看到是第几号数据库,但我们一般都是用其他客户端操作,所以有些命令的使用必须要注意FLUSHDB,最好先SELECT一下,切换一下数据库。

 

数据库键空间

Redis是一个键值对(key-value pair)数据库服务器,服务器中的每个数据库都由一个redis.h/redisDb结构表示,其中,redisDb结构的dict字典保存了数据库中的所有键值对。

因为数据库的键空间是一个字典,所以所有针对数据库的操作,比如添加一个键值对到数据库,或者从数据库中删除一个键值对,又或者在数据库中获取某个键值对等,实际上都是通过对键空间字典进行操作来实现的。

 

设置键的生存时间和过期时间

通过EXPIRE命令或者PEXPIRE命令,客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间(Time To Live,TTL),在经过指定的秒数或者毫秒数之后,服务器就会自动删除生存时间为0的键。

Redis有四个不同的命令可以用于设置键的生存时间(键可以存在多久)或过期时间(键什么时候会被删除):

  • EXPIRE<key><ttl>命令用于将键key的生存时间设置为ttl秒。

  • PEXPIRE<key><ttl>命令用于将键key的生存时间设置为ttl毫秒。

  • EXPIREAT<key><timestamp>命令用于将键key的过期时间设置为timestamp所指定的秒数时间戳。

  • PEXPIREAT<key><timestamp>命令用于将键key的过期时间设置为timestamp所指定的毫秒数时间戳。

 

TTL命令和PTTL命令接受一个带有生存时间或者过期时间的键,返回这个键的剩余生存时间,也就是,返回距离这个键被服务器自动删除还有多长时间。

 

那么问题来了,这个过期时间是怎么保存的:

redisDb结构的expires字典保存了数据库中所有键的过期时间,我们称这个字典为过期字典:

  • 过期字典的键是一个指针,这个指针指向键空间中的某个键对象(也即是某个数据库键)。

  • 过期字典的值是一个long long类型的整数,这个整数保存了键所指向的数据库键的过期时间——一个毫秒精度的UNIX时间戳。

那么问题又来了,假如某个键过期了,删除策略是怎样的呢?

总的来说,有三种方法:

  • 定时删除:在设置键的过期时间的同时,创建一个定时器(timer),让定时器在键的过期时间来临时,立即执行对键的删除操作。

  • 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。

  • 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

Redis服务器实际使用的是惰性删除和定期删除两种策略:通过配合使用这两种删除策略,服务器可以很好地在合理使用CPU时间和避免浪费内存空间之间取得平衡。

为什么redis单线程的而且查询还这么快呢

redis应用

redis事物

开启事物:multi 提交事务:exec

 

过期时间

expire key seconds 设置key的过期时间

ttl key 获取剩余超时时间

 

发布订阅

这个应该很少用,用专门的消息中间件实现更好。

 

redis实现分布式锁

话不多说,直接上简单的代码实现,注意下,连接redis时,需要修改下redis.conf,将bind:ip 注释掉,还有就是protect-demo 修改为no

public class RedisManager {
    private static JedisPool jedisPool;
​
    static{
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(20);
        poolConfig.setMaxIdle(10);
        jedisPool = new JedisPool(poolConfig, JedisProperties.HOST, 6379);
    }
​
    public static Jedis getJedis() throws Exception {
        if (jedisPool != null) {
            return jedisPool.getResource();
        }
        throw new Exception("jedisPool is null");
    }
}
​
public class RedisLock {
    private String lockValue;
​
    public String getLock(String key, int tryTimeOut, int expire) {
        try {
            Jedis jedis = RedisManager.getJedis();
            String uuid = UUID.randomUUID().toString();
            long endTime = System.currentTimeMillis() + tryTimeOut;
            while (System.currentTimeMillis() < endTime) {
                if (jedis.setnx(key, uuid) == 1) {//这个地方不是阻塞的 所以要只是延迟请求时间
                    //设置超时时间  这里还是很多问题的,只是做一个简单实现,加入key已经设置了,然后
                    //客户端连接段了,没来得及设置超时时间,那么这个锁就不会被释放。
                    jedis.expire(key, expire);
                    this.lockValue = uuid;
                    return lockValue;
                }
                //防止在设置超时时间前,连接挂了  
                if (jedis.ttl(key) < 0) {
                    jedis.expire(key, expire);
                }
                Thread.sleep(1000L);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
​
    public boolean releaseLock(String key, String lockValue) {
        try {
            Jedis jedis = RedisManager.getJedis();
            while (true) {
                jedis.watch(key);
                if (lockValue.equals(jedis.get(key))) {
                    Transaction transaction = jedis.multi();
                    transaction.del(key);
                    List<Object> list = transaction.exec();
                    if (list == null) {
                        continue;
                    }
                    return true;
                }
                jedis.unwatch();
                break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
​
​
    public static void main(String[] args) throws InterruptedException {
        RedisLock redisLock = new RedisLock();
        String key = "lock:one";
        String lock = redisLock.getLock(key, 2000, 10);
        if (lock != null) {
            System.err.println("lock success :" + lock);
        }
        String lock2 = redisLock.getLock(key, 1000, 10);
        if (lock2 == null) {
            System.err.println("second get fail");
        }
        Thread.sleep(3);
       // redisLock.releaseLock(key, lock);
    }
}

 

lua脚本在redis中的应用

lua脚本在redis中的执行是一个原子过程,也就是说,在脚本中调用的所有redis命令操作会在一个事物中执行。

redis的脚本执行是原子的,即脚本执行期间Redis不会执行其他命令。所有的命令必须等待脚本执行完以后才能执行。为了防止某个脚本执行时间过程导致Redis无法提供服务。Redis提供了lua-time-limit参数限制脚本的最长运行时间。默认是5秒钟。

 

  • lua在linux中的安装

    到官网下载lua的tar.gz的源码包

    tar -zxvf lua-5.3.0.tar.gz

    进入解压的目录:

    cd lua-5.2.0

    make linux (linux环境下编译)

    make install

    如果报错,说找不到readline/readline.h, 可以通过yum命令安装:yum -y install readline-devel ncurses-devel

    安装完以后再make linux / make install

    最后,直接输入 lua命令即可进入lua的控制台

     

  • 看个案例,比如我们对某个IP访问做次数限制,下面是这段lua脚本。

--注意KEY 和参数都要大写 这里第一个参数是过期时间,第二个是范围限制次数
local num=redis.call('incr',KEYS[1])
if tonumber(num)==1 then
    redis.call('expire',KEYS[1],ARGV[1])
    return 1
elseif tonumber(num)>tonumber(ARGV[2]) then
    return 0
else
    return 1
end

当然可以直接在redis客户端下直接运行,把脚本保存在本地目录,执行命令为,下面有个坑,参数之间要空格。

./redis-cli --eval "ip_limit.lua" ip:limit:192.168.146.140 , 6000 10

 

生产的时候肯定是通过读取脚本,然后加载脚本得到一个摘要,这个摘要会对应这个脚本缓存在redis内存中,后续访问只需要传递摘要即可,话不多说,上代码。

public class LuaUse {
    //当然我们一般是从文件去加载
    public static final String SCRIPT_STR = "local num=redis.call('incr',KEYS[1])\n" +
            "if tonumber(num)==1 then\n" +
            "    redis.call('expire',KEYS[1],ARGV[1])\n" +
            "    return 1\n" +
            "elseif tonumber(num)>tonumber(ARGV[2]) then\n" +
            "    return 0\n" +
            "else\n" +
            "    return 1\n" +
            "end";
​
    public static void main(String[] args) throws Exception {
        Jedis jedis = RedisManager.getJedis();
        List<String> keys = new ArrayList<>();
        keys.add("ip:limit:192.168.146.128");
        List<String> argvs = new ArrayList<>();
        argvs.add("6000");
        argvs.add("10");
        Object o = jedis.evalsha(jedis.scriptLoad(SCRIPT_STR), keys, argvs);
        System.err.println(o);
    }
}

 

  • 短信验证码过期

 

Redis持久化机制

redis提供了两种持久化机制RDB和AOF,两种持久化策略可以同时使用,也可以使用其中一种。如果同时使用的话, 那么Redis重启时,会优先使用AOF文件来还原数据。

RDB

RDB的持久化策略: 按照规则定时将内从的数据同步到磁盘

snapshot:redis在指定的情况下会触发快照

  • 自己配置的快照规则

    save

    save 900 1 当在900秒内被更改的key的数量大于1的时候,就执行快照

    save 300 10

    save 60 10000

    这三个是或的关系,满足其中一个就会执行快照

  • save或者bgsave

    save: 执行内存的数据同步到磁盘的操作,这个操作会阻塞客户端的请求

    bgsave: 在后台异步执行快照操作,这个操作不会阻塞客户端的请求

  • 执行flushall的时候

    清除内存的所有数据,只要快照的规则不为空,也就是第一个规则存在。那么redis会执行快照

  • 执行复制的时候

     

快照的实现原理

1:redis使用fork函数复制一份当前进程的副本(子进程)

2:父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件

3:当子进程写入完所有数据后会用该临时文件替换旧的RDB文件,至此,一次快照操作完成。

注意:redis在进行快照的过程中不会修改RDB文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候RDB文件都是完整的。 这就使得我们可以通过定时备份RDB文件来实现redis数据库的备份, RDB文件是经过压缩的二进制文件,占用的空间会小于内存中的数据,更加利于传输。

RDB的优缺点

  • 使用RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这个时候我们就需要根据具体的应用场景,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受范围。如果数据相对来说比较重要,希望将损失降到最小,则可以使用AOF方式进行持久化

  • RDB可以最大化Redis的性能:父进程在保存RDB文件时唯一要做的就是fork出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无序执行任何磁盘I/O操作。同时这个也是一个缺点,如果数据集比较大的时候,fork可以能比较耗时,造成服务器在一段时间内停止处理客户端的请求;

配置修改

修改redis.conf中的appendonly yes ; 重启后执行对数据的变更命令, 会在bin目录下生成对应的.aof文件, aof文件中会记录所有的操作命令

如下两个参数可以去对aof文件做优化

auto-aof-rewrite-percentage 100表示当前aof文件大小超过上一次aof文件大小的百分之多少的时候会进行重写。如果之前没有重写过,以启动时aof文件大小为准

auto-aof-rewrite-min-size 64mb限制允许重写最小aof文件大小,也就是文件大小小于64mb的时候,不需要进行优化

 

AOF

AOF可以将Redis执行的每一条写命令追加到硬盘文件中,这一过程显然会降低Redis的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提高AOF的性能

配置修改

默认情况下Redis没有开启AOF(appendonlyfile)方式的持久化,可以通过appendonly参数启用,在redis.conf中找到 appendonly yes

开启AOF持久化后每执行一条会更改Redis中的数据的命令后,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是apendonly.aof. 可以在redis.conf中的属性 appendfilename appendonlyh.aof修改

 

AOF重写的原理

Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松

同步磁盘数据

redis每次更改数据的时候, aof机制都会讲命令记录到aof文件,但是实际上由于操作系统的缓存机制,数据并没有实时写入到硬盘,而是进入硬盘缓存。再通过硬盘缓存机制去刷新到保存到文件

# appendfsync always 每次执行写入都会进行同步 , 这个是最安全但是是效率比较低的方式

appendfsync everysec 每一秒执行

# appendfsync no 不主动进行同步操作,由操作系统去执行,这个是最快但是最不安全的方式

 

AOF文件损坏以后如何修复

服务器可能在程序正在对 AOF 文件进行写入时停机, 如果停机造成了 AOF 文件出错(corrupt), 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。

当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:

\1. 为现有的 AOF 文件创建一个备份。

\2. 使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复。

redis-check-aof --fix

重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。

 

Q&A

1:RDB 和 AOF ,如何选择

一般来说,如果对数据的安全性要求非常高的话,应该同时使用两种持久化功能。如果可以承受数分钟以内的数据丢失,那么可以只使用 RDB 持久化。有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快 。

 

集群

复制(MASTER/SLAVE模型)

这其实就是一个主从模型,Master负责写和同步,Slave负责读,假设三台服务器构建一个主从模型:

 

配置过程

修改192.168.146.129和1192.168.146.130的redis.conf文件,增加slaveof masterip masterport

slaveof 192.168.146.128 6379

实现原理

  1. slave第一次或者重连到master上以后,会向master发送一个SYNC的命令

  2. master收到SYNC的时候,会做两件事

    a) 执行bgsave(rdb的快照文件)

    b) master会把新收到的修改命令存入到缓冲区

缺点 没有办法对master进行动态选举

 

复制的方式

  1. 基于rdb文件的复制(第一次连接或者重连的时候)

  2. 无硬盘复制

  3. 增量复制

    PSYNC master run id. offset

哨兵机制(sentinel)

因为上面的主从模型,如果不能动态选举master的话,那么当master挂掉后,这个集群就蹦了。哨兵机制,能够对master进行监听,然后如果master挂掉的话,会在slave中,重现选举master,如果之前的master重新加入的话,会以slave的身份加入,并会修改自己的配置文件,设置当前的masterip。

sentinel

  1. 监控master和salve是否正常运行

  2. 如果master出现故障,那么会把其中一台salve数据升级为master

bg-redis2.jpg?raw=trueuploading.4e448015.gif转存失败重新上传取消

 

集群(redis3.0以后的功能)

其实就是对数据进行分片,因为前面的是所有的数据都在一个服务器内存中。根据key的hash % 服务器的数量 ,确定这个key在哪个服务器。

集群的原理

Redis Cluster中,Sharding采用slot(槽)的概念,一共分成16384个槽,这有点儿类似前面讲的pre sharding思路。对于每个进入Redis的键值对,根据key进行散列,分配到这16384个slot中的某一个中。使用的hash算法也比较简单,就是CRC16后16384取模。Redis集群中的每个node(节点)负责分摊这16384个slot中的一部分,也就是说,每个slot都对应一个node负责处理。当动态添加或减少node节点时,需要将16384个槽做个再分配,槽中的键值也要迁移。当然,这一过程,在目前实现中,还处于半自动状态,需要人工介入。Redis集群,要保证16384个槽对应的node都正常工作,如果某个node发生故障,那它负责的slots也就失效,整个集群将不能工作。为了增加集群的可访问性,官方推荐的方案是将node配置成主从结构,即一个master主节点,挂n个slave从节点。这时,如果主节点失效,Redis Cluster会根据选举算法从slave节点中选择一个上升为主节点,整个集群继续对外提供服务。这非常类似服务器节点通过Sentinel监控架构成主从结构,只是Redis Cluster本身提供了故障转移容错的能力。

slot(槽)的概念,在redis集群中一共会有16384个槽,

根据key的CRC16算法,得到的结果再对16384进行取模。 假如有3个节点

node1 0 5461 ,node2 5462 10923 ,node3 10924 16383

节点新增,会对槽进行重新分配

0-1364,5461-6826,10923-12287

删除节点

先将节点的数据移动到其他节点上,然后才能执行删除

 

[参考书籍]-《Redis设计与实现》

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值