redis学习

一.redis特点:
    1.性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
    2.原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。watch监控key。
    3.丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
    4.Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。
      在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。
      同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
    5.Redis支持数据的备份,即master-slave模式的数据备份。
    6.Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

二.数据类型:
    分为4类,底层数据结构共有八种:
        简单动态字符串sds(Simple Dynamic String)
        双端链表(LinkedList)
        字典(Map)
        跳跃表(SkipList)

    编码常量                   编码所对应的底层数据结构
    REDIS_ENCODING_INT         long 类型的整数
    REDIS_ENCODING_EMBSTR      embstr 编码的简单动态字符串
    REDIS_ENCODING_RAW         简单动态字符串
    REDIS_ENCODING_HT          字典
    REDIS_ENCODING_LINKEDLIST  双端链表
    REDIS_ENCODING_ZIPLIST     压缩列表
    REDIS_ENCODING_INTSET      整数集合
    REDIS_ENCODING_SKIPLIST        跳跃表和字典


    1.String(字符串)
        SET name "runoob"
        GET name

        字符串对象的编码可以是int、raw或者embstr。
        普通的字符串有两种,embstr和raw。embstr应该是Redis 3.0新增的数据结构,在2.8中是没有的。如果字符串对象的长度小于39字节,就用embstr对象。否则用传统的raw对象。

        embstr的好处有如下几点:
        embstr的创建只需分配一次内存,而raw为两次(一次为sds分配对象,另一次为objet分配对象,embstr省去了第一次)。
        相对地,释放内存的次数也由两次变为一次。
        embstr的objet和sds放在一起,更好地利用缓存带来的优势。

        struct sdshdr{
            int len;
            int free;
            int buf[];
        }
        len :   第一个变量是字符串的长度,是buf数组已使用的字节的数量,这个长度和C++中的长度不同,这个长度就是字符串的长度,
                而C++中的因为还有一个字符串结尾\0,所以长度比C++中的少1.
        free :  free是buf数组中未使用的字节的数量
        buf[] : buf数组的长度等于len+free+1(1是\0)
        这样的好处是:
            1)相比C++的字符串,这个获取字符串长度的时间复杂度是常数,也就是O(1),不需要遍历字符串。
            2)而且,C++的数组是有缓冲区溢出和内存泄露的风险的,而SDS巧妙的解决了这个风险,解决的措施是SDS可以自动的对数组容量进行修改。
                比如进行字符串拼接操作,这个时候buf数组容量不够的时候,若buf数组小于1MB大小,会对buf数组的容量扩容到原来的两倍,
                如果大于1MB,那么程序会分配1MB的free空间,这叫做空间预分配,这样可以大大的减少因为多次空间不足导致的频繁分配空间的情况发生。
                而对于空间回收,Redis的SDS采用的是惰性空间释放,也就是说,当字符串数组buf存储的字符串内容变少时,并不立即回收空间,而是先将空间释放,
                修改free值(加上释放的空间),与此同时,你也可以调用redis真正释放空间的api来释放掉多于的空间。
            3)SDS是二进制安全的。由于\0操作是SDS函数自动添加到buf数组中的,所以buf数组中的特殊字符(包括\0)都将被视为其本身的含义,不需要转义符号的出现。
            4)兼容部分C字符串函数。

        一个字符串类型的值最多能存储512M字节的内容;
        利用INCR命令簇(INCR, DECR, INCRBY)来把字符串当作原子计数器使用;
        使用APPEND命令在字符串后添加内容。

        set(key, value):给数据库中名称为key的string赋予值value
        get(key):返回数据库中名称为key的string的value
        getset(key, value):给名称为key的string赋予上一次的value
        mget(key1, key2,…, key N):返回库中多个string的value
        setnx(key, value):添加string,名称为key,值为value
        setex(key, time, value):向库中添加string,设定过期时间time
        mset(key N, value N):批量设置多个string的值
        msetnx(key N, value N):如果所有名称为key i的string都不存在
        incr(key):名称为key的string增1操作
        incrby(key, integer):名称为key的string增加integer
        decr(key):名称为key的string减1操作
        decrby(key, integer):名称为key的string减少integer
        append(key, value):名称为key的string的值附加value
        substr(key, start, end):返回名称为key的string的value的子串

    2.List(列表)
          lpush runoob redis
          lpush runoob mongodb
          lpush runoob rabitmq
          lrange runoob 0 2

          typedef struct listNode{
              struct listNode *prev;
              struct listNode *next;
              void *value;
          }listNode;

          typedef struct list{
              listNode *head;
              listNode *tail;
              unsigned long len;
              void *(*dup) (void *ptr)
              void *(*free)(void *ptr)
              int (*match)(void *ptr,void *key);
          }list;

          这个list结构封装了头结点,尾节点和链表的长度以及若干操作,包括赋值操作,释放操作以及节点对比的操作。而且,Redis是一个双向无环链表,而且是多态的。

          Redis列表是简单的字符串列表,按照插入顺序排序;
              你可以添加一个元素到列表的头部(左边:LPUSH)或者尾部(右边:RPUSH);
              一个列表最多可以包含232-1个元素(4294967295,每个表超过40亿个元素);
              在社交网络中建立一个时间线模型,使用LPUSH去添加新的元素到用户时间线中,使用LRANGE去检索一些最近插入的条目;
              你可以同时使用LPUSH和LTRIM去创建一个永远不会超过指定元素数目的列表并同时记住最后的N个元素;
              列表可以用来当作消息传递的基元(primitive),例如,众所周知的用来创建后台任务的Resque Ruby库。

              rpush(key, value):在名称为key的list尾添加一个值为value的元素
              lpush(key, value):在名称为key的list头添加一个值为value的 元素
              llen(key):返回名称为key的list的长度
              lrange(key, start, end):返回名称为key的list中start至end之间的元素
              ltrim(key, start, end):截取名称为key的list
              lindex(key, index):返回名称为key的list中index位置的元素
              lset(key, index, value):给名称为key的list中index位置的元素赋值
              lrem(key, count, value):删除count个key的list中值为value的元素
              lpop(key):返回并删除名称为key的list中的首元素
              rpop(key):返回并删除名称为key的list中的尾元素
              blpop(key1, key2,… key N, timeout):lpop命令的block版本。
              brpop(key1, key2,… key N, timeout):rpop的block版本。
              rpoplpush(srckey, dstkey):返回并删除名称为srckey的list的尾元素

    3.Hash(哈希)
          HMSET myhash field1 "Hello" field2 "World"
          HGET myhash field1

          字典的结构从高层到底层实现分别是:字典(dict),字典哈希表(dictht),哈希表节点(dictEntry)

          typedef struct dictht{
              //哈希表数组
              dictEntry **table;  //哈希表的结构使用的是拉链法来实现的, 拉链法既可以实现基本的哈希表结构  类似 java1.7 hashMap
              //哈希表大小
              unsigned long size;
              //哈希表大小掩码,
              //总是等于size-1,
              //用于计算索引值
              unsigned long sizemask;

              //该哈希表已有的节点的数量
              unsigned long used;
          }dictht

          typedef struct dictEntry{
              //键
              void *key;
              //值
              union{
                  void *val;
                  unit64_t u64;
                  int64_t s64;
              } v;
              //指向下个哈希表节点,形成链表
              struct dictEntry *next;

          }dictEntry;


        typedef struct dict{
            //类型特定函数
            dictType *type; //其中type属性指向的是一个dictType结构的指针,每个dictType结构都保存了一系列用于操作特定类型键值对的函数,
                            //比如字典的增删改查操作,redis会为用途不同的字典设置不同的类型特定函数
            //私有数据
            void *privdata; //privdata数组则保存了需要传给那些特定函数的可选参数
            //哈希表
            dictht ht[2]    //字典会默认使用ht[0],另外一个ht[1]是用来再散列
            //rehash索引
            //当rehash不在进行时,值为-1
            int trehashidx;
        }dict;

        哈希算法:哈希算法比较简单,采用的是按位与的操作,首先使用dict的type的一个函数hashFunction(key)对key计算一个hash值,然后将这个哈希值和字典的某个哈希表dictht的sizemask按位与计算出索引位置。
        hash = dict -> type -> hashfunction(key);
        index = hash & dict ->ht[x].sizemask;

        再散列Rehash:
            redis的字典随着不断的增加(减少)元素,会导致装载因子慢慢的变大(变小)(装载因子=元素个数/ht[0].size,也就是哈希表内实际元素个数和容量的比值,拉链法允许比值大于1),
            这个时候需要调整数组ht数组的大小,调整的基本方式是:
                1) 为字典的ht[1]分配空间(一开始空的嘛,对吧),然后分情况讨论:
                    如果是扩展操作,那么ht[1]的大小为第一个大于等于ht[0].used*2的2的n次方
                    如果是缩小操作,那么ht[1]的大小为第一个大于等于ht[0].used的2的n次方
                2) 分配完空间后,那么就是将ht[0]上面的数据移动到ht[1]上面去了,这个移动的过程中还是需要前面的哈希算法计算索引值,将数据移动到新的索引上。
                3) 接下来,那肯定就是回收空间了,回收ht[0]的空间,然后将ht[1]设置为ht[0],ht[0]设置为ht[1]。

                    过程还是很简单的, 但是有一种情况,你需要考虑到,那么就是我们知道redis可能存储的数据是非常大的,
                    比如存储了几百万条数据在字典中,这个时候rehash是一件非常消耗时间的操作,如果单纯的按上面的流程进行再散列,
                    那么会导致一个问题,系统可能会长时间不能响应。这违背了我们使用redis 的初衷(本来使用redis缓存为了就是加速响应 的)。
                    那么怎么办呢?解决办法就是渐进式rehash

        渐进式Rehash:
            渐进式rehash在上面的rehash的基础上,进行了改进,改进过后的步骤将rehash的过程变得不在那么占用cpu时间,
            详细的步骤如下:
                1)为ht[1]分配空间,让字典同时拥有ht[0]和ht[1]
                2)在字典中维持一个索引计数器变量rehashidx,将它的值设置为0,表示rehash工作正在进行。
                3)在rehash执行期间,每次对字典的增删改查,程序成了执行指定的操作之外,还会将ht[0]的哈希表在rehashidx索引上的所有键值对rehash到ht[1],
                   当rehash工作完成之后,程序将rehashidx的属性值增一。(比如当前rehashidx为0,这个时候ht[0]的table数组中索引为0的那些dictEntry会不断的向ht[1]发送,进行rehash分布到ht['1]的不同位置)
                4)这样随着操作不断进行,rehasidx不断的增加1,不断的将ht[0]中的所以为0,1,2,3,上面的元素rehash到ht[1]中,
                    总会在一定的时间之后,会rehash结束,这个时候就将rehashidx设置为-1,表示rehash操作完成。

            hset(key, field, value):向名称为key的hash中添加元素field
            hget(key, field):返回名称为key的hash中field对应的value
            hmget(key, (fields)):返回名称为key的hash中field i对应的value
            hmset(key, (fields)):向名称为key的hash中添加元素field
            hincrby(key, field, integer):将名称为key的hash中field的value增加integer
            hexists(key, field):名称为key的hash中是否存在键为field的域
            hdel(key, field):删除名称为key的hash中键为field的域
            hlen(key):返回名称为key的hash中元素个数
            hkeys(key):返回名称为key的hash中所有键
            hvals(key):返回名称为key的hash中所有键对应的value
            hgetall(key):返回名称为key的hash中所有的键(field)及其对应的value


    4.Set(集合)
        sadd runoob redis
        sadd runoob mongodb
        sadd runoob rabitmq
        smembers runoob

        typedef struct intset{
            //编码方式
            uint32_t enconding;
            //集合包含的元素数量
            uint32_t length;
            //保存元素的数组
            int8_t contents[];
        }intset;

        Redis集合是一个无序的,不允许相同成员存在的字符串合集(Uniq操作,获取某段时间所有数据排重值);
        支持一些服务端的命令从现有的集合出发去进行集合运算,如合并(并集:union),求交(交集:intersection),差集, 找出不同元素的操作(共同好友、二度好友);
        用集合跟踪一个独特的事。想要知道所有访问某个博客文章的独立IP?只要每次都用SADD来处理一个页面访问。那么你可以肯定重复的IP是不会插入的( 利用唯一性,可以统计访问网站的所有独立IP);
        Redis集合能很好的表示关系。
        你可以创建一个tagging系统,然后用集合来代表单个tag。接下来你可以用SADD命令把所有拥有tag的对象的所有ID添加进集合,这样来表示这个特定的tag。
        如果你想要同时有3个不同tag的所有对象的所有ID,那么你需要使用SINTER。
        使用SPOP或者SRANDMEMBER命令随机地获取元素。

        sadd(key, member):向名称为key的set中添加元素member
        srem(key, member) :删除名称为key的set中的元素member
        spop(key) :随机返回并删除名称为key的set中一个元素
        smove(srckey, dstkey, member) :移到集合元素
        scard(key) :返回名称为key的set的基数
        sismember(key, member) :member是否是名称为key的set的元素
        sinter(key1, key2,…key N) :求交集
        sinterstore(dstkey, (keys)) :求交集并将交集保存到dstkey的集合
        sunion(key1, (keys)) :求并集
        sunionstore(dstkey, (keys)) :求并集并将并集保存到dstkey的集合
        sdiff(key1, (keys)) :求差集
        sdiffstore(dstkey, (keys)) :求差集并将差集保存到dstkey的集合
        smembers(key) :返回名称为key的set的所有元素
        srandmember(key) :随机返回名称为key的set的一个元素

    5. zset(sorted set:有序集合)
        zadd runoob 0 redis
        zadd runoob 0 mongodb
        zadd runoob 0 rabitmq
        zadd runoob 0 rabitmq
        ZRANGEBYSCORE runoob 0 1000

        typedef struct zset{
            zskiplist *zsl;  //跳跃表  按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素。
            dict *dict;      //字典表  为有序集合创建了一个从成员到分值的映射,字典中的每一个键值对都保存了一个几何元素:字典的键保存了元素的成员,而字典的值则保存了元素的分值。
        }zset


    6. 对象:
        Redis中的一个对象的结构体
        /*
         * Redis 对象
         */
        typedef struct redisObject {

            // 类型
            unsigned type:4; //redisObject的类型,字符串、列表、集合、有序集、哈希表

            // 不使用(对齐位)
            unsigned notused:2;

            // 编码方式
            unsigned encoding:4; //底层实现结构,字符串、整数、跳跃表、压缩列表等

            // LRU 时间(相对于 server.lruclock)
            unsigned lru:22;

            // 引用计数
            int refcount;

            // 指向对象的值
            void *ptr; //实际指向保存值的数据结构

        } robj;

        如果一个 redisObject 的 type 属性为 REDIS_LIST , encoding 属性为 REDIS_ENCODING_LINKEDLIST ,
        那么这个对象就是一个 Redis 列表,它的值保存在一个双端链表内,而 ptr 指针就指向这个双端链表;
        如果一个 redisObject 的 type 属性为 REDIS_HASH , encoding 属性为 REDIS_ENCODING_ZIPMAP ,
        那么这个对象就是一个 Redis 哈希表,它的值保存在一个 zipmap 里,而 ptr 指针就指向这个 zipmap



三. 命令
    1.CONFIG 命令
    CONFIG GET “#CONFIG_SETTING_NAME”
    CONFIG GET *
    CONFIG GET loglevel

    CONFIG SET “#CONFIG_SETTING_NAME” “#NEW_CONFIG_VALUE”
    config set loglevel "notice"

    redis.config:
        指定本地数据库文件名,默认值为dump.rdb
        dbfilename dump.rdb

        指定本地数据库存放目录
        dir ./

        指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
        loglevel verbose

        设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
        slaveof <masterip> <masterport>

    2.keys 命令
        DEL key
        该命令用于在 key 存在时删除 key。

        DUMP key
        序列化给定 key ,并返回被序列化的值。

        EXISTS key
        检查给定 key 是否存在。

        EXPIRE key seconds
        为给定 key 设置过期时间。

        KEYS pattern
        查找所有符合给定模式( pattern)的 key 。

        MOVE key db
        将当前数据库的 key 移动到给定的数据库 db 当中。

        PERSIST key
        移除 key 的过期时间,key 将持久保持。

        RENAME key newkey
        修改 key 的名称

        TYPE key
        返回 key 所储存的值的类型。

        exists(key):确认一个key是否存在
        del(key):删除一个key
        type(key):返回值的类型
        keys(pattern):返回满足给定pattern的所有key
        randomkey:随机返回key空间的一个
        keyrename(oldname, newname):重命名key
        dbsize:返回当前数据库中key的数目
        expire:设定一个key的活动时间(s)
        ttl:获得一个key的活动时间
        select(index):按索引查询
        move(key, dbindex):移动当前数据库中的key到dbindex数据库
        flushdb:删除当前选择数据库中的所有key
        flushall:删除所有数据库中的所有key


    3. 连接命令
        AUTH password
        验证密码是否正确

        ECHO message
        打印字符串

        PING
        查看服务是否运行

        QUIT
        关闭当前连接

        SELECT index
        切换到指定的数据库


    4. redis 服务器相关命令
        info:提供服务器的信息和统计
        monitor:实时转储收到的请求
        slaveof:改变复制策略设置
        config:在运行时配置Redis服务器


三. HyperLogLog
    Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的

    PFADD key element [element ...]
    添加指定元素到 HyperLogLog 中。

    PFCOUNT key [key ...]
    返回给定 HyperLogLog 的基数估算值。

    PFMERGE destkey sourcekey [sourcekey ...]
    将多个 HyperLogLog 合并为一个 HyperLogLog

    redis 127.0.0.1:6379> PFADD runoobkey "redis"
    1) (integer) 1

    redis 127.0.0.1:6379> PFADD runoobkey "mongodb"
    1) (integer) 1

    redis 127.0.0.1:6379> PFADD runoobkey "mysql"
    1) (integer) 1

    redis 127.0.0.1:6379> PFCOUNT runoobkey
    (integer) 3


