Redis高级特性浅解

Redis高级特性浅解,第1张

Redis高级特性浅解

文章目录

一、慢查询日志二、事务(Transactions)

2.1 回滚(roll back)2.2 使用 check-and-set *** 作实现乐观锁 三、发布/订阅(Pub/Sub)四、位图(Bitmaps)五、HyperLogLogs六、GEO七、持久化(Persistence)

7.1 RDB(Redis Database)

7.1.1 优缺点7.1.2 使用方法 7.2 AOF(Append only File)

7.2.1 优缺点7.2.2 使用方法7.2.3 AOF 重写 7.3 如何选择持久化方式 八、主从复制

8.1 工作原理8.2 使用方法 九、哨兵(Sentinel)

9.1 使用方法9.2 所依赖的配置文件 十、集群(Cluster)

10.1 搭建方法10.2 集群 *** 作 十一、缓存的使用和优化

11.1 双写一致性

11.1.1 先更新数据库,再更新缓存(极少使用)11.1.2 先删除缓存,再更新数据库(酌情使用)11.1.3 先更新数据库,再删除缓存(推荐使用) 11.2 缓存过期策略11.3 缓存穿透、击穿、雪崩

11.3.1 缓存穿透11.3.2 缓存击穿11.3.3 缓存雪崩

一、慢查询日志

慢查询日志是为了记录执行时间超过给定时长的redis命令请求,让使用者更好地监视和找出在业务中一些慢redis *** 作,找到更好的优化方法。

在Redis中,关于慢查询有两个设置:

slowlog-max-len:

慢查询最大日志数,默认值为128。

设置为0时,没有数量限制;设置为<0时,不记录任何命令。

slowlog-log-slower-than:

慢查询最大超时时间,单位为微秒,默认值为10000微秒。推荐设置为1000微秒。

注意!负数表示禁用慢速日志,而0则强制记录每个命令的日志。

获取慢查询记录的命令:

获取慢查询记录:

SLOWLOG GET [数量]

记录由4个部分构成:记录的标识id、发生的时间戳、命令耗时、执行的命令和参数。

获取慢查询记录的数量:

SLOWLOG LEN

清空慢查询日志:

SLOWLOG RESET
二、事务(Transactions)

事务的特征:

事务是一个单独的隔离 *** 作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。事务是一个原子 *** 作:事务中的命令要么全部被执行,要么全部都不执行。

用法:

MULTI    # 开启事务

# 若干条命令……
# 这些命令会被保存到队列中,不会执行

# 如果想清空队列中的命令,并放弃执行事务:DISCARD

EXEC    # 执行队列中的 *** 作
2.1 回滚(roll back)

Redis 不支持回滚,它在事务失败时不进行回滚,而是继续执行余下的命令。这样做的理由:

Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的 key 上面。而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。 2.2 使用 check-and-set *** 作实现乐观锁

WATCH命令用来监视一个 key 是否被修改。如果至少一个被监视的键在EXEC执行之前被修改了, 那么整个事务都会被取消, EXEC返回nil-reply来表示事务已经失败。

三、发布/订阅(Pub/Sub)

Redis 发布订阅是一种消息通信模式:

发布者:发布者将消息发布到不同的频道(channels)。

订阅者:订阅者订阅一个或多个频道,并从中接收消息。

当一个发布者通过发布一条消息到A频道时,所有订阅了A频道的订阅者都会接收到这条消息,但是后来订阅的订阅者无法获取历史消息。

发布/订阅与key所在空间没有关系,它不会受任何级别的干扰,包括不同数据库编码。 发布在db 10,订阅可以在db 1。 如果你需要区分某些频道,可以通过在频道名称前面加上所在环境的名称(例如:测试环境,演示环境,线上环境等)。

常用命令:

发布消息:

PUBLISH channel message    # 返回订阅者个数

指定频道名称订阅频道:

SUBSCRIBE channel [channel ……]

按照模式订阅匹配到的频道:

PSUBSCRIBE [pattern ……]

模式:

? :代表匹配任意一个字符。*:代表匹配任意多个字符。[ab]:代表匹配a或者b。

指定频道名称取消订阅频道:

UNSUBSCRIBE [channel ……]
UNSUBSCRIBE    # 取消所有频道

按照模式取消订阅匹配到的频道:

