Redis基础知识讲解(一)

Redis属于NoSQL数据库,不使用表。
Redis可以存储键与5种不同数据结构类型之间的映射,
    1.String:可以是字符串、整数、浮点数
    2.List(列表):一个链表,链表上的每个节点都包含一个字符串
    3.Set(集合):包含字符串的无序收集器,并且被包含的每个字符串都是独一无二的,各不相 
    4.Hash(散列):包含键值对的无序散列表。
    5.ZSet(有序集合):字符串成员与浮点数分值之间的有序映射,元素的排列顺序有分值大小决

以上数据结构的读写能力:
    String:对整个字符串或者字符串的其中一部分执行操作,对整数或浮点数执行自增或自减操作
    List:从链表的两端推入或弹出元素;根据偏移量对链表进行修剪,读取单个或者多个元素;根据值查找或移除数据

Redis命令中有一些是5种数据通用的,有的只针对特定的一种或者两种数据结构使用

关于Redis中的String的一些知识
1).方法:(redis命令后不加括号,直接以空格在识别方法key或value)
            set key-name value 设置存储在给定键中的值
            get key-name value 获取存储在给定键中的值 
            del key-name value 删除存储在给定键中的值 
            incr key-name  将键存储的值加上1
            decr key-name 将键存储的值减去1
            incrby key-name amount 将键存储的值加上整数amount
            decrby key-name amount 将键存储的值减去整数amount
            incrbyfloat key-name amount 将键存储的值加上浮点数amount,(只在redis2.6及以上版本可用)
            append key-name value 将值value追加到给定键key-name当前存储的值的末尾
            getrange key-name start end 获取一个有偏移量start至偏移量end范围内所有字符组成的子串,包括start和end在内
            setrange key-name offset value 将从start偏移量开始的子串设置为给定值(从offset开始,包括offset,将原子串替换成value)
            getbit key-name offset 将字符串看作是二进制位串,并返回位串中偏移量为offset的二进制位的值
            setbit key-name offset value 将字符串看作是二进制位串,并将位串中偏移量为offset的二进制位的值设置为value
            bitcount key-name [start end] 统计二进制位串中值为1的二进制位的数量,如果给定了可选的start、end偏移量,则只统计指定范围内的二进制位数量
            bitop operation dest-key key-name [key-name...] 对一个或多个二进制位串执行包括and(并)\or(或)\xor(异或)\not(非)在内的任意一种按位运算操作,
                                                                                        并将计算结果保存在dest-key键里面
            
2).在Redis中,字符串可以存储以下3种数据类型:字节串(byte string)、整数、浮点数。
3).可以通过给定任意数值,对存储着整数或浮点数的字符串执行自增(increment)或自减(decrement)。
4).redis还会将整数转换成浮点数,整数的取值范围和系统的长整数(long integer)取值范围一致,浮点数的取值范围和精度则与IEEE754标准的双精度浮点数(double)相同。
5).将一个值存储到redis字符串里面的时候,如果这个值可以被interpret成整数或浮点数,那么redis会允许对字符串进行incr*和decr*操作。
6).如果对一个不存在的键或者保存了一个空串的键执行自增或自减,redis会将这个键的值当成0来处理。
7).如果对无法被解释成整数或者浮点数的值进行自增或者自减操作,redis会返回一个错误。
8).在使用setrange和setbit对字符串进行写入的时候,如果字符串的长度不足,redis会自动使用空字串将字符串扩展至所需的长度,然后执行写入或者更新操作
9).在使用getrange时超出字符串末尾的数据会被视为空串,在使用getbit时,超出字符串末尾的二进制位会被视为0