四. Redis 发布订阅
    PSUBSCRIBE pattern [pattern ...]
    订阅一个或多个符合给定模式的频道。

    PUBSUB subcommand [argument [argument ...]]
    查看订阅与发布系统状态。

    PUBLISH channel message
    将信息发送到指定的频道。

    PUNSUBSCRIBE [pattern [pattern ...]]
    退订所有给定模式的频道。

    SUBSCRIBE channel [channel ...]
    订阅给定的一个或多个频道的信息。

    UNSUBSCRIBE [channel [channel ...]]
    指退订给定的频道。


五. Redis 事务
    先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令
        redis 127.0.0.1:6379> MULTI
        OK

        redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
        QUEUED

        redis 127.0.0.1:6379> GET book-name
        QUEUED

        redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
        QUEUED

        redis 127.0.0.1:6379> SMEMBERS tag
        QUEUED

        redis 127.0.0.1:6379> EXEC
        1) OK
        2) "Mastering C++ in 21 days"
        3) (integer) 3
        4) 1) "Mastering Series"
           2) "C++"
           3) "Programming"

    单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
    事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

    事务 MULTI 、 EXEC 、 DISCARD 和 WATCH 是 Redis 事务的基础事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。
    事务在执行的过程中,不会被其他客户端发送来的命令请求所打断事务中的命令要么全部被执行,要么全部都不执行,EXEC 命令负责触发并执行事务中的所有命令
    Redis 的 Transactions 提供的并不是严格的 ACID 的事务Transactions 还是提供了基本的命令打包执行的功能: 可以保证一连串的命令是顺序在一起执行的,
    中间有会有其它客户端命令插进来执行Redis 还提供了一个 Watch 功能,你可以对一个 key 进行 Watch,然后再执行 Transactions,
    在这过程中,如果这个 Watched 的值进行了修改,那么这个 Transactions 会发现并拒绝执行数据持久化 RDB。