PUNSUBSCRIBE [pattern ……]
PUNSUBSCRIBE    # 取消所有频道

检查发布/订阅子系统的状态:

PUBSUB CHANNELS [pattern]    # 列出至少有一个订阅者的频道
PUBSUB NUMSUB [channel ……]   #  列出给定频道的订阅者数量
PUBSUB NUMPAT     # 列出被订阅模式的数量
四、位图(Bitmaps)

位图不是实际的数据类型,而是在String类型上定义的一组面向位(bit)的 *** 作。由于字符串的最大长度为512 MB,因此可以将其设置为 2 32 2^{32} 232个不同的位。

位图的最大优点之一是,在存储信息时,它们通常可以极大地节省空间。例如,在一个由增量用户id表示不同用户的系统中,仅使用512mb内存就可以记住40亿用户的单个位信息(例如,记录一个用户是否想要接收时事通讯)。

常用命令:

设置指定的位:

SETBIT key 索引 值

超出索引,会自动用0扩展位到相应位置,然后放入值。

获取指定的位:

GETBIT key 索引

超出索引范围会返回: (integer) 0。

统计字符串被设置为1的位数:

BITCOUNT key [start end]

注意:起始、结束位置是按照字节算的,也就是8个位为1个字节。

在字符串之间执行按位 *** 作:

# 对一个或多个 key 求按位并,并将结果保存到 destkey
BITOP AND destkey key1 [key2 ……]

# 对一个或多个 key 求按位或,并将结果保存到 destkey
BITOP OR destkey key1 [key2 ……]

# 对一个或多个 key 求按位异或,并将结果保存到 destkey
BITOP XOR destkey key1 [key2 ……]

# 对指定的 key 求按位非,并将结果保存到 destkey
BITOP NOT destkey key
五、HyperLogLogs

HyperLogLog (以下简称HLL)是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。因此,常用于独立用户统计。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2 64 2^{64} 264 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

常用命令:

将指定元素添加到 HLL:

pfadd key 元素 [元素 ……]

返回HLL在 key 处观察到的集合的近似基数

PFCOUNT key [key ……]

将N个不同的HLL合并成一个单一的HLL:

PFMERGE destkey key [key ……]
六、GEO

GEO 主要用于存储地理位置信息(纬度、经度),这些信息存储在有序集合中,可以用来计算范围、距离等。

常用命令:

添加位置:

GEOADD key 经度 维度 名称 [经度 维度 名称 ……]

获取位置:

GEOPOS key 名称 [名称 ……]    # 不存在则返回 nil

获取两个位置之间的距离:

GEODIST key 名字1 名字2 [m|km|ft|mi]  # 名字后面的是距离单位

以给定坐标为中心,距离为半径,返回范围内所有的位置:

GEORADIUS key 经度 纬度 距离 m|km|ft|mi
GEORADIUSBYMEMBER key 名字 距离 m|km|ft|mi    # 用GEO中的名字指定中心位置
七、持久化(Persistence)

Redis 提供了两种级别的持久化方式,RDB 方式和 AOF 方式。

7.1 RDB(Redis Database)

RDB 持久化方式能够在指定的时间间隔后,对数据进行快照存储。例如,在24小时内每小时进行一次备份,并将这些文件保存30天。

7.1.1 优缺点

优点:

RDB 文件是一个紧凑的单一文件,方便管理和传输。RDB 在保存 RDB 文件时,父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO *** 作,所以RDB持久化方式可以最大化redis的性能。与 AOF 相比,在恢复大的数据集的时候,RDB方式会更快一些。

缺点:

如果意外断电,丢失的数据相对较多。我们通常会每隔5分钟或者更久做一次备份,恰好这5分钟内停电,就会丢失这几分钟的数据。虽然可以把备份间隔设置到很短,但也不能太短,毕竟保存整个数据集是一个比较消耗资源的工作。RDB 需要经常 fork 子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致 redis 在几毫秒甚至 1 秒内失去响应。 7.1.2 使用方法