关于Redis中的列表List
1).方法
            lpush key-name value [value...] 将一个或多个元素推入列表的左端
            rpush key-name value [value...] 将一个或多个元素推入列表的右端
              lpop key-name 移除并返回列表的左端元素
              rpop key-name 移除并返回列表的右端元素
            lindex key-name offset 返回列表中偏移量为offset的元素
            lrange key-name start end 返回列表从start(包括start)偏移量到end(包括end)偏移量范围内的所有元素
            ltrim key-name start end 对列表进行修剪,只保留从start(包括start)偏移量到end(包括end)偏移量范围内的元素
        以下几个命令是关于将元素从一个列表移动到另一个列表,或者阻塞(block)执行命令的客户端直到有其他的客户端给列表添加元素为止
            blpop key-name [key-name...] timeout 从第一个非空列表中弹出位于最左端的元素,或者在timeout秒之内阻塞并等待可弹出的元素出现
            brpop key-name [key-name...] timeout 从第一个非空列表中弹出位于最右端的元素,或者在timeout秒之内阻塞并等待可弹出的元素出现
            rpoplpush source-key dest-key 从source-key列表弹出位于最右端的元素,然后将元素推入dest-key列表的最左端,并向用户返回这个元素
            b rpoplpush source-key dest-key timeout 从source-key列表弹出位于最右端的元素,然后将元素推入dest-key列表的最左端,并向用户返回这个元素,如果source-                    
                                                                            key为空,那么在timeout秒之内阻塞并等待可淡出元素的出现
2).在redis中多个命令原子的执行指的是,在这些命令正在读取或者修改数据的时候,其他客户端不等读取或修改相同的数据
3).列表里面的元素可以重复。
4).列表的优点在于他可以包含多个字符串值,这使的用户可以将数据集中的放在一个地方
 关于Redis中的集合Set
1).方法
            sadd key-name item [item...] 将一个或多个元素添加到集合里面,并返回被添加元素的当中原本并不存在于集合里的元素的数量
            srem key-name item [item...] 从集合里面移除一个或多个元素,并返回被移除元素的数量
            sismember key-name item 检查除元素item是否存在于集合key-name里
            scard key-name 返回集合包含的元素数量
            smembers key-name 返回集合包含的所有元素
            srandmember key-name [count] 从集合里随机的返回一个或多个元素,当count为正数时,命令返回的随机元素不会重复,当count为负数时,命令返回的随机元素
                                                               可能会出现重复  
            spop key-name 随机的移除集合中的一个元素,并返回被移除的元素 
            smove source-key dest-key item 如果集合source-key包含元素item,那么从集合source-key里面移除元素item,并将元素item添加到集合dest-key中,如果item成
                                                               功移除则返回1,否则返回0  
    下面是组合和关联多个集合的命令
            sdiff key-name [key-name] 返回那些存在与第一个集合而不存在于其他集合的元素(差集运算)
            sdiffstore dest-key key-name [key-name...] 将那些存在于第一个key-name集合,而不存在于其他集合的元素存储到dest-key键里面
            sinter key-name [key-name] 返回那些同时存在于所有集合中的元素(交集运算)
            sinterstore dest-name key-name [key-name...] 将那些同时存在于所有集合中的元素,存储到dest-key键里面
            sunion key-name [key-name...] 返回那些至少存在于一个集合中的元素(并集运算)
             sunionstore dest-name key-name [key-name...] 将那些至少存在于一个集合中的元素存储到dest-key键里面
2).集合通过散列表来保证每个元素都是唯一的,可以以无序的方式来存储多个各不相同的元素
3).redis的集合可以被多个客户端远程进行访问
  关于Redis的散列hashSet
1).方法
            hset key-name key value 在散列中关联起给定的键值对
            hget key-name key 获取指定散列键的值
            hgetall key-name获取散列包含的所有的键值对
            hdel key-name如果给定键存在于散列里面,那么移除这个键
            hmget key-name key [key...] 从散列里面获取一个或多个键的值
            hmset key-name key value [key value...] 为散列里面的一个或多个键设置值
            hdel key-name key [key...] 删除散列里面一个或多个键值对,返回成功找到并删除的键值对的数量
            hlen key-name 返回散列包含的键值对的数量
            hexists key-name key 检查给定键是否存在与散列中
            hkeys key-name 获取散列包含的所有键
            hvals key-name 获取散列包含的所有值
            hgetall key-name 获取散列中包含的所有键值对
            hincby key-name key increment 将键key保存的值加上整数increment
            hincrbyfloat key-name key increment 将键key保存的值上加上浮点数increment