六. redis脚本
    Redis 脚本使用 Lua 解释器来执行脚本
    EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second


七. redis高级
    1.Redis 数据备份与恢复
        CONFIG GET dir
        SAVE
        该命令将在 redis 安装目录中创建dump.rdb文件。

            save:将数据同步保存到磁盘
            bgsave:将数据异步保存到磁盘
            lastsave:返回上次成功将数据保存到磁盘的Unix时戳
            shundown:将数据同步保存到磁盘,然后关闭服务

        RDB
        特点:
            RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储。
        优点:
            RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份;
            RDB是一个紧凑的单一文件, 非常适用于灾难恢复;
            RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能;
            与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。
        缺点:
            如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合,Redis要完整的保存整个数据集是一个比较繁重的工作。
            RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.
            如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。

        AOF
        特点
            AOF持久化方式记录每次对服务器写的操作;redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
        优点
            使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync;
            AOF文件是一个只进行追加的日志文件,所以不需要写入seek;
            Redis 可以在 AOF 文件体积变得过大时,自动地在后台对AOF 进行重写;
            AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂,
            对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单;
        缺点
            对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积;
            根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。


    2.redis安全
        CONFIG set requirepass "runoob"
        AUTH "runoob"
        CONFIG get requirepass

    3.Redis 性能测试
        Redis 性能测试是通过同时执行多个命令实现的
        redis-benchmark [option] [option value]

    4.Redis 客户端连接
        Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:
        首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。
        然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
        然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送

        config get maxclients

        CLIENT LIST    返回连接到 redis 服务的客户端列表
        CLIENT SETNAME 设置当前连接的名称
        CLIENT GETNAME 获取通过 CLIENT SETNAME 命令设置的服务名称
        CLIENT PAUSE   挂起客户端连接,指定挂起的时间以毫秒计
        CLIENT KILL    关闭客户端连接

    5. Redis 管道技术
        Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:

        客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
        服务端处理命令,并将结果返回给客户端。
        Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。

    6. Redis 分区
        分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。
        分区的优势
        通过利用多台计算机内存的和值,允许我们构造更大的数据库。
        通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽。
        分区的不足
        redis的一些特性在分区方面表现的不是很好:

        涉及多个key的操作通常是不被支持的。举例来说,当两个set映射到不同的redis实例上时,你就不能对这两个set执行交集操作。
        涉及多个key的redis事务不能使用。
        当使用分区时,数据处理较为复杂,比如你需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件。
        增加或删除容量也比较复杂。redis集群大多数支持在运行时增加、删除节点的透明数据平衡的能力,但是类似于客户端分区、代理等其他系统则不支持这项特性。
        然而,一种叫做presharding的技术对此是有帮助的。

        分区类型
        Redis 有两种类型分区。 假设有4个Redis实例 R0,R1,R2,R3,和类似user:1,user:2这样的表示用户的多个key,对既定的key有多种不同方式来选择这个key存放在哪个实例中。
        也就是说,有不同的系统来映射某个key到某个Redis服务。

        范围分区
        0-100,101-200 ......

        哈希分区
        取模


