一、慢查询日志二、事务(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。
优点:
使用默认的每秒 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 缓存雪崩
大量的热点数据设置了相同的过期时间,导在缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩,甚至导致数据库崩溃。
解决方法:
过期时间打散:给缓存的过期时间时加上一个随机值时间,使得每个数据的过期时间分散开来,不要集中在同一时刻失效。热点数据不过期:该方式和缓存击穿一样。加互斥锁:该方式和缓存击穿一样。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)