2).redis的散列可以让用户将多个键值对存储到redis键里面
3).像hmget和hset这种批量处理多个键值对的命令既可以为用户带来方便,并且可以通过减少命令的调用次数以及客户端与redis之间的通信次数来提升redis的性能
4).如果散列包含的值非常大,用户可以使用hkeys去除所有的键,然后使用hget一个一个的取出对应的值,从而避免因一次取出多个大体积的值导致服务器阻塞
  关于Redis的有序集合ZSet
1).有序集合与散列一样,都用于存储键值对;有序集合的键被称为成员(member)每个成员都是唯一的;
       有序集合的值则被称为分值(score),分值必须是浮点数;有序集合是Redis里面唯一一个既可以根据成员访问元素,
        又可以根据分值以及分值的排列顺序来访问元素的结构
2).方法
            zadd key-name score member [score member...] 将带有给定分值的成员添加到有序集合里面
            zrange key-name start stop [withscores] 返回有序集合中排名介于start和stop之间的成员,如果给定了可选的withscores(just withscores本身)选项,那么命令会将成员的分值也一并返回
            zrangebyscore key-name min max [withscores] [limit offset count] 返回有序集合中排名介于min和max之间的所有成员,
            zrem key-name member [member...] 从有序集合里面移除给定的成员,并返回被移除成员的数量
            zcard key-name 返回有序集合包含的成员数量
            zincrby key-name increment member 将成员的分值上加上increment
            zcount key-name min max 返回分值介于min和max之间的成员数量
            zrank key-name member 返回成员member在有序集合中的排名
            zscore key-name member 返回成员member的分值
            zrevrank key-name member 返回有序集合成员member所处的位置,成员按照分值从大到小排列
            zrevrange key-name start stop [withscores] 返回有序集合给定的排名范围内的成员,成员按照分值从大到小排列
            zrevrangebyscore key-name max min [withscores] [limit offset count] 获取有序集合中分值介于min和max之间的所有成员,并按照分值从大到小的顺序返回他们
            zremrangebyrank key-name start stop 移除有序集合中排名介于start和stop之间的所有成员
            zremrangebyscore key-name min max 移除有序集合中分值介于start和stop之间的所有成员
            zinterstore dest-key key-count key [key...] [weights weight [weight...]] [aggregate sum|min|max] 对给定的有序集合执行类似于集合的交集运算(sum|min|max 三种聚合函数)
            zunionstore dest-key key-count key [key...] [weights weight [weight...]] [aggregate sum|min|max] 对给定的有序集合执行类似于集合的并集运算(sum|min|max 三种聚合函数)
redis的发布与订阅功能
    发布与订阅(pub/sub)的特点是发布者(listener)负责订阅频道(channel),发送者(publisher)负责向频道发送二进制字符串消息(binary string message),每当有消息被发送到给定频道时,频道的所有订阅者都会收到消息,如果将频道看做电台的话,订阅者可以同时收听多个电台,而发送者可以在任何电台发送消息
    redis关于发布与订阅的5个命令
        subscribe channel [channel...] 订阅给定的一个或多个频道
        unsubscribe channel [channel...] 退订给定的一个或多个频道,如果执行时没有给定任何频道,那么退订所有频道。
        publish channel message 向给定频道发送消息
        psubscribe pattern [pattern] 订阅与给定模式相匹配的所有频道
        punsubscribe [pattern [pattern...]] 退订给定的模式,如果执行时没有给定任何模式,那么退订所有模式

        