八. redis 并发
    redis是以单进程的形式运行的

    1. 分布式集群redis-cluster
        Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接
        其结构特点:
         1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
         2、节点的fail是通过集群中超过半数的节点检测失效时才生效。
         3、客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
         4、redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护node<->slot<->value。

         5、Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。

    2. redis Master slave 主从(一主一从,一主多从)
        主写数据,从读数据。

        主从复制的步骤:
        1、从服务器开始链接主服务器时,会向主服务器发送一个 SYNC 命令
        2、主服务接收到命令之后,执行 BGSAVE,异步的将写命令保存到一个缓冲区里面
        3、主服务器执行完BGSAVE之后,就.rdb 文件(BOF的持久化文件)发送给从服务器,从服务器从该文件恢复数据到内存中
        4、主服务器还会以 Redis 命令协议的格式, 将写命令缓冲区中积累的所有内容都发送给从服务器,从服务器通过执行命令将数据恢复到内存中。

    3. Redis 哨兵模式 Sentinel redis
        1、不时地监控redis是否按照预期良好地运行;
        2、如果发现某个redis节点运行出现状况,能够通知另外一个进程(例如它的客户端);
        3、能够进行自动切换。当一个master节点不可用时,能够选举出master的多个slave(如果有超过一个slave的话)中的一个来作为新的master,其它的slave节点会将它所追随的master的地址改为被提升为master的slave的新地址。
        4、哨兵为客户端提供服务发现,客户端链接哨兵,哨兵提供当前master的地址然后提供服务,如果出现切换,也就是master挂了,哨兵会提供客户端一个新地址。

九. redis and memcache比较
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值