在默认情况下, Redis 将数据库快照保存在名字为 dump.rdb 的二进制文件中。通过下面3种触发机制,触发快照的拍摄:

    save命令:该命令是同步执行的,或造成 redis 的阻塞,所以很少用。

    bgsave命令:该命令是异步执行的,由 redis fork 一个子进程,再由子进程去执行快照的拍摄。

    在配置文件中:

    # 如果在3600秒(1小时)内至少有1个键改变,触发快照的拍摄
    save 3600 1
    # 如果在300秒(5分钟)内至少有100个键改变,触发快照的拍摄
    save 300 100
    # 如果在60秒(1分钟)内至少有10000个键改变,触发快照的拍摄
    save 60 10000
    

    可以写多个触发条件。

注意!新的 dump.rdb 会替换掉旧的 dump.rdb 文件。

7.2 AOF(Append only File)

AOF 持久化方式记录每次对服务器写的 *** 作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。AOF命令以 redis 协议追加保存每次写的 *** 作到文件末尾。Redis 还能在后台对 AOF 文件进行重写,控制 AOF文件的体积不至于过大。

AOF 有3 fsync(同步内存中所有已修改的文件数据到储存设备)策略:

    no:无 fsync;everysec:每秒一次 fsync(默认);always:每次写入的时候 fsync。
7.2.1 优缺点

优点:

使用默认的每秒 fsync 策略,Redis 的性能依然很好,一旦出现故障,最多丢失1秒的数据。AOF 文件是一个只进行追加的日志文件,所以不需要写入的seek(文件的写指针)。即使由于某些原因(磁盘空间已满,写的过程中宕机等)未执行完整的写入命令,我们也可使用 redis-check-aof 工具修复这些问题。AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写 *** 作是绝对安全的,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。AOF 文件有序地以 Redis 协议的格式保存了所有写入 *** 作,非常易读,容易分析,导出也简单。

缺点:

对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。AOF 的速度可能会慢于 RDB ,这取决于 fsync 策略。 7.2.2 使用方法

在配置文件中:

appendonly:设置为yes,开启 AOF。appendfilename:AOF 文件的名称,如appendonly.aof。appendfsync:fsync 策略,默认值为everysec。dir:存放路径。 7.2.3 AOF 重写

AOF 重写的本质,就是删除无效的、重复的命令。我们可以使用BGREWRITEAOF命令主动调用重写机制。

与其相关的配置:

auto-aof-rewrite-percentage:

默认值为100,当前文件的大小比上一次重写后文件的大小大了100%,就触发重写

auto-aof-rewrite-min-size:触发重写的最大文件尺寸,默认值为64mb。

no-appendfsync-on-rewrite:在BGSAVE或BGREWRITEAOF过程中(消占用磁盘,导致阻塞),是否进行 *** 作的记录。当选项为:

yes:会有少量记录丢失,但没有阻塞。no:不会丢失数据,但有阻塞。 7.3 如何选择持久化方式

一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 应该同时使用两种持久化功能。

如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失,那么你可以只使用 RDB 持久化。

有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。

八、主从复制

redis 可以通过主从模式搭建多台服务器为同一个应用服务,这么做的好处:

一个服务器出现故障时,其他服务器可以继续正常工作。提高容量。实现读写分离,提高性能。……

主从模式中,有一个服务器称为 master 服务器(主服务器),而其它服务器称为 replica 服务器(从服务器,也叫 slave 服务器)。一个 master 可以有一个或多个 replica,但一个 replica 只能有一个 master。

8.1 工作原理
    从服务器使用REPLICAOF ip 端口命令连接到主服务器,并发送SYNC命令给主服务器;主服务器接收到SYNC命名后,开始执行BGSAVE命令生成 RDB 快照文件;主服务器BGSAVE执行完后,向所有从服务器发送快照文件;从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;之后,主服务器只要发生新的 *** 作,都会以命令的方式发送给所有从服务器。如果主从断开,从服务器的数据没有损坏。那么,重连之后从服务器发送PSYNC命令给主服务器。主服务器收到命令,会将断连期间缺失的数据发送给从服务器,达到快速恢复的目的。

主从服务器之间的数据流是单向的,只能从 master 流向 replica。即 replica 只能从 master 获取数据。

强烈建议给 master 开启持久化!!!因为,如果 master 重启会导致 master 的数据变为空,如果一个 replica 试图与它同步,那么这个 replica 也会被清空。

8.2 使用方法

通过命令:

从 replica 连接到 master:

REPLICAOF master的ip地址 端口

断开连接:

REPLICAOF NO ONE