redis的其他方法
    排序 sort
        sort source-key [by pattern] [limit offset count] [get pattern [get pattern...]] [asc|desc] [alpha] [store dest-key] 根据给定的选项,对输入列表、集合或者有序集合进行排序,然后返回或者存储排序的结果
    使用sort命令提供的选项可以实现以下功能:根据降序而不是默认的顺序来排序元素;
            将元素看作数字来进行排序
            将元素看作是二进制字符串来进行排序
            使用被排序元素之外的其他值作为权重来进行排序,甚至还可以从输入的列表、集合、有序集合以外的其他地方进行取值。

redis的基本事务
    redis的基本事务需要用到multi和exec命令,这种事务可以让一个客户端在不被其他客户端的打断的情况下执行多个命令,被multi和exec命令包围的所有命令会一个接一个的执行,直到所有命令执行完毕,当一个事务执行完毕以后,redis才会处理其他客户端的命令。
    在redis中执行事务时,首先执行multi命令,然后输入那些我们想要在事务里面执行的命令,最后在执行exec命令,当redis从一个客户端接收到multi命令以后,redis会将这个客户端发送的所有命令都放在一个队列里,直到接受到客户端发送的exec命令,当redis使用只增文件(AOF:Append-Only File )时,redis能够确保使用一个单独的write系统调用,这样便能将事务写入磁盘,当redis服务器宕机,或者系统管理员以某种方式体质了redis服务进程的运行,redis很有可能只执行了事务的一部分操作,redis将会在重启时检查上述状态,然后退出运行,并且输出报错信息,使用redis-check-aof工具可以修复上述的只增文件,这个工具将上述文件中删除中不完全是事务,这样redis服务器才能再次启动。
    相关命令
        multi: 用于标记事务块的开始,redis会将后续的命令逐个放入队列中,然后才能使用exec命令原子化执行这个命令序列,这个命令的返回值是一个简单的字符串,总是OK。
        exec: 在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态。当使用watch命令时,只有当后监控的键没有被修改时,调用exec命令才会执行事务中的命令,这种方式利用了检查在设置(check and set)的机制。exec这个命令的返回值是一个数组,其中每个元素分别是原子化事务中的每个命令的返回值。当使用watch命令是若果事务执行中止,那么exec命令就会返回一个null值·。
        discard: 清楚所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。如果使用了watch命令,discard命令就会将当前连接监控的所有键取消监控。这个命令的返回值总是OK。
        watch: 当某个事务需要按照条件执行时,就要使用这个命令将给定的键设置为受监控的
            watch key [key...] 返回值是一个字符串,总是OK。对每个键来说,时间复杂度总是0(1)。
        unwatch: 清除所有先前为一个事务监控的键。如果你调用了exec或discard命令,那么就不需要手动调用unwatch命令,返回值是一个字符串,总是OK。对每个键来说,时间复杂度总是0(1)。

方法使用实例:
 
    上面的命令原子化的递增foo、bar键的值
    exec返回值是一个数组,其中的每个元素是每个命令的返回值,返回值的顺序和命令的执行顺序一致。
    当一个redis连接正处于multi请求的上下文时,通过这个连接发出的所有命令的返回值都是queue字符串。

事务内部的错误
    在一个事务的运行期间一般会遇到两种类型的命令错误:
        1).一个命令可能会在被放入队列时失败,服务器报错,并丢弃这个事务,如果调用exec命令,错误信息是                
                     EXECABORT Transaction discarded because of previous errors
                     由于先前的错误而丢弃的事务
        2).在调用exec命令之后发生的事务错误,redis不会进行任何特殊处理:事务在运行期间,即使某个命令运行失败,所有其他的命令也将会继续执行。

redis不支持回滚的原因
    1.只有当被调用的redis命令有语法错误时,这条命令才会执行失败,或是对某个键执行不符合其数据类型的操作;这种错误很有可能是长须在开发期间才出现,一般很少在生产环境发现,redis在系统内部进行功能简化,这样可以确保更快的运行速度。因此redis不需要事务回滚的能力。
    2.事务回滚机制不能解决任何程序问题。

丢弃命令队列(queude)
    discard命令可以用来中止事务运行,在这种情况下,不会执行事务中的任何命令,并且将redis连接恢复为正常状态
    示例

