redis学习(二)-高级特性

1. 主从复制

1.1 主从复制的特点

redis的主从复制配置十分简单,它可以使从服务器是主服务器的完全拷贝,有以下几个特点:

  1. master可以拥有多个slave
  2. 多个slave除了可以连接同一个master外,还可以连接到其他slave
  3. 使用异步复制。主从复制不会阻塞master,向slave同步数据时,master可以继续处理client的请求
  4. 可以提高系统的扩展性,如使用多个从服务器来作为只读处理器(如繁重的排序操作可以放到从服务器去做),也可以简单的用来做数据冗余

1.2 如何配置主从服务器

  1. 配置slave服务器很简单,只需要在slave端的redis.conf文件中添加以下配置
slaveof <masterip> <masterport>
# 如果 master 需要密码认证,就在这里设置
masterauth <master-password>

另外你也可以调用SLAVEOF命令,主服务器就会开始与从服务器同步。
2. 然后可通过role命令(或者info命令)查看结果
(slave服务运行在6378端口,master服务运行在6379端口)

[root@VM_0_11_centos src]# ./redis-cli -h 127.0.0.1 -p 6378
127.0.0.1:6378> role
1) "slave"
2) "127.0.0.1"
3) (integer) 6379
4) "connected"
5) (integer) 15

此时可以在master设置key,在slave上即可get到该key值

1.3 Redis主从复制是如何工作的

       如果你设置了一个从服务器,在连接时它发送了一个SYNC命令,不管它是第一次连接还是再次连接都没有关系。然后主服务器开始后台存储,并且开始缓存新连接进来的修改数据的命令。当后台存储完成后,主服务器把数据文件发送到从服务器,从服务器将其保存在磁盘上,然后加载到内存中。然后主服务器把刚才缓存的命令发送到从服务器。这是作为命令流来完成的,并且和Redis协议本身格式相同。
       你可以通过telnet自己尝试一下。在Redis服务器工作时连接到Redis端口,发送SYNC命令,会看到一个批量的传输,并且主服务器接收的每一个命令都会通过telnet会话重新发送一遍。
       当主从服务器之间的连接由于某些原因断开时,从服务器可以自动进行重连接。当有多个从服务器同时请求同步时,主服务器只进行一个后台存储。
       当连接断开又重新连上之后,一般都会进行一个完整的重新同步,但是从Redis2.8开始,重新连接之后可以从中断处继续进行复制,而不必重新同步。

1.4 只读从服务器

       从Redis 2.6开始,从服务器支持只读模式,并且是默认模式。向从服务器写入命令会报错误:

(error) READONLY You can't write against a read only slave.

这个行为是由Redis.conf文件中的slave-read-only 参数控制的,可以在运行中通过CONFIG SET来启用或者禁用。
       只读的从服务器会拒绝所有写命令,所以对从服务器不会有误写操作。但这不表示可以把从服务器实例暴露在危险的网络环境下,因为像DEBUG或者CONFIG这样的管理命令还是可以运行的。不过你可以通过使用rename-command命令来为这些命令改名来增加安全性。
       注意只读限制还可以被还原,使得从服务器还可以进行写操作。虽然当主从服务器进行重新同步或者从服务器重启后,这些写操作都会失效,还是有一些使用场景会想从服务器中写入临时数据的,但将来这个特性可能会被去掉。

1.5 注意

  • 在做主从同步时,如果主服务器关闭持久化并允许自动重启,在主服务器意外崩溃时可能导致所有服务器的数据都被清空。
  • 可以配置限制有N个以上从服务器才允许对主服务器写入,如果条件不满足,主服务器会拒绝写操作并返回一个错误。
  • 主从模式的升级——哨兵模式
           Redis的哨兵模式是以主从模式为基础的,所以说,主从模式拥有的一些缺点,在哨兵模式下也具有。哨兵模式主要是监控Master主服务器的运行情况,当然也会监控Slave从服务器的运行情况,如果Master主服务器发生了故障,该模式可以保证Slave从服务器顺利升级为Master主服务器继续提供服务,以此提高系统的高可用性。虽然哨兵模式比主从模式提高了不少系统的高可用性,但是该模式不能水平扩容,不能动态的增、删节点,这也是限制哨兵模式广泛应用的主要原因。Redis也看到了这个情况,所在在Redis的3.x以后的版本提供了一个更加强大集群模式,那就是Cluster集群模式

2. redis的持久化

2.1 快照RDB(snapshoting)

       快照是默认的持久化方式。这种方式将内存中的数据以快照的方式写入到磁盘的二进制文件中,默认的文件名为dump.rdb。我们可以配置redis在n秒内如果超过m个key就自动做快照:

save 900 1    #     900 秒内如果至少有 1 个 key 的值变化,则保存
save 300 10     #   300 秒内如果至少有 10 个 key 的值变化,则保存
save 60 10000   #   60 秒内如果至少有 10000 个 key 的值变化,则保存


# 默认情况下,如果 redis 最后一次的后台保存失败,redis 将停止接受写操作,
# 这样以一种强硬的方式让用户知道数据不能正确的持久化到磁盘,
# 否则就会没人注意到灾难的发生。
#
# 如果后台保存进程重新启动工作了,redis 也将自动的允许写操作。
#
# 然而你要是安装了靠谱的监控,你可能不希望 redis 这样做,那你就改成 no 好了。
stop-writes-on-bgsave-error yes
 
# 是否在 dump .rdb 数据库的时候使用 LZF 压缩字符串
# 默认都设为 yes
# 如果你希望保存子进程节省点 cpu ,你就设置它为 no ,
# 不过这个数据集可能就会比较大
rdbcompression yes
 
