redis总结

Redis数据类型: 主要包括string , list , sorted set 和hash
    
    1、 keys

        redis本质上就是一个key value 的数据库(DB), key是字符串类型,但是key中不包括边界字符。由于key不是binary safe(二进制安全)的字符串,所以想"my key"和"mykey\n"这样包含空格和换行的key是不允许的。 redis内部是不限制使用binary字符,这是redis协议限制的。"\r\n"在协议格式中会作为特殊字符

        key的相关命令:
        exits key :测试指定的key是否存在,返回"1"表示存在,返回"0"表示不存在
        del key1 key2...keyN :删除给定key,返回删除key的数据,"0"表示给定的key都不存在
        type key : 返回给定key的value类型。返回none表示不存在key,string字符类型,list链表类型,set无序集合类型....
        keys pattern(匹配) :返回匹配指定模式的所有key
        randomkey :返回从当前数据库中随机选择的一个key,如果当前数据库是空的,返回空字符串
        rename oldkey newkey : 原子的重命名一个key,如果newkey存在,将会被覆盖,返回"1"表示成功,"0"表示失败。可能是oldkey不存在或者和newkey相同。
        renamenx oldkey newkey: 功能和rename oldkey newkey相同,只是在newkey存在时返回失败
        dbsize :  返回当前数据库的key数据
        expire key seconds : 为key指定过期时间,单位是秒。返回'1'成功,返回'0'失败,0表示key已经设置过过期时间或者不存在key
        ttl key : 返回设置过期时间的key的剩余过期秒数 -1表示key不存在或者没有设置过过期时间
        select db-index : 通过索引选择数据库,默认连接的数据库索引是 "0" , 默认数据库是16个。返回1表示成功,0表示失败
        move key db-index:将key从当前数据库中移动到指定数据库。返回1表示成功,返回0表示 key不存在或者已经在指定数据库中。
        flushdb :删除当前数据库中所有key,此方法不会失败。慎用
        flushall : 删除所有数据库中的所有key,此返回不会失败。慎用。

    2、string

        是redis最基本的数据类型,而且string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象。从内容实现来看string可以看做byte数组,最大上线1G字节。以下是string类型的定义。

        struct sdshdr{
            long len;
            long free;
            char buf[];
        }

        buf是个char数组用于储存实际的字符串内容。其实char和c#中的byte是等价的,都是一个字节len是buf数组的长度,free是数组中剩余可用字节数。由此可以理解为什么string类型是二进制安全的了。因为他本质上就是一个byte数组。当然可以包含任何数据了。另外string类型可以被部分命令按int处理。比如incr等名称。

        string相关命令: 
        set key value : 设置key对应的值为string类型的value,返回'1'表示成功,'0'表示失败
        setnx key value : 设置key对应的值为string类型的value,返回'1'表示成功,'0'表示失败,如果key已经存在,返回'0'.nx 是 not exist的意思
        get key : 获取key对应的string值,如果key不存在返回null
        getset key value : 原子的设置key的值,并返回key的旧值。如果key不存在,则返回nil
        mget key1 key2...keyN : 一次获取多个key的值,如果对应key不存在,则对应返回nil
        mset key1 value1 key2 value2 ... keyN valueN : 一次设置多个key的值,成功返回1表示所有的值都设置了,失败返回0表示没有任何值被设置
        msetnx key1 value1 key2 value2 ... keyN valueN:一次设置多个key的值,成功返回1表示所有的值都设置了,失败返回0表示没有任何值被设置,不会覆盖已经存在的key
        incr key : 对key的值做加加操作,并返回新的值。注意incr一个不是int的value会返回错误,incr一个不存在的key,则设置key为1
        decr key : 对key的值做减减操作,并返回新的值。注意incr一个不是int的value会返回错误,incr一个不存在的key,则设置key为-1
        incrby key integer : 同incr,加指定的值,key不存在的时候会设置key,并认为原来的value是0
        decrby key integer : 同decr,减指定的值,decrby完全是为了可读性,我们完全可以通过incrby一个负数值来是想同样效果,反之一样
        append key value : 给指定key的字符串值追加value,返回新字符串值的长度
        sbustr key start end : 返回截取过的key的字符串值,注意并不修改key的值,下标是从0开始的。


    3、list

        redis的list类型其实就是一个每个子元素都是string类型的双向链表。所以[lr]push和[lr]pop命令的算法时间复杂度都是O(0)。另外list会记录链表的长度。所以llen操作也是O(1),链表的最大长度是(2的32 次方 -1 )。我们可以通过push.pop操作从链表的头部或者尾部添加删除元素。这使得lsit即可以用作栈,也可以用作队列。有意思的是list的pop操作还有阻塞版本的。当我们[lr]pop一个list对象是,如果list是空,或者不存在,会立即返回nil。但是阻塞版本的 b[lr]pop 可以则可以阻塞,当然可以加超时时间,超时后也会返回 nil。

        list的相关命令:
        lpush key string : 在key对应list的头部添加字符串元素,返回'1'表示成功,'0'表示key存在且不是list类型
        rpush key string : 在key对应list的尾部添加字符串元素,返回'1'表示成功,'0'表示key存在且不是list类型
        llen key : 返回key对应的list的长度,key不存在返回 '0',如果key对应类型不是list返回错误
        lrange key start end : 返回指定区间内的元素,下标从'0'开始,负值表示从后面计算,-1表示倒数第一个元素,key不存在返回空列表
        ltrim key start end : 截取list,保留指定区间内元素,成功返回1,可以不存在返回错误
        lset key index value : 设置list中指定下标的元素值,成功返回1,key或者下标不存在返回错误
        lrem key count value : 从key对应list中删除count个和value相同的元素。count为0是,删除全部
        lpop key :从list的头部删除元素,并返回删除元素。如果key对应list不存在或者是空,返回nil,如果key对应值不是list返回错误。
        rpop key : 同上,但是从尾部删除。
        blpop key1.....leyN timeout :从左到右扫描返回对第一个非空list进行lpop操作并返回,比如blpop list1 list2 list3 0,;如果List不存在,list2,list3都是非空则对list2做lpop中删除的元素。如果所有的list都是空或不存在,则会阻塞timeout秒,timeout为0表示一直阻塞。当阻塞时,如果有client对key1...keyN中的任意key进行push操作,则第一在这个key上被阻塞的client会立即返回。如果超时发生,则返回nil。
        brpop : 同blpop相同,一个从头开始删除,一个从尾开始删除
        rpoplpush srckey destkey : 从srckey对应list的尾部移除元素并添加到destkey对应list的头部,最后返回被移除的元素值,真个操作是原子的,如果scrkey是空或者不存在返回nil.


    4、set    

        redis的set是string类型的无序集合。set元素最大可以包含(2的32次方-1)个元素。set的是通过hash table实现的,所以添加、删除,查找的复杂度都是O(1)。hash table会随着添加或者删除自动的调整大小。需要注意的是调整hash table大小时候需要同步(获取血锁)会阻塞其他读写操作。可能不久后就会改用跳表(skip list)来实现,跳表已经在sorted set 中使用了。

        set相关命令
        sadd key member :添加一个string元素到key对应的set集合中,成功返回'1',如果元素已经在集合中返回'0',key对应的set不存在返回错误
        srem key member :从key对应set中移除给定元素,成功返回'1',如果member在集合中不存在或者key不存在返回'0',如果key对应的不是set类型的值返回错误
        spop key : 删除并返回key对应set中随机的一个元素,如果set是空或者key不存在返回nil
        srandmember key: 同spop,随机取set中的一个元素,但是不删除元素
        smove srckey dstkey member: 从srckey对应set中移除member并添加到dstkey对应的set中,整个操作是原子的,成功返回1,如果member在srckey中不存在,则返回0,如果key不是set类型返回错误。
        scard key: 返回set的元素个数,如果set是空或者key不存在返回0
        sismember key member: 判断member是否在set中,存在返回1,0表示不存在或者key不存在
        sinter key1 key2...keyN: 返回所有给定key的交集
        sinterstore dstkey key1...keyN: 同sinter,但是会同时将交集存到dstkey下
        sunion key1 key2...keyN: 返回所有给定key的并集
        sunionstore dstkey key1...keyN: 同sunion,并同时保存并集到dstkey下
        sdiff key1 key2...keyN: 返回所有给定key的差集
        sdiffsotre dstkey key1....keyN: 同sdiff,并同时保存差集到dstkey下
        smembers key: 返回key对应set的所有元素,结果是无序的


    5、sorted set类型

        和set一样,sorted set也是spring类型元素的集合,不同的是每个元素都会关联一个double类型的sore。 sorted set 的实现是 skip list和hash tabel的混合体。当元素被添加到集合中时,一个元素到score的映射被添加到hash table中,所以给定一个元素获取score的开销是O(1),另一个score到元素的映射被添加到skip list并按照score排序,所以就可以有序的获取集合中的元素。添加、删除操作可以逆序从尾部取元素

        sorted set相关命令
        zadd key score member:添加元素到集合,元素在集合中存在更新对应score
        zrem key member: 删除指定元素,1 表示成功,如果元素不存在则返回0
        zincrby key incr member: 增加对应member的score值,然后移动元素并保持skip list保持有序,返回更新后的score值
        zrank key member: 返回指定元素在集合中的排名(下标),集合中元素是按score从小到大排序的
        zrevrank key member: 同上,但是集合中元素是按score从大到小排序
        zrange key start end: 类似lrange操作从集合中去指定区间的元素。返回的是有序结果
        zrevrange key start end: 同上,返回结果是按照score逆序的
        zrangebyscore key min max: 返回集合中score在给定区间的元素
        zcount key min max: 返回集合中score在给定区间的数量
        zscore key element: 返回给定元素对应的score
        zremrangebyrank key min max: 删除集合中排名的给定区间的元素
        zremrangebyscore key min max: 删除集合中的score在给定区间的元素


    6、hash类型

        redis hash 是一个string类型的field和value的映射表。特别适合用于存储对象。相交于将对象的每个字段存成单个string类型。讲一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象。省内存的原因是新建一个hash对象时,开始是有那个zipmap(又称为small hash)来存储的。这个zipmap其实并不是hash table,但是zipmap相比正常的hash实现可以节省不少hash本生需要的一些元素存储开销。尽管zipmap的添加、删除、查找都是O(n),但是由于一般对象的field数量都太多,所以使用zipmap也是很快的,也就是说添加删除平均哈市O(1),如果field或者value的大小超出一定限制后,redis会在内部自动将zipmap替换成正常的hash实现,这个限制可以在配置文件中指定。

        hash相关命令
        hset key field value: 设置hash field为指定值,如果key不存在,则先创建
        hset key field: 获取指定的hash field
        hmget key field1...fieldN: 获取全部指定的hash filed
        hmset key field1 value1 ... fieldN valueN: 同时设置hash的多个field
        hincrby key field integer: 将指定的hash filed加上给定值
        hexists key field: 测试指定field是否存在
        hdel key field: 删除指定的hash field
        hlen key: 返回指定hash的field数量
        hkeys key: 返回hash的所有field
        hvals key: 返回hash的所有value
        hgetall key: 返回hash的所有filed和value

     7、redis事务

         redis对事务的支持目前还比较简单。redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。由于redis是单线程来处理所有client的请求的所以做到这点不容易的。

         事务的执行

             一般情况下redis在接受一个client发来的命令后会立即处理并返回处理结果,但是当一个client在一个连接中发出multi命令后,这个链接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中。当从此链接收到exec命令后,redis会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给client。吼吼此链接就结束事务上下文。

         事务的取消

             可以调用discard命令来取消一个事务。 discard命令其实就是清空事务的命令队列并退出事务上下文。虽然说redis事务在本质上也相当于序列化隔离界别了,但是由于事务上下文的命令只排队并不立即执行,所以事务中的写操作不能依赖事务中的读操作结果。

         实现CAS

             Redis使用乐观锁来实现了CAS(check and set)操作,在rendis2.1后添加了wathc命令,可以用来实现redis事务中的CAS操作
             watch监视指定的key,这个key可以有多个。当被watched的key在事务exec前至少有一个发生改变,那么整个事务将跳出,exec命令返回(nil).

             exec,discard,unwatch命令都会清楚连接中的所有监视

         事务的缺陷

             redis的事务实现是如此简单,当然会存在一些问题
             第一个问题是redis只能保证事务的每个命令连续执行,但是如果事务中的一个命令失败了,并不回滚其他命令,比如使用命令类型不匹配

             当事务执行过程中,如果redis意外的挂了。很遗憾只有部分命令执行了,后面的也就被丢弃了。当然如果我们使用append-only file方式持久化,redis会用单个write操作写入整个事务内容。即是这种方式还是有可能只部分写入了事务到磁盘。发生部分写入事务的情况下,redis重启会检测到这种情况,然后失败退出。可以使用redis-check-aof工具进行修复,修复会删除部分写入的事务内容。修复完成后就能够重写启动了。

         redis 之 pipeline

             pipeline的用途
             redis是一个cs模式的tcp server,使用和http类似的请求响应协议。一个client可以通过一个socket连接发起多个请求命令。每个请求命令发出后client通常会阻塞并到等待redis服务处理,redis处理完后请求命令会将结果通过响应报文返回给client。基本的通信过程如下:

             Client:INCR X
             Server:1
             Client:INCR X
             Server:2
             Client:INCR X
             Server:3
             Client:INCR X
             server:4

             基本上四个命令需要8个tcp报文才能完成。由于同行会有网络延迟,假如从client和server之间的包传输时间需要0.125秒。那么上面的四个命令,client可以将四个incr命令放到一个tcp报文一起发送,server则可以将四条命令的处理结果放到一个tcp报文返回。通过pipeline方式当有大量的操作时候。我们可以节省很多原来浪费的网络延迟的时间。需要注意:是用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令越多越好。具体多少合适需要根据情况测试。


         redis 之发布订阅

             pub/sub
             发布订阅(pub/sub)是一种消息通信模式,主要的目的是解耦消息发布者和消息订阅者之间的耦合,这点和设计模式中的观察者模式比较相似。pub/sub不仅仅解决发布者和订阅者直接代码级别耦合,也解决两者在物理部署上的耦合。redis作为一个pub/sub server,在订阅者和发布者之间起到了消息路由的功能。订阅者可以通过subscribe和psubscribe命令向redis server订阅自己感兴趣的消息时。订阅该消息类型的全部client都会受到此消息。这里消息的床底是多对多的。一个client可以订阅多个channel,也可以向多个channel发送消息


         redis 持久化

             redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到磁盘来保证持久化。redis支持两种持久化方式,一种是Snapshotting(快照)也是默认方式,另一种是Append-only file(aof)的方式
         
             snapshotting:快照是默认的持久化方式。这种方式是将内存中数据以快照的方式写入到二进制文件中,默认的文件名称dump.rdb文件。可以通过配置设置自动做快照持久化的方式
             我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置:

             save 900 1 #900秒内如果超过1个key被修改,则发起快照保存
             save 300 10 #300秒内如果超过10个key被修改,则发起快照保存
             save 600 10000

             下面介绍详细的快照保存过程:
             1、redis调用fork,现在有了子进程和父进程
             2、父进程继续处理client请求,子进程负责将内存内容写入到临时文件,由于OS的写时复制机制(copy or write) 父子进程会共享相同的物理页面,当父进程处理写请求时OS会为父进程要修改的页面创建副本,而不是写共享的页面。所以紫荆城的地址空间内的数据是fork时刻整个数据库的一个快照
             3、当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。
                 client也可以使用save或bgsave命令通过redis做一次快照持久化。save操作是主线程中保存快照的,由于redis是用一个主线程来处理所有client请求,这种方式会阻塞所有client请求,所以不推荐使用。另一点需要注意的是,每次快照持久化是将一个内存数据完整写入的磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘IO操作,可能会严重影响性能。
                 另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外dow吊的话,就会丢失最后一次快照的所有修改。如果应用要求不能丢失任何修改的话,可以重用aof持久化方式。


             Append-only file

                 aof比快照方式有更好的持久化性,是由于在使用aof持久化方式时,redis会将每一个收到的写命令通过write函数追加到文件中(默认是appendonly.aof)。当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于OS会在内核中缓存write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要通过fsync函数强制OS写入到磁盘的时机。有三种方式如下(默认是:每秒fsync一次)

                 appendonly yes 启用aof持久化方式
                 #appendonly always //每次收到写命令就立即前置写入磁盘,最慢的,但是保证完全的持久化,不推荐
                 appendfsnc everysec //每秒强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
                 #appendfsync no //完全依赖OS,性能最好,持久化没有保证

             aof的方式也同时带来了另一个问题,持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态,其实文件中保存一条set test 100就够了。为了压缩aof持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快长类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件。具体过程如下:

                 1.redis调用fork,现在有父子两个进程
                 2.子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
                 3.父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出现问题
                 4.当子进程把快照内容写入已命令方式写到临时文件后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
                 5.现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的命令也开始往新的aof文件中追加。

                 需要注意到时重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。


         redis之主从复制
             redis主从复制配置和使用都非常简单。通过主从复制可以允许多个slave server拥有和master server相同的数据库副本。下面是关于redis主从复制的一些特点:

             master可以有多个slave
             除了多个save连到相同的master外,slave也可以连接其他slave形成图状结构
             主从复制不会阻塞master。也就是说当一个或多个slave与master进行初次同步数据时,master可以继续处理client发来的请求。想法slave在初次同步数据时则会阻塞不能处理的client的请求。
             主从复制可以用来提高系统的可伸缩性,我们可以用多个slave专门用于client的读请求,比如sort操作可以使用slave来处理,也可以用来做简单的数据冗余
             可以在master禁用数据持久化,只需要注释掉master配置文件中的所有save配置,然后只在slave上配置数据持久化


         M/S过程
             当设置好slave服务器后,slave会建立和master的连接,然后发送命令。无论是第一次同步建立的连接还是连接断开后的重新连接,master都会启动一个后台进程,将数据库快照保存到文件中,同时master主进程会开始收集新的写命令并缓存起来。后台进程完成写文件后,master就发送文件给slave,slave将文件保存到磁盘上,然后加载到内存恢复数据库快照到slave上。接着master就会把缓存命令转发给slave。而且后续master收到的写命令都会通过开始建立的连接发送给slave。从master到slave的同步数据的命令和从client发送的命令使用小沟通的协议格式。当master和slave的连接断开时slave可以自动重新建立连接。如果master同时收到多个slave发来的同步连接命令,只会启动一个进程来写数据库镜像,然后发送给所有slave。


    redis之虚拟内存
 

你可能感兴趣的:(java,高级知识系列)