通过CAS(check and set)操作实现乐观锁
    redis通过使用watch命令实现事务的“检查再设置”行为
    作为watch命令的键会受到redis的监控,redis能够检测到它们的变化。在执行exec命令之前,如果redis检测到至少一个键被修改了,那么整个事务便会中止运行,然后exec命令会返回一个null值,提醒用户事务运行失败。
因为在执行multi命令和exec命令,只是要将执行的命令添加到队列中,并不执行,如果这时watch监测的某个键的值发生了改变,被其他客户端改变,那么这个实物奖运行失败。

键的过期时间
    在使用redis时有些数据可能在某个时间点之后就不再有用了,这时可以通过del命令显式的删除这些无用数据,也可以通过redis的过期时间特性来让一个键在给定的时限(timeout)之后自动删除,
以下为使用键的过期操作来自动删除过期数据并降低redis内存占用的方法
        persist key-name 移除键的过期时间
        ttl key-name 查看给定键距离过期还有多少秒
        expire key-name seconds 让给定的键在指定的秒数之后过期
        expireat key-name timestamp 将给定键的过期时间设置为给定的unix时间戳
        pttl key-name 查看给定键距离过期事件还有多少毫秒
        pexpire key-name milliseconds 让给定键在指定的毫秒数之后过期
        pexpireat key-name timestamp-milliseconds 将一个毫秒级精度的unix时间戳设置为给定键的过期时间

redis对数据安全和性能的保障
    redis持久化选项:redis提供了两种不同的持久化方法来将数据存储到硬盘里面;
        1).快照(snapshotting):将存在某一时刻的所有数据都写入硬盘里面。
        2).只追加文件(append-only file)会在执行写命令时,将被执行的写命令复制到硬盘里面
        以上两种方法可以同时使用也可以单独使用
        将内存中的数据存储到硬盘的主要原因是为了之后重用数据,或者是为了防止系统故障而将数据备份到一个远程位置,或者是因为存储在redis中的数据是经过长时间计算得出的,或者有程序正在使用redis存储的数据计算 
    快照持久化的配置选项
    save 60 1000   ----->60秒内有1000次写入
    stop-writes-on-bgsave-error no   ----->当创建快照失败后仍然继续执行写命令
    rdbcompression yes   
    dbfilename dump.rdb   ----->快照将被写入dbfilename选项指定的文件里面
    AOF持久化选项
    appendonly no
    appendfsync everysec
    no-appendfsync-on-rewrite no
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb

    dir ./    ----->共享选项,决定了快照文件和aof文件的保存位置
        快照持久化,在创建快照后,用户可以对快照进行备份,可以将快照复制到其他服务器从而创建出具有相同数据的服务器副本,还可以将快照保留在原地以便重启服务器时使用;如果在新的快照文件创建完毕前,redis、系统或硬件这三者之中的任意一个崩溃了,那么redis将丢失最近一次创建快照之后写入的所有数据。
    创建快照的办法;
        1).客户端可以通过向redis发送bgsave命令创建一个快照。对于支持bgsave命令的平台来说(基本所有平台都支持,除了windows平台)redis会调用fork来创建一个子进程,然后子进程负责将快照写入硬盘,父进程继续处理命令请求。
        2).客户端可以向redis发送save命令来创建快照,接收到save的redis服务器在快照创建完毕前将不再响应任何其他命令。save命令并不常用,我们通常只会在没有足够内存区执行bgsave命令的情况下,又或者即使等待持久化操作执行完毕也无所谓的情况下,才会使用save命令
        3).如果用户设置了save配置选项,比如save 60 10000,那么从redis最近创建快照之后开始算起,当“60秒之内有10000次写入”这个条件被满足时,redis就会自动出发bgsave命令,如果用户配置了多个save选项,那么当任意一个save配置选项设置的条件被满足时,redis就会触发一次bgsave命令。
        4).当redis通过shutdown命令接收到关闭服务器的请求时,或者接收到标准term信号时,会执行一个save命令,阻塞所有客户端,不在执行客户端发送的任何命令,并在save命令执行完毕之后关闭服务器。
        5).当一个redis服务器连接另一个redis服务器,并向对方发送sync命令来开始一次复制操作的时候,如果主服务器目前没有在执行bgsave操作,或者主服务器并非刚刚执行完bgsave,那么主服务器就会执行bgsave命令,

        快照持久化场景;
            对日志进行聚合计算;需要考虑的因素:1).如果redis因为崩溃而未能成功创建新的快照,那么我们能承受丢失多长时间以内的产生的新数据。2).如何恢复因为故障而被中断的日志处理操作
            大数据;
        AOF持久化,AOF持久化会将被执行的写命令写到AOF文件的末尾,以此来记录数据发生的变化,因此,redis只要从头到尾重新执行一次AOF文件包含的所有写命令,就可以恢复AOF文件所记录的数据集。
            文件同步:用户可以命令操作系统将文件同步到硬盘,同步操作会一直阻塞直到文件被写入硬盘为止,当同步操作执行完毕之后,即使系统出现故障也不会对被同步的文件造成任何影响;
               appengfsync选项及同步频率
                    always 每个redis写命令都要同步写入硬盘,这样会严重降低redis的速度
                    everysec 每秒执行一次同步,显式的将多个写命令同步到硬盘
                    no 让操作系统来决定应该何时进行同步
            虽然AOF持久化非常灵活的提供了多种不同的选项来满足不同应用程序对数据安全的不同要求,但AOF持久化也有缺陷-那就是AOF文件的体积大小
        