# 是否校验rdb文件
rdbchecksum yes
 
# 设置 dump 的文件位置
dbfilename dump.rdb
 
# 工作目录
# 例如上面的 dbfilename 只指定了文件名,
# 但是它会写入到这个目录下。这个配置项一定是个目录,而不能是文件名。
dir ./

       你也可以通过调用 SAVE或者 BGSAVE , 手动让 Redis 进行数据集保存操作。它们俩的区别是save会直接在主进程中执行备份(此时主进程阻塞,就相当于此时redis不能提供服务了),bgsave会启动一个子进程执行备份操作,而父进程继续提供服务。 
快照RDB的优点

  • RDB是一个非常紧凑的文件,它保存了某个时间点的数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
  • RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心(可以加密),非常适用于灾难恢复.
  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.

快照RDB的缺点

  • 如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.
  • RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.
    由于快照方式是在一定时间间隔内做一次快照,如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。

2.2 AOF(Append Only File)

aof比快照有更好的持久化性,redis会将每一个收到的命令都通过write函数追加到文件(默认文件名为appendonly.aof)中,redis重启时会通过重新执行文件中保存的命令来在内存中重建整个数据库的内容。

appendonly yes   启用持久化方式

由于os(Operating System )会在内核中缓存write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也是有可能丢失部分修改。可以通过配置文件告诉redis我们想要通过fsync函数强制os写入到磁盘的时机

# appendfsync always    dofsync after every write to the append only log . Slow, Safest
appendfsync everysec     fsync only one time every second. Compromise
# appendfsync no     don't fsync, just let the OS flush the data when it wants. Faster

AOF 优点

  • 使用AOF 会让你的Redis持久性性能更好: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理 的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.
  • AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行优化重写,使得AOF文件的体积不至于过大, 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合(例如将多个incr命令优化为一个set命令)。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

AOF 缺点

  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
  • 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)

       你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.

如何选择使用哪种持久化方式?

       你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
       一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
       如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
       有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。
redis持久化官方文档

3. 内存优化

3.1 虚拟内存

  • 就是暂时把不经常访问的数据从内存交换到磁盘中,从而腾出宝贵的内存空间用于其他需要访问的数据。redis虚拟内存官方中文文档。(虚拟内存功能在2.6版本后已经去除,个人感觉是因为现在内存不足有更好的方式(如集群),没必要因此去损失最关键的性能)

3.2 设置maxmemory

       为了存储用户数据,当设置了maxmemory后Redis会分配几乎和maxmemory一样大的内存(然而也有可能还会有其他方面的一些内存分配).

       精确的值可以在配置文件中设置,或者在启动后通过 CONFIG SET 命令设置(see Using memory as an LRU cache for more info). Redis内存管理方面,你需要注意以下几点:

       当某些缓存被删除后Redis并不是总是立即将内存归还给操作系统。这并不是redis所特有的,而是函数malloc()的特性。例如你缓存了5G的数据,然后删除了2G数据,从操作系统看,redis可能仍然占用了5G的内存(这个内存叫RSS,后面会用到这个概念),即使redis已经明确声明只使用了3G的空间。这是因为redis使用的底层内存分配器不会这么简单的就把内存归还给操作系统,可能是因为已经删除的key和没有删除的key在同一个页面(page),这样就不能把完整的一页归还给操作系统.
       上面的一点意味着,你应该基于你可能会用到的 最大内存 来指定redis的最大内存。如果你的程序时不时的需要10G内存,即便在大多数情况是使用5G内存,你也需要指定最大内存为10G.
       内存分配器是智能的,可以复用用户已经释放的内存。所以当使用的内存从5G降低到3G时,你可以重新添加更多的key,而不需要再向操作系统申请内存。分配器将复用之前已经释放的2G内存.
       因为这些,当redis的peak内存非常高于平时的内存使用时,碎片所占可用内存的比例就会波动很大。当前使用的内存除以实际使用的物理内存(RSS)就是fragmentation;因为RSS就是peak memory,所以当大部分key被释放的时候,此时内存的mem_used / RSS就比较高.
       如果 maxmemory 没有设置,redis就会一直向OS申请内存,直到OS的所有内存都被使用完。所以通常建议设置上redis的内存限制。或许你也想设置 maxmemory-policy 的值为 noeviction(在redis的某些老版本默认 并 不是这样)
       设置了maxmemory后,当redis的内存达到内存限制后,再向redis发送写指令,会返回一个内存耗尽的错误。错误通常会触发一个应用程序错误,但是不会导致整台机器宕掉.

3.3 尽可能使用散列表(hashes)

参考此网址

4. 用redis实现分布式锁

分布式锁特性:
不管使用什么中间件,有几点是实现分布式锁必须要考虑到的。

  • 互斥:互斥好像是必须的,否则怎么叫锁。
  • 死锁: 如果一个线程获得锁,然后挂了,并没有释放锁,致使其他节点(线程)永远无法获取锁,这就是死锁。分布式锁必须做到避免死锁。
  • 性能: 高并发分布式系统中,线程互斥等待会成为性能瓶颈,需要好的中间件和实现来保证性能。
  • 锁特性:考虑到复杂的场景,分布式锁不能只是加锁,然后一直等待。最好实现如Java Lock的一些功能如:锁判断,超时设置,可重入性等。

4.1 单机Redis的分布式锁

分布式锁的实现有哪些?

4.2 集群环境下redis的分布式锁

对于集群环境下的redis,redis官方提出了redlock的算法,但目前也有较成熟的方案有redission,redission简单使用redission使用redission_github相关文章很多.

posted on 2020-01-11 11:19  spiritt  阅读(109)  评论(0编辑  收藏  举报