该命令不会丢弃 replica 的数据。

通过配置文件:

# 配置主从
replicaof master的ip地址 端口

# replica只读
replica-read-only yes或no
九、哨兵(Sentinel)

Redis 的哨兵系统是官方提供的高可用性(High Availability)解决方案,用于管理多个 Redis 服务器,它主要执行以下工作:

监控:不断地检查你的主服务器和从服务器是否运作正常。提醒:当被监控的某个 Redis 服务器出现问题时, 哨兵可以通过 API 向管理员或者其他应用程序发送通知。自动故障迁移:当一个主服务器不能正常工作时,哨兵会将失效主服务器的一个从服务器升级为新的主服务器; 当客户端试图连接失效的主服务器时, 哨兵也会向客户端返回新主服务器的地址, 使新主服务器代替失效的旧主服务器。

Redis 哨兵是一个分布式系统, 你可以在一个架构中运行多个哨兵进程,这些哨兵通过投票来决定是否执行自动故障迁移, 以及选择哪个从服务器作为新的主服务器。

注意事项:

为了系统足够健壮,至少需要3个哨兵进程。哨兵必须运行在不同的物理机或虚拟机中,不能放在一起。 9.1 使用方法

启动命令:

redis-server /path/to/sentinel.conf --sentinel

默认情况下,Sentinels会监听到TCP端口26379的连接。

9.2 所依赖的配置文件

哨兵需要使用一个配置文件,因为系统将使用这个文件来保存当前状态,以便在重新启动时重新加载。如果没有提供,则哨兵会拒绝启动。

Redis 的源代码中包含了一个名为 Sentinel .conf 的文件,我们可以使用它进行配置,也可以自行创建配置,下面是一个最精简的配置:

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5

上面的示例配置监视了两组 Redis 实例,每个实例由一个主服务器和一个未定义数量的从服务器组成。一组实例的名称为 mymaster ,另一组的名称为 resque。sentinel配置的详细说明如下:

monitor 名称(随便写) 主服务器IP 端口 投票通过的数量:最后的这个数量一般是哨兵数量的一半+1。

down-after-milliseconds 名称 时间(毫秒):如果服务器在给定的毫秒数之内, 没有回复哨兵发送的 PING 命令, 或者返回一个错误, 那么哨兵会主观地认为服务器已经下线。

主观下线不会引起自动故障迁移,只有足够数量(投票通过的数量)哨兵都认为该服务器已经下线,这时才会引起自动故障迁移。

parallel-syncs:指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小, 完成故障转移所需的时间就越长。

十、集群(Cluster)

集群是在3.0之后加入的,在3.0到5.0版本中间,需要使用一个 ruby 脚本,5.0版本之后就不需要了。

Redis 集群是一个由多个主从节点群组成的分布式服务系统,它具有复制、高可用和分片特性,不需要哨兵也能完成自动故障迁移。

集群模式没有中心节点,可水平扩展。性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。

10.1 搭建方法

集群搭建最少需要3个主从节点才能正常运行,从服务器用于主服务器的数据备份。

    在每台机器上创建配置文件:

    主服务器:

    bind 192.168.146.199 
    port 7000
    daemonize yes
    pidfile /usr/local/redis/run/redis_7000.pid  
    logfile /usr/local/redis/log/redis_7000.log
    dir /usr/local/redis/data/7000
    
    # 启用集群模式
    cluster-enabled yes
    
    # 集群节点信息文件
    cluster-config-file nodes.conf
    
    # 节点离线的超时时间,超时就认为离线了
    cluster-node-timeout 5000
    
    # 持久化
    appendonly yes
    
    # 如果要设置密码需要增加如下配置:
    # 设置redis访问密码
    requirepass 123456
    
    # 设置集群节点间访问密码,跟上面一致
    masterauth 123456
    

    从服务器:

    bind 192.168.146.199  
    port 7001
    pidfile /usr/local/redis/run/redis_7001.pid
    logfile /usr/local/redis/log/redis_7001.log
    dir /usr/local/redis/data/7001
    cluster-config-file nodes-7001.conf
    

    相关的目录要提前创建好。

    启动所有 redis 服务:

    redis-server redis-7000.conf
    

    搭建集群(这里使用的是6.0版本,不需要 ruby 脚本):

    redis-cli -a 123456 --cluster create --cluster-replicas 1 ip:端口 ip:端口 ……
    

    --cluster-replicas 1:表示1个主服务器下挂1个从服务器;

    通过该方式创建的带有从节点的机器不能够自己手动指定主节点,redis集群会尽量把主从服务器分配在不同机器上。