重写/压缩AOF文件
       启用AOF持久化后redis会不断地将被执行的写命令记录到AOF文件里面,随着redis的不断运行,AOF的体积也会不断增长,极端情况下Redis的AOF文件会用完硬盘的所有可用空间;redis在重启之后需要通过重新执行AOF文件记录的所有写命令还原数据集,如果AOF文件体积非常大,那么还原操作执行时间就可能会非常长。
        重写AOF文件(调用bgrewriteaof命令)可以使AOF文件尽可能的小,该命令和bgsave命令的工作原理非常相似,会创建子线程负责对AOF文件进行重写,如果AOF文件体积过大,那么在对AOF文件进行重写以及删除旧数据时有可能会导致操作系统挂起数秒,AOF持久化可以通过设置auto-aof-rewrite-percentage选项和auto-aof-rewrite-min-size选项来自动执行bgrewriteaof命令

复制(replication)
    复制可以让其他服务器拥有一个不断的更新的数据副本,从而使得拥有数据副本的服务器可以用于处理客户端发送的读请求;
    sunionstore命令的性能 对两个分别包含10000个元素的集合执行该命令,会产生一个包含20000个元素的结果集合,需要花费redis7、8毫秒的时间;
    在需要扩展读请求的时候,或者在需要写入临时数据时,用户可以通过设置额外的redis从服务器来保存数据集的副本,在接收到主服务器发送的数据初始副本之后,客户端每次向主服务器进行些写入时,从服务器都会实时的得到更新,再部署好主从服务器之后,客户端就可以向任何一个从服务器发送读请求了,而不需要每次都把读请求发送给主服务器

redis的复制相关选项的配置
     当从服务器连接主服务器时,主服务器会进行bgsave操作,因此为了正确使用复制特性,主服务器需要正确设置dir、dbfilename选项,并且这两个选项所指示的路径和文件对于redis进程来说都是可写的(writable).
     开启从服务器所必需的的选项只有slaveof,启动redis服务器,制定一个包含slaveof host port 选项的配置文件,redis服务器会根据选项的ip、port来连接主服务器;
     对于一个正在运行的redis服务器,用户可以通过发送slaveof no one命令来让服务器终止复制操作,不再接受主服务器的数据更新;

redis复制的启动过程
从服务器连接主服务器的步骤:
                                          主服务器操作                                                                                                             从服务器操作
1            (等待命令进入)                                                                                                       连接(或者重连接)主服务器,发送sync命令

2              开始执行bgsave,并使用缓冲区记录bgsave之后执行的所有写命令                               根据配置选项来决定是继续使用现有的数据(如果有的话)来处理客户端的命令请求还是向发送请求的客户端返回错误

3              bgsave执行完毕,向服务器发送快照文件,并在发送期间据需使用                                丢弃所有旧数据(如果有的话),开始载入主服务器发来的快照文件
                缓冲区记录被执行的写命令

4              快照文件发送完毕,开始向从服务器发送存储在缓冲区里面的写命令                              完成对快照文件的解释操作,像往常一样开始接受命令请求

5              缓冲区存储的写命令发送完毕:从现在开始,每执行一个写命令,就
                向 从服务器发送相同的写命令                                                                                     执行主服务器发来的所有存储在缓冲区里面的写命令;并从现在开始,接收并执行主服务器传来的每个写命令

        以上是从服务器连接主服务器时的步骤,由上面的步骤可以看出,redas服务器在复制进行期间,会尽可能的处理接受到的命令请求,但由于主从服务器之间的网络带宽不足,或者主服务器没有足够的内存来创建子进程和创建记录写命令的缓冲区,那么redis在处理命令请求的效率就会受到影响;
        可以通过配置slaveof host port来将一个redis服务器设置为从服务器;还可以想运行中的redis服务器发送slaveof命令来将其设置为从服务器;
        从服务器在与主服务器进行初始连接时,数据库原有的数据都将丢失,并被替换成主服务器发来的数据;
        redis不支持猪猪复制(master-master replication)
        当多个从服务器尝试连接一个主服务器的时候;分两种情况
            1).在上面步骤3尚未执行时,所有服务器都会接受到相同的快照文件和相同的缓冲区写命令;
            2).在上面步骤3正在执行或者已经执行完毕,当服务器与较早进行连接的从服务器执行完毕上面的5个步骤之后,主服务器会与新连接的从服务器重新执行一次上面的步骤;

主从链
    redis的主服务器和从服务器并没有什么特别不同的地方,所以从服务器也可以拥有自己的从服务器,由此可以形成主从链。
    slave-slave与master-slave在进行复制时的唯一区别在于:如果slave1拥有slave2,那么在slave1在执行上面步骤4时,它将断开与slave2的连接,导致slave2需要重新连接并重新同步;
    当读请求的重要性明显高于写请求的重要性,并且读请求的数量远远超出一台redis服务器可以处理的范围,用户就需要添加新的从服务器来处理读请求。
    redis主从复制树(master/slave replica tree)

      为了将数据保存到多台机器上,首先需要为主服务器设置多个从服务器,然后对每个从服务器设置appendonly yes选项和appendfsync everysec选项,如果有需要的话可以对主服务器进行相同的设置;这样可以让多台服务器以1s/次的频率将数据同步到硬盘上了;
    
  检验硬盘写入
        1).判断主服务器是否把写数据发送至从服务器的方式,向主服务器写入真正的数据之后,再向主服务器写入一个唯一的虚构值,然后通过检查虚构值是否存在与从服务器来判断写数据是否已经到达从服务器上;
        2).判断数据是否已经保存到硬盘里面的方式,检查info命令的输出结果中aof_pending_bio_fsync属性的值是否为0,是0的话就表示服务器已经将已知的所有数据都保存到硬盘里面了
redis的info命令对于了解redis服务器的综合状态非常有帮助,以下是redis的info方法下的信息显示:
"# Server
redis_version:3.0.503//redis服务器版本号
redis_git_sha1:00000000//Git SHA1
redis_git_dirty:0//Git dirty flag
redis_build_id:d14575c6134f877
redis_mode:standalone
os:Windows//服务器的宿主操作系统
arch_bits:64//架构32/64位
multiplexing_api:WinSock_IOCP//redis使用的事件处理集中
process_id:3628//服务器进程的PID
run_id:9fae8fbb688736987f1c4bdd78561ee9507445bf//redis服务器随机标识符
tcp_port:6379//TCP/IP监听端口
uptime_in_seconds:197656//自redis服务启动以来,经过的秒数
uptime_in_days:2//自redis服务器启动以来,经过的天数
hz:10
lru_clock:11854162//以分钟为单位进行自增的时钟,用于LRU管理
config_file:D:\redis-3.0\redis.conf