每个主服务器都是平等的,都可以登录进行各种 *** 作。而从服务器之间虽然也是平等的,但它更多的是作为备份而已。

10.2 集群 *** 作

登录集群:

redis-cli -c -h ip地址 -p 端口 -a 123456

查看信息:

CLUSTER INFO

列出节点信息:

CLUSTER NODES

增加节点:

按照前面的方法配置好节点后启动,然后在集群中执行以下命令:

CLUSTER MEET IP地址 端口

删除节点:

CLUSTER FORGET 节点id
十一、缓存的使用和优化 11.1 双写一致性

数据有了变化后,在数据库和缓存之间,先更新哪一个,有以下几种方案:

11.1.1 先更新数据库,再更新缓存(极少使用)

存在问题:

产生脏数据:

比如,线程A、B按照顺序先后更新了数据库,却因为某种原因,在更新缓存时,B反而跑到了A前面,这就导致了B的最新数据被A的旧数据覆盖掉,产生了脏数据。

浪费系统资源:

我们主动写入缓存的数据不一定就是用户想要读取的,主动更新缓存反而浪费计算机资源。不如直接删除,用户需要什么就缓存什么,用户所请求的数据是最具代表性的。

11.1.2 先删除缓存,再更新数据库(酌情使用)

存在问题:

产生脏数据:

比如,A线程删除了缓存,还没来得及更新数据库。恰好B线程在此期间来读取数据,未能在缓存中获得,于是从数据库中读取了旧的数据,并将旧的数据写入缓存。于是产生了脏数据。

11.1.3 先更新数据库,再删除缓存(推荐使用)

存在问题:

读取脏数据:

比如,A线程更新完数据库后,没来得及删除缓存。恰好被B线程读取了缓存当中旧的数据。

但是,这种情况极少见,而且很快就会被A线程删掉缓存,不会出现前面两种情况下,缓存中一直存在脏数据的情况。

11.2 缓存过期策略

FIFO(First In First out):

先进先出算法,淘汰最先进来的数据,完全符合队列数据结构的特征。

LRU(Least recently used):

最近最少使用算法,淘汰距离上一次被使用过去时间最久的数据。该算法能保证数据最热,所以最常用。
配置:

# 使用 LRU 算法,但仅限于设置了有效期的
maxmemory-policy volatile-lru

LFU(Least frequently used):

最近使用次数最少算法, 淘汰使用次数最少的数据。

11.3 缓存穿透、击穿、雪崩 11.3.1 缓存穿透

用户查询一个完全不存在的值时,缓存不会被命中,导致大量请求直接落到数据库上。数据库也没有数据,无法写入缓存,所以下一次请求同样也会落到数据库上,最终导致数据库压力过大。

解决方法:

接口校验:用户鉴权、数据合法性校验等,例如商品查询中,商品的ID是正整数,则可以直接对非正整数直接过滤等等。缓存空值:将空值写进缓存,但是设置较短的过期时间,该时间需要根据产品业务特性来设置。布隆过滤器:使用布隆过滤器存储所有可能访问的 key,不存在的 key 直接返回空值,存在的 key 则再进一步查询缓存和数据库。 11.3.2 缓存击穿

某一个热点数据,在缓存过期的一瞬间有大量的请求进来,由于此时缓存过期了,所以请求最终都会落到数据库上,造成瞬时数据库请求量大、压力骤增,甚至可能崩溃。

解决方法:

加互斥锁:在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询 *** 作,其他的线程阻塞,等到第一个线程将数据写入缓存后,从缓存得到数据。热点数据不过期:直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。 11.3.3 缓存雪崩

大量的热点数据设置了相同的过期时间,导在缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩,甚至导致数据库崩溃。

解决方法:

过期时间打散:给缓存的过期时间时加上一个随机值时间,使得每个数据的过期时间分散开来,不要集中在同一时刻失效。热点数据不过期:该方式和缓存击穿一样。加互斥锁:该方式和缓存击穿一样。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5715555.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存