# Clients
connected_clients:2//已连接客户端的数量(不包括从属服务器连接的客户端)
client_longest_output_list:0//当前连接的客户端当中最长的输出列表
client_biggest_input_buf:0//当前连接中最大输入缓存
blocked_clients:0//正在等待阻塞命令的(BLPOP\BRPOP\BRPOPLPUSH)的客户端的数量

# Memory
used_memory:720360//由redis分配器分配的内存总量(以字节byte为单位)
used_memory_human:703.48K//以人类可读的格式返回redis分配的内存总量
used_memory_rss:682592//从操作系统的角度,返回redis已分配的内存总量
used_memory_peak:721328//redis的内存消耗峰值
used_memory_peak_human:704.42K//以人类可读的格式返回redis的内存消耗峰值
used_memory_lua:36864//Lua引擎所使用的内存的大小(以字节为单位)
mem_fragmentation_ratio:0.95// used_memory_rss与 used_memory的比值
mem_allocator:jemalloc-3.6.0//在编译时指定的,redis所使用的内存分配器,可以是libc\jemalloc\tcmalloc

# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1521605946
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

# Stats
total_connections_received:4
total_commands_processed:58
instantaneous_ops_per_sec:0
total_net_input_bytes:1371
total_net_output_bytes:9466
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0

# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

# CPU
used_cpu_sys:2.70
used_cpu_user:6.64
used_cpu_sys_children:0.00
used_cpu_user_children:0.00

# Cluster
cluster_enabled:0

# Keyspace
db0:keys=53,expires=0,avg_ttl=0
"

处理系统故障
    1.验证快照文件和AOF文件
    2.更换故障主服务器:A:原主服务器;B从服务器;C:新主服务器;D新从服务器
            方法一:向B发送save命令让他创建一个新的AOF快照文件,接着将这个AOF快照文件发送给机器C,并在C上面启动Redis,最后将B成为机器C的从服务器;
            方法二:将B升级为主服务器,并为B创建C
            以上两种方法都可以让redis恢复到一个主服务器一个从服务器的状态;

redis事务
    redis事务与传统的关系数据库的事务不相同;redis的简单的事务处理以multi开始多个执行命令和exec命令结束,多个事务同事对同一个对象时通常使用二阶提交(two-phase-commit)
        延迟执行事务有助于提升性能,即一次性发送多个命令,然后等待所有回复出现,他可以通过减少客户端与redsi服务器之间的网络通讯次数来提升redis在执行多个命令时的性能。
    在关系型数据库中,数据库会对被访问的数据进行加锁,直到事务被提交(commit)或者回滚(rollback),如果有其他的客户端对数据进行写入,那么该客户端将被阻塞,直到第一个事务执行完毕为止,但缺点是持有锁的客户端运行越慢,等待解锁的客户端被阻塞的时间就越长,加锁会造成长时间的等待(此为悲观锁 pessimistic locking);
    redis为了尽可能的减少客户端的等待时间不会在执行watch的时候对数据进行加锁,相反的是redis会在数据已经被其他客户端抢先修改的情况下,通知执行watch的客户端;(此为乐观锁optimistic locking)
乐观锁在实际使用中同样非常有效,客户端永远不需要等待第一个获取锁的客户端,只需要在自己事务执行失败以后进行重试即可;
     
非事务型流水线

    对于像mget、mset、hmget、hmset、rpush、lpush、sadd、zadd这些命令简化了那些需要重复执行相同命令的操作,极大的提升了性能;即非事务性流水线,尽可能的减少应用程序与redis之间的通信往返次数。

未完待续......

你可能感兴趣的:(数据库)