Redis 学习

https://redis.io/docs/latest/develop/data-types
redis 设计是围绕着内存和 map 进行设计的,围绕着 map 是说,其本质是 kv 类型的 noSql ,比如一些过期的 key 会记录到内部的过期mp 结构中,正常的值,也是通过 map 来管理的。
SDS 字符串、List 、Set 、Hashs 、Streams、Bitmaps

数据结构

String

Typedef struct sdshdr {
	int len;
	int free;
	char buf[];
};
  • 二进制安全
  • 空间预分配,懒回收,分配时 * 2,如果大于 1M,每次走 1M。
  • 最大 512MB
  • INCR command parses the string value as an integer,
  • BitMap 的底层实现
  • SDS字符串,底层sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64 进行更细致的划分,节省空间。
  • 底层字符集选择:
    • 小于 20 位的整数,会按照 int 来存储。
    • 当存储的字符串长度小于44个字符时,采用 embstr 来存储,可以一次读入大小为 64B 的 cpu 高速缓冲行。
    • 其余采用 SDS

List

  • 基于链表的(Redis lists are implemented via Linked Lists.)
  • 底层 = zipList + 双向链表
  • 最大长度:2^32 - 1
  • 支持阻塞的命令,可以用来做一些简单的消息队列。
quikListNodeHead
quikListNodeA
quikListNodeB
quikListNodeTail
quikListNode
ZipList

redis为什么选择List :
a database system it is crucial to be able to add elements to a very long list in a very fast way.
支持随机读取的结构可以用其他数据结构来支持,比如hash
数组的话插入太慢,并且 redis 中的命令是单线程执行的,写入操作会可能阻塞过多的读操作。
链表可以更好的使用内存,数组需要连续的内存空间。

hash

dictEntry
// 散列表节点
void *key;
void *val;
// 下一个节点,用于解决冲突问题
struct dictEntry *next;
dictht
dictEntry **table; // 哈希数组
unsigned long size;
unsigned long sizemask;
unsigned long used;
dict
dictht ht[2]; // 维护写时复制时的新旧table
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
int iterators; /* number of iterators currently running */
  • hashtable 或者 ziplist 实现
  • hashtable 通过拉链解决冲突问题
  • 通过写时复制技术解决扩容问题

hash-max-ziplist-entries : 个数超过该值,转换为 hashtable 存储,default = 512
hash-max-ziplist-value : zipList 的节点的大小,超过会转换为 hashtable ,defalt = 64B

Set

  • 底层是利用的 dict(数组做桶,<string,dictEntry>的键值对)、 intSet(int8 数组,全部为整数且不超过一定数量(512)的时候会使用)
  • 最大数量:2^32 - 1
  • 海量数据且不精确的要求下可以使用 Bloom filter or Cuckoo filter
  • 提供了求交集(SINTERN A B,返回 A B的交集)、并集合(SUNION A B)和差集的命令(SDIFF A B, 返回的是A不在B的集合)。

SortedSet(ZSet)

  • 内容大于64的时候同时使用了hashTable 和 skiplist 两种设计实现,查找使用hash,插入和删除使用 skipList (https://blog.csdn.net/2401_84254279/article/details/137686853)
  • 支持排序、分数范围查找、带着分数的 union 操纵(分数会相加)、带分数的交集(求和)

A Redis sorted set is a collection of unique strings (members) ordered by an associated score. When more than one string has the same score, the strings are ordered lexicographically.

跳表

  • 跳表是一种多层级的数据结构,每一层都是一个有序链表,
  • 跳表插入和删除的性能要好于 b+ 树,更适合内存使用场景
  • 跳表节点更多一些(默认是一个节点存储一个数据),内存消耗较大,不适合存储大量的数据

在这里插入图片描述
练习 : https://leetcode.cn/problems/design-skiplist/
插入算法:

  1. 找到小于该节点的层级
  2. 判断节点值是否存在
  3. 随机产生层级数目 lvRand,如果 lvRand 大于头节点的值,则更新 maxLevel
  4. 遍历小于 lvRand 的层级更新到新节点(多层链表 node 的插入过程),从头节点的高层向下
    a. 最高层向后遍历,如果没有后继节点,则直接指向 node
    b. node 的前继节点向下走一层,重复 a
  5. 实际实现的时候为了方便更新

跳表和B+树对比:https://blog.csdn.net/m0_66490875/article/details/136842619

Streams

采用 堆树 来管理数据。

  • 通过基数树来组织消息 ID,形成有序集合。
  • 通过 hash 结构 +listPack来组织消息数据(key,listPacks )。
    在这里插入图片描述

BitMap

Bitmaps are not an actual data type, but a set of bit-oriented operations defined on the String type which is treated like a bit vector. Since strings are binary safe blobs and their maximum length is 512 MB, they are suitable to set up to 2^32 different bits.
基于字符串实现的。

小结

hash
set
zset
ZipList
hashMap
List
intSet
Set
HashMap
string

请求处理流程

操作系统的多路复用 api ---- 事件派发器 ---- 事件处理单元
命令执行单元是单线程的。
在这里插入图片描述


内存管理

键过期
过期删除策略
惰性
周期
定时触发器
内存不足
驱逐策略

键值删除策略

惰性删除、定时删除和立即删除、周期扫描

  1. 惰性删除在访问的时候检查和判断是否删除。lazyfree-lazy-eviction = yes
  2. 定时删除和立即删除需要设置定时器(触发器)。需要使用方自己提供。
  3. 周期删除,redis 默认会每段时间进行扫描,在过期字典中随机抽样,然后删除过期的key。
    Redis2.8版本后,可以通过修改配置文件redis.conf 的 hz 选项来调整频率,比如 hz = 10 表示每秒运行10次。

内存驱逐策略

在这里插入图片描述
内存满的时候会触发淘汰策略:(maxmemery)
1)noeviction: 不会驱逐任何key,新的写操作会被拒绝,Redis会返回错误。
2)allkeys-lru: 对所有key 使用 LRU算法进行删除
3)volatile-lru: 对所有设置了过期时间的key使用LRU算法进行删除
4)allkeys-random: 对所有key随机删除
5)volatile-random: 对所有设置了过期时间的key随机删除
6)volatile-ttl: 在内存不足时,首选删除 TTL(Time To Live) 值较小的键,即选择剩余过期时间最短的键进行删除
7)allkeys-lfu: 对所有key使用LFU算法进行删除
8)volatile-lfu: 对所有设置了过期时间的key使用LFU算法进行删除


Pub/Sub 机制

在这里插入图片描述

底层结构(https://blog.csdn.net/weixin_39992884/article/details/128255363):
在这里插入图片描述

  • subscribe命令的实质即为在key中添加value的订阅链。
  • Redis的订阅操作是阻塞式的,因此一旦客户端订阅了某个频道或模式,就将会一直处于订阅状态直到退出。
  • 对消息积压处理不好。

Stream 机制

https://redis.io/docs/latest/develop/data-types/streams/
5.0 之后提供
stream 是一个支持持久化的消息队列机制,每个消息具有唯一 ID,可以根据ID进行查找、删除和确认
在这里插入图片描述
在这里插入图片描述

使用建议

  • 可实现延时队列,消息广播,死信队列等。
  • 点到点不支持,规则路由支持的不是很灵活,包括消息监控等配套都不是很成熟,所以如果只是轻量级使用可以,大型和高并发的消息队列可能不适用。

结构

  • Redis Stream的底层数据结构是基数树(Radix Tree)和listpack
  • 通过基数树来组织消息 ID,形成有序集合。通过 hash 结构 +listPack来组织消息数据(key,listPacks )。
  • listpack 用于压缩节点,减少存储量,基数树用来做索引,加快查找速度。
  • 默认ID组成 : <时间戳>-<序列号>

和 pub/sub 区别

  1. pub/sub 机制的区别
    there are fundamental differences in the way you consume a stream:
  2. A stream can have multiple clients (consumers) waiting for data. Every new item, by default, will be delivered to every consumer that is waiting for data in a given stream. This behavior is different than blocking lists, where each consumer will get a different element. However, the ability to fan out to multiple consumers is similar to Pub/Sub.
  3. While in Pub/Sub messages are fire and forget and are never stored anyway, and while when using blocking lists, when a message is received by the client it is popped (effectively removed) from the list, streams work in a fundamentally different way. All the messages are appended in the stream indefinitely (unless the user explicitly asks to delete entries): different consumers will know what is a new message from its point of view by remembering the ID of the last message received.
  4. Streams Consumer Groups provide a level of control that Pub/Sub or blocking lists cannot achieve, with different groups for the same stream, explicit acknowledgment of processed items, ability to inspect the pending items, claiming of unprocessed messages, and coherent history visibility for each single client, that is only able to see its private past history of messages.
  • 扇出策略不同,stream 支持广播和消费者组的概念;
  • stream 支持持久化策略;
  • stream 提供支持可选的阻塞策略、提供消息状态感知

消息管理

append-only log 的模式。

自动生成的ID

millisecondsTime-sequenceNumber
本地时间-该时间产生的消息的序号

sequence number is used for entries created in the same millisecond.
类似雪花算法,sequenceNumber主要是防止并发的时候产生相同的ID。

ID 生成算法:
Note that because these IDs are related to time, the ones shown here may vary and will be different from the IDs you see in your own Redis instance.

基本命令

  • XADD key ID field1 value1 [field2 value2 …]: XADD:主题:ID:k:v

  • XDEL - 删除消息

  • XRANGE key start end [COUNT count] : 批量获取消息
    在这里插入图片描述

  • XLEN mystream : 返回消息数量

  • XGROUP CREATE - 创建消费者组

  • XREADGROUP GROUP - 读取消费者组中的消息

  • XINFO - 查看流和消费者组的相关信息;

生产者相关

XADD : 传递 * 时会自动生成一个唯一递增 ID,该ID生成和时间有关系。
If for some reason the user needs incremental IDs that are not related to time but are actually associated to another external system ID, as previously mentioned, the XADD command can take an explicit ID instead of the * wildcard ID that triggers auto-generation, like in the following examples:
XADD 可以提供自己的ID。

消费者

消费者同样适用消费者组的概念,使用游标机制维护消息状态。
last_delivered_id:每个消费者组会维护,任意一个消费者读取了消息都会使游标 last_delivered_id 往前移动。
pending_ids : 每个消费者维护,记录了当前已经被客户端读取的消息,但是还没有 ack (Acknowledge character:确认字符)。

消息整体是一个游标滑动的维护,对最小的pendingIDs 之前的消息是可以清除的。

消息读取

XREAD

XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id   [id ...]

only returning entries with an ID greater than the last received ID reported by the caller. This command has an option to block if items are not available
提供阻塞和非阻塞方式。

XRANGE


XRANGE key start end [COUNT count]
// 获取全部数据。- 表示最小。+ 表示最大。
XRANGE somestream - +
1) 1) 1526985054069-0
   2) 1) "duration"
      2) "72"
      3) "event-id"
      4) "9"
      5) "user-id"
      6) "839248"
2) 1) 1526985069902-0
   2) 1) "duration"
      2) "415"
      3) "event-id"
      4) "2"
      5) "user-id"
      6) "772213"

通过特殊的redis 的 ID 的设计,可以支持范围的查找。
The range is specified by a minimum and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified (closed interval) are returned.

消息积压问题

Stream在处理消息积压这种情况时也是积压到一定程度就把旧消息丢弃来防止消息积压导致内存爆炸。
XPENDING 查看消息:
通过 XPENDING 命令,可以获取指定流的指定消费者组目前的待处理消息的相关信息。在很多场景下,你需要通过它来观察和了解消费者的处理情况,从而做出处理,例如以下场景:

  • 您可以获取消费组中挂起(未确认)的消息信息,从而了解消费者处理消息的速度和效率
  • 如果某个消费者的挂起消息数量不断增加,或者某些消息长时间未被处理,可能表明该消费者存在问题
  • 在高并发或高负载的情况下,消费者可能无法及时处理所有消息。通过 XPENDING 命令检测到积压消息
  • 通过定期运行 XPENDING 命令,您可以在发现挂起消息数量超过预设阈值时触发报警
XPENDING mystream mygroup
1) (integer) 4			# 待处理消息数量
2) "1681655897993-0"	# 首条消息 ID
3) "1681655916001-0"	# 最后一条消息的 ID
4) 1) 1) "myconsumer"	# 各消费者正在处理的消息数量
      2) "4"

批量操作

pipline 机制

会将多个命令一次性发送到机器上。

  1. 不支持跨 slot,集群模式下要确保 key 都在一个 slot 中;
  2. 会占用缓存,所以命令总体长度要适中。
  3. 非原子性,仅是传输的 合并请求 的一种实现, server 会把请求拆开执行。
  4. 不支持回滚:redis 本身也没有支持回滚的操作

Lua 脚本

Lua脚本具有以下好处:
1、减少网络开销:Lua脚本在执行的时候,是先发送到Redis服务器的,然后在服务器上执行脚本。多个命令和业务逻辑都封装到脚本里,一次性提交到服务器。
2、原子性操作:我们都知道redis在执行命令时是单线程的,但是每个命令之间就存在并发的情况,就存在先查询再操作时,两个命令没办法保证线程安全。但使用Lua脚本时,redis把这个脚本操作当成是一个命令,那么这个脚本中的多条操作也就保证了原子性。(注意:只保证原子性,不是事务)

虽然Lua脚本有这么多优点,但是也不能乱用,使用的时候要注意:
1、Lua脚本可以在redis单机模式、主从模式、Sentinel集群模式下正常使用,但是无法在分片集群模式下使用。(脚本操作的key可能不在同一个分片)。(其实集群模式不支持问题也是可以解决的,在使用spring的RedisTemplate执行lua脚本时,报错EvalSha is not supported in cluster environment,不支持cluster。但是redis是支持lua脚本的,只要拿到原redis的connection对象,通过connection去执行即可,在后面会说下这个问题)
2、Lua脚本中尽量避免使用循环操作(可能引发死循环问题),尽量避免长时间运行。
3、redis在执行lua脚本时,默认最长运行时间时5秒,当脚本运行时间超过这一限制后,Redis将开始接受其他命令但不会执行(以确保脚本的原子性,因为此时脚本并没有被终止),而是会返回“BUSY”错误。监听到该错误需要手动去 kill 掉该任务(另一个客户端中 script kill )

lua 超时配置:

# 如果达到最大时间限制(毫秒),redis会记个log,然后返回error。当一个脚本超过了最大时限。只有
SCRIPT KILL和SHUTDOWN NOSAVE可以用。第一个可以杀没有调write命令的东西。要是已经调用了
write,只能用第二个命令强行停止该脚本(redis 127.0.0.1:6379> SCRIPT KILL)
lua-time-limit 5000

集群

分片

  • 未采用一致性hash
  • 分成 2^14 个槽,16384
  • 通信量 o(n^2),集群总数 1000 台左右,不适合海量数据。
  • 未同步、半同步、同步三个状态,
    • 未同步通过 RDB 来迁移数据。
    • 半同步使用 AOF 来进行同步,就是快照读 + 缓冲命令配合完成同步。
    • 已同步则直接根据后续的命令来进行。

数据同步

https://blog.csdn.net/Mr_VK/article/details/132353177

在这里插入图片描述

选举和故障恢复机制

  1. slave发现自己的master不可用;
  2. slave将记录集群的currentEpoch(选举周期)加1,并广播FAILOVER_AUTH_REQUEST 信息进行选举;
    其他节点收到FAILOVER_AUTH_REQUEST信息后,只有其他的master可以进行响应,master收到消息后返回FAILOVER_AUTH_ACK信息,对于同一个Epoch,只能响应一次ack;
  3. slave收集maste返回的ack消息
  4. slave判断收到的ack消息个数是否大于半数的master个数,若是,则变成新的master;
  5. 广播Pong消息通知其他集群节点,自己已经成为新的master。

搭建

相关配置:https://redis.io/docs/latest/operate/rs/clusters/configure/cluster-settings/

配置主从

  1. 启动 redis 的服务
  2. 通过命令进行
    a. SLAVEOF ip port # SLAVEOF host 6379 找谁当自己的老大!
    或者通过配置进行:
#复制选项,slave复制对应的master。
# slaveof <masterip> <masterport>

完整配置:

############### 主从复制 ###############
 
#复制选项,slave复制对应的master。
# slaveof <masterip> <masterport>
 
#如果master设置了requirepass,那么slave要连上master,需要有master的密码才行。masterauth
就是用来配置master的密码,这样可以在连上master后进行认证。
# masterauth <master-password>
 
#当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:1) 如果slave-serve-stale-data
设置为yes(默认设置),从库会继续响应客户端的请求。2) 如果slave-serve-stale-data设置为no,除
去INFO和SLAVOF命令之外的任何请求都会返回一个错误”SYNC with master in progress”。
slave-serve-stale-data yes
 
#作为从服务器,默认情况下是只读的(yes),可以修改成NO,用于写(不建议)。
slave-read-only yes
 
#是否使用socket方式复制数据。目前redis复制提供两种方式,disk和socket。如果新的slave连上来或
者重连的slave无法部分同步,就会执行全量同步,master会生成rdb文件。有2种方式:disk方式是
master创建一个新的进程把rdb文件保存到磁盘,再把磁盘上的rdb文件传递给slave。socket是master
创建一个新的进程,直接把rdb文件以socket的方式发给slave。disk方式的时候,当一个rdb保存的过程
中,多个slave都能共享这个rdb文件。socket的方式就的一个个slave顺序复制。在磁盘速度缓慢,网速快
的情况下推荐用socket方式。
repl-diskless-sync no
 
#diskless复制的延迟时间,防止设置为0。一旦复制开始,节点不会再接收新slave的复制请求直到下一个
rdb传输。所以最好等待一段时间,等更多的slave连上来。
repl-diskless-sync-delay 5
 
#slave根据指定的时间间隔向服务器发送ping请求。时间间隔可以通过 repl_ping_slave_period 来
设置,默认10秒。
# repl-ping-slave-period 10
 
#复制连接超时时间。master和slave都有超时时间的设置。master检测到slave上次发送的时间超过
repl-timeout,即认为slave离线,清除该slave信息。slave检测到上次和master交互的时间超过
repl-timeout,则认为master离线。需要注意的是repl-timeout需要设置一个比repl-ping-slave
period更大的值,不然会经常检测到超时。
# repl-timeout 60
 
#是否禁止复制tcp链接的tcp nodelay参数,可传递yes或者no。默认是no,即使用tcp nodelay。如果
master设置了yes来禁止tcp nodelay设置,在把数据复制给slave的时候,会减少包的数量和更小的网络
带宽。但是这也可能带来数据的延迟。默认我们推荐更小的延迟,但是在数据量传输很大的场景下,建议选择
yes。
repl-disable-tcp-nodelay no
 
#复制缓冲区大小,这是一个环形复制缓冲区,用来保存最新复制的命令。这样在slave离线的时候,不需要
完全复制master的数据,如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常
复制状态。缓冲区的大小越大,slave离线的时间可以更长,复制缓冲区只有在有slave连接的时候才分配内
存。没有slave的一段时间,内存会被释放出来,默认1m。
# repl-backlog-size 5mb
 
#master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为
秒。
# repl-backlog-ttl 3600
 
#当master不可用,Sentinel会根据slave的优先级选举一个master。最低的优先级的slave,当选
master。而配置成0,永远不会被选举。
slave-priority 100
 
#redis提供了可以让master停止写入的方式,如果配置了min-slaves-to-write,健康的slave的个数
小于N,mater就禁止写入。master最少得有多少个健康的slave存活才能执行写命令。这个配置虽然不能保
证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不能写入
来避免数据丢失。设置为0是关闭该功能。
# min-slaves-to-write 3
 
#延迟小于min-slaves-max-lag秒的slave才认为是健康的slave。
# min-slaves-max-lag 10
 
# 设置1或另一个设置为0禁用这个特性。
# Setting one or the other to 0 disables the feature.
# By default min-slaves-to-write is set to 0 (feature disabled) and
 # min-slaves-max-lag is set to 10.

配置分片

https://www.cnblogs.com/zhangwencheng/p/17717821.html

port 6379
# 开启集群功能
cluster-enabled yes
# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /tmp/6379/nodes.conf
# 节点心跳失败的超时时间
cluster-node-timeout 5000
# 持久化文件存放目录
dir /tmp/6379
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip
replica-announce-ip 192.168.150.101
# 保护模式
protected-mode no
# 数据库数量
databases 1
# 日志
logfile /tmp/6379/run.log

master 的选取策略
集群的master 配置是自动的,会根据 replicas + 1 对后面的 node 进行分组,master 数量(N) = 总数/(replicas+1),顺序取前 N 个作为master

//redis-cli --cluster 集群操作命令
// --cluster-replicas 1 副本数量
redis-cli --cluster create --cluster-replicas 1 192.168.150.101:7001 192.168.150.101:7002

slave 监控master 的健康状态,所以通信复杂度 O(n^2)。
率先获得半数以上 master 反馈的称为新的master。
经验值集群最大 1000 个节点,假设心跳为 1s 进行一次,此时会产生 100W/s 的通信,如果每个载体是 64B,会产生 64MB的通信量。

哨兵机制

哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
哨兵负责监控主节点健康状态,负责触发选取主节点的选举流程,选举通过抢先的投票流程完成,同 kafka 的controllor 直接控制选举的流程不同。

配置说明

持久化策略

# redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化) 
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化) 
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
save 300 10
save 60 10000
#默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导
致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,可以
提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时
Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendonly no
 
#aof文件名
appendfilename "appendonly.aof"
 
#aof持久化策略的配置
#no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
#always表示每次写入都执行fsync,以保证数据同步到磁盘。
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
appendfsync everysec

# 在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行
fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no,是最安全的方式,
不会丢失数据,但是要忍受阻塞的问题。如果对延迟要求很高的应用,这个字段可以设置为yes,,设置为
yes表示rewrite期间对新写操作不fsync,暂时存在内存中,不会造成阻塞的问题(因为没有磁盘竞争),等
rewrite完成后再写入,这个时候redis会丢失数据。Linux的默认fsync策略是30秒。可能丢失30秒数
据。因此,如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes。如果应用系统无法忍受
数据丢失,则设置为no。
no-appendfsync-on-rewrite no
 
#aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文
件增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志
重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100
 #设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写
auto-aof-rewrite-min-size 64mb
 
#aof文件可能在尾部是不完整的,当redis启动的时候,aof文件的数据被载入内存。重启可能发生在redis
所在的主机操作系统宕机后,尤其在ext4文件系统没有加上data=ordered选项(redis宕机或者异常终止
不会造成尾部不完整现象。)出现这种现象,可以选择让redis退出,或者导入尽可能多的数据。如果选择的
是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。如果是no,用户必须手动
redis-check-aof修复AOF文件才可以。
aof-load-truncated yes

内存淘汰策略

#redis配置的最大内存容量。当内存满了,需要配合maxmemory-policy策略进行处理。注意slave的输出
缓冲区是不计算在maxmemory内的。所以为了防止主机内存使用完,建议设置的maxmemory需要更小一些。
# maxmemory <bytes>
 
#内存容量超过maxmemory后的处理策略。
#volatile-lru:利用LRU算法移除设置过过期时间的key。
#volatile-random:随机移除设置过过期时间的key。
#volatile-ttl:移除即将过期的key,根据最近过期时间来删除(辅以TTL)
#allkeys-lru:利用LRU算法移除任何key。
#allkeys-random:随机移除任何key。
#noeviction:不移除任何key,只是返回一个写错误。
#上面的这些驱逐策略,如果redis没有合适的key驱逐,对于写命令,还是会返回错误。redis将不再接收写
请求,只接收get请求。写命令包括:set setnx setex append incr decr rpush lpush rpushx 
lpushx linsert lset rpoplpush sadd sinter sinterstore sunion sunionstore sdiff 
sdiffstore zadd zincrby zunionstore zinterstore hset hsetnx hmset hincrby incrby 
decrby getset mset msetnx exec sort。
# maxmemory-policy noeviction

主备相关

#是否在后台运行;no:不是后台运行
daemonize yes
 
#是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访
问,拒绝外部访问。
protected-mode yes
 
#redis的进程文件
pidfile /var/run/redis/redis-server.pid
 
#redis监听的端口号。
port 6379
 
#此参数确定了TCP连接中已完成队列(完成三次握手之后)的长度, 当然此值必须不大于Linux系统定义
的/proc/sys/net/core/somaxconn值,默认是511,而Linux的默认参数值是128。当系统并发量大并
且客户端速度缓慢的时候,可以将这二个参数一起参考设定。该内核参数默认值一般是128,对于负载很大的
服务程序来说大大的不够。一般会将它修改为2048或者更大。在/etc/sysctl.conf中添
加:net.core.somaxconn = 2048,然后在终端中执行sysctl -p。
tcp-backlog 511
 
#指定 redis 只接收来自于该 IP 地址的请求,如果不进行设置,那么将处理所有请求
#bind 127.0.0.1
 #bind 0.0.0.0
 
#配置unix socket来让redis支持监听本地连接。
# unixsocket /var/run/redis/redis.sock
 
#配置unix socket使用文件的权限
# unixsocketperm 700
 
# 此参数为设置客户端空闲超过timeout,服务端会断开连接,为0则服务端不会主动断开连接,不能小于
0。
timeout 0
 
#tcp keepalive参数。如果设置不为0,就使用配置tcp的SO_KEEPALIVE值,使用keepalive有两个好:检测挂掉的对端。降低中间设备出问题而导致网络看似连接却已经与对端端口的问题。在Linux内核中,
设置了keepalive,redis会定时给对端发送ack。检测到对端关闭需要两倍的设置值。
tcp-keepalive 0
 
#指定了服务端日志的级别。级别包括:debug(很多信息,方便开发、测试),verbose(许多有用的信
息,但是没有debug级别信息多),notice(适当的日志级别,适合生产环境),warn(只有非常重要的信
息)
loglevel notice
 
#指定了记录日志的文件。空字符串的话,日志会打印到标准输出设备。后台运行的redis标准输出
是/dev/null。
logfile /var/log/redis/redis-server.log
 
#是否打开记录syslog功能
# syslog-enabled no
 
#syslog的标识符。
# syslog-ident redis
 
#日志的来源、设备
# syslog-facility local0
 
#数据库的数量,默认使用的数据库是DB 0。可以通过SELECT命令选择一个db
 databases 16
 
# redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化) 
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化) 
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
 save 300 10
 save 60 10000
 
#当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以
通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
stop-writes-on-bgsave-error yes
 
#使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,但是需要一些cpu的消耗。no:不压缩,
需要更多的磁盘空间
rdbcompression yes
 
#是否校验rdb文件。从rdb格式的第五个版本开始,在rdb文件的末尾会带上CRC64的校验和。这跟有利于文
件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗,所以如果你追求高性能,可以关闭该配
置。
rdbchecksum yes
 
#rdb文件的名称
dbfilename dump.rdb
 
#数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir /data
 
 
############### 主从复制 ###############
 
#复制选项,slave复制对应的master。
# slaveof <masterip> <masterport>
 
#如果master设置了requirepass,那么slave要连上master,需要有master的密码才行。masterauth
就是用来配置master的密码,这样可以在连上master后进行认证。
# masterauth <master-password>
 
#当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:1) 如果slave-serve-stale-data
设置为yes(默认设置),从库会继续响应客户端的请求。2) 如果slave-serve-stale-data设置为no,除
去INFO和SLAVOF命令之外的任何请求都会返回一个错误”SYNC with master in progress”。
slave-serve-stale-data yes
 
#作为从服务器,默认情况下是只读的(yes),可以修改成NO,用于写(不建议)。
slave-read-only yes
 
#是否使用socket方式复制数据。目前redis复制提供两种方式,disk和socket。如果新的slave连上来或
者重连的slave无法部分同步,就会执行全量同步,master会生成rdb文件。有2种方式:disk方式是
master创建一个新的进程把rdb文件保存到磁盘,再把磁盘上的rdb文件传递给slave。socket是master
创建一个新的进程,直接把rdb文件以socket的方式发给slave。disk方式的时候,当一个rdb保存的过程
中,多个slave都能共享这个rdb文件。socket的方式就的一个个slave顺序复制。在磁盘速度缓慢,网速快
的情况下推荐用socket方式。
repl-diskless-sync no
 
#diskless复制的延迟时间,防止设置为0。一旦复制开始,节点不会再接收新slave的复制请求直到下一个
rdb传输。所以最好等待一段时间,等更多的slave连上来。
repl-diskless-sync-delay 5
 
#slave根据指定的时间间隔向服务器发送ping请求。时间间隔可以通过 repl_ping_slave_period 来
设置,默认10秒。
# repl-ping-slave-period 10
 
#复制连接超时时间。master和slave都有超时时间的设置。master检测到slave上次发送的时间超过
repl-timeout,即认为slave离线,清除该slave信息。slave检测到上次和master交互的时间超过
repl-timeout,则认为master离线。需要注意的是repl-timeout需要设置一个比repl-ping-slave
period更大的值,不然会经常检测到超时。
# repl-timeout 60
 
#是否禁止复制tcp链接的tcp nodelay参数,可传递yes或者no。默认是no,即使用tcp nodelay。如果
master设置了yes来禁止tcp nodelay设置,在把数据复制给slave的时候,会减少包的数量和更小的网络
带宽。但是这也可能带来数据的延迟。默认我们推荐更小的延迟,但是在数据量传输很大的场景下,建议选择
yes。
repl-disable-tcp-nodelay no
 
#复制缓冲区大小,这是一个环形复制缓冲区,用来保存最新复制的命令。这样在slave离线的时候,不需要
完全复制master的数据,如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常
复制状态。缓冲区的大小越大,slave离线的时间可以更长,复制缓冲区只有在有slave连接的时候才分配内
存。没有slave的一段时间,内存会被释放出来,默认1m。
# repl-backlog-size 5mb
 
#master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为
秒。
# repl-backlog-ttl 3600
 
#当master不可用,Sentinel会根据slave的优先级选举一个master。最低的优先级的slave,当选
master。而配置成0,永远不会被选举。
slave-priority 100
 
#redis提供了可以让master停止写入的方式,如果配置了min-slaves-to-write,健康的slave的个数
小于N,mater就禁止写入。master最少得有多少个健康的slave存活才能执行写命令。这个配置虽然不能保
证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不能写入
来避免数据丢失。设置为0是关闭该功能。
# min-slaves-to-write 3
 
#延迟小于min-slaves-max-lag秒的slave才认为是健康的slave。
# min-slaves-max-lag 10
 
# 设置1或另一个设置为0禁用这个特性。
# Setting one or the other to 0 disables the feature.
# By default min-slaves-to-write is set to 0 (feature disabled) and
 # min-slaves-max-lag is set to 10.
 
 
############### 安全相关 ###############
 
#requirepass配置可以让用户使用AUTH命令来认证密码,才能使用其他命令。这让redis可以使用在不受
信任的网络中。为了保持向后的兼容性,可以注释该命令,因为大部分用户也不需要认证。使用
requirepass的时候需要注意,因为redis太快了,每秒可以认证15w次密码,简单的密码很容易被攻破,所
以最好使用一个更复杂的密码。注意只有密码没有用户名。
# requirepass foobared
 
#把危险的命令给修改成其他名称。比如CONFIG命令可以重命名为一个很难被猜到的命令,这样用户不能使
用,而内部工具还能接着使用。
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
 
#设置成一个空的值,可以禁止一个命令
# rename-command CONFIG ""
 
 
############### 进程限制相关 ###############
 
# 设置能连上redis的最大客户端连接数量。默认是10000个客户端连接。由于redis不区分连接是客户端连
接还是内部打开文件或者和slave连接等,所以maxclients最小建议设置到32。如果超过了maxclients,
redis会给新的连接发送’max number of clients reached’,并关闭连接。
# maxclients 10000
 
#redis配置的最大内存容量。当内存满了,需要配合maxmemory-policy策略进行处理。注意slave的输出
缓冲区是不计算在maxmemory内的。所以为了防止主机内存使用完,建议设置的maxmemory需要更小一些。
# maxmemory <bytes>
 
#内存容量超过maxmemory后的处理策略。
#volatile-lru:利用LRU算法移除设置过过期时间的key。
#volatile-random:随机移除设置过过期时间的key。
#volatile-ttl:移除即将过期的key,根据最近过期时间来删除(辅以TTL)
#allkeys-lru:利用LRU算法移除任何key。
#allkeys-random:随机移除任何key。
#noeviction:不移除任何key,只是返回一个写错误。
#上面的这些驱逐策略,如果redis没有合适的key驱逐,对于写命令,还是会返回错误。redis将不再接收写
请求,只接收get请求。写命令包括:set setnx setex append incr decr rpush lpush rpushx 
lpushx linsert lset rpoplpush sadd sinter sinterstore sunion sunionstore sdiff 
sdiffstore zadd zincrby zunionstore zinterstore hset hsetnx hmset hincrby incrby 
decrby getset mset msetnx exec sort。
# maxmemory-policy noeviction
 
#lru检测的样本数。使用lru或者ttl淘汰算法,从需要淘汰的列表中随机选择sample个key,选出闲置时间
最长的key移除。
# maxmemory-samples 5
 
 
############### APPEND ONLY 持久化方式 ###############
 
#默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导
致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,可以
提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时
Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendonly no
 
#aof文件名
appendfilename "appendonly.aof"
 
#aof持久化策略的配置
#no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
#always表示每次写入都执行fsync,以保证数据同步到磁盘。
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
appendfsync everysec
 
# 在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行
fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no,是最安全的方式,
不会丢失数据,但是要忍受阻塞的问题。如果对延迟要求很高的应用,这个字段可以设置为yes,,设置为
yes表示rewrite期间对新写操作不fsync,暂时存在内存中,不会造成阻塞的问题(因为没有磁盘竞争),等
rewrite完成后再写入,这个时候redis会丢失数据。Linux的默认fsync策略是30秒。可能丢失30秒数
据。因此,如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes。如果应用系统无法忍受
数据丢失,则设置为no。
no-appendfsync-on-rewrite no
 
#aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文
件增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志
重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100
 #设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写
auto-aof-rewrite-min-size 64mb
 
#aof文件可能在尾部是不完整的,当redis启动的时候,aof文件的数据被载入内存。重启可能发生在redis
所在的主机操作系统宕机后,尤其在ext4文件系统没有加上data=ordered选项(redis宕机或者异常终止
不会造成尾部不完整现象。)出现这种现象,可以选择让redis退出,或者导入尽可能多的数据。如果选择的
是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。如果是no,用户必须手动
redis-check-aof修复AOF文件才可以。
aof-load-truncated yes
 
 
############### LUA SCRIPTING ###############
 
# 如果达到最大时间限制(毫秒),redis会记个log,然后返回error。当一个脚本超过了最大时限。只有
SCRIPT KILL和SHUTDOWN NOSAVE可以用。第一个可以杀没有调write命令的东西。要是已经调用了
write,只能用第二个命令杀。
lua-time-limit 5000
 
 
############### 集群相关 ###############
 
#集群开关,默认是不开启集群模式。
# cluster-enabled yes
 
#集群配置文件的名称,每个节点都有一个集群相关的配置文件,持久化保存集群的信息。这个文件并不需要
手动配置,这个配置文件有Redis生成并更新,每个Redis集群节点需要一个单独的配置文件,请确保与实例
运行的系统中配置文件名称不冲突
# cluster-config-file nodes-6379.conf
 
#节点互连超时的阀值。集群节点超时毫秒数
# cluster-node-timeout 15000
 
#在进行故障转移的时候,全部slave都会请求申请为master,但是有些slave可能与master断开连接一段
时间了,导致数据过于陈旧,这样的slave不应该被提升为master。该参数就是用来判断slave节点与
master断线的时间是否过长。判断方法是:
#比较slave断开连接的时间和(node-timeout * slave-validity-factor) + repl-ping-slave
period
 #如果节点超时时间为三十秒, 并且slave-validity-factor为10,假设默认的repl-ping-slave
period是10秒,即如果超过310秒slave将不会尝试进行故障转移 
# cluster-slave-validity-factor 10
 
#master的slave数量大于该值,slave才能迁移到其他孤立master上,如这个参数若被设为2,那么只有当
一个主节点拥有2 个可工作的从节点时,它的一个从节点会尝试迁移。
# cluster-migration-barrier 1
 
#默认情况下,集群全部的slot有节点负责,集群状态才为ok,才能提供服务。设置为no,可以在slot没有
全部分配的时候提供服务。不建议打开该配置。
# cluster-require-full-coverage yes
 
 
############### SLOW LOG 慢查询日志 ###############
 
###slog log是用来记录redis运行中执行比较慢的命令耗时。当命令的执行超过了指定时间,就记录在
slow log中,slog log保存在内存中,所以没有IO操作。
#执行时间比slowlog-log-slower-than大的请求记录到slowlog里面,单位是微秒,所以1000000就是
1秒。注意,负数时间会禁用慢查询日志,而0则会强制记录所有命令。
slowlog-log-slower-than 10000
 
#慢查询日志长度。当一个新的命令被写进日志的时候,最老的那个记录会被删掉。这个长度没有限制。只要
有足够的内存就行。你可以通过 SLOWLOG RESET 来释放内存。
slowlog-max-len 128
 
############### 延迟监控 ###############
 #延迟监控功能是用来监控redis中执行比较缓慢的一些操作,用LATENCY打印redis实例在跑命令时的耗时
图表。只记录大于等于下边设置的值的操作。0的话,就是关闭监视。默认延迟监控功能是关闭的,如果你需
要打开,也可以通过CONFIG SET命令动态设置。
latency-monitor-threshold 0
 
############### EVENT NOTIFICATION 订阅通知 ###############
 #键空间通知使得客户端可以通过订阅频道或模式,来接收那些以某种方式改动了 Redis 数据集的事件。因
为开启键空间通知功能需要消耗一些 CPU ,所以在默认配置下,该功能处于关闭状态。
#notify-keyspace-events 的参数可以是以下字符的任意组合,它指定了服务器该发送哪些类型的通
知:
##K 键空间通知,所有通知以 __keyspace@__ 为前缀
##E 键事件通知,所有通知以 __keyevent@__ 为前缀
##g DEL 、 EXPIRE 、 RENAME 等类型无关的通用命令的通知
##$ 字符串命令的通知
##l 列表命令的通知
##s 集合命令的通知
##h 哈希命令的通知
##z 有序集合命令的通知
##x 过期事件:每当有过期键被删除时发送
##e 驱逐(evict)事件:每当有键因为 maxmemory 政策而被删除时发送
##A 参数 g$lshzxe 的别名
#输入的参数中至少要有一个 K 或者 E,否则的话,不管其余的参数是什么,都不会有任何 通知被分发。详
细使用可以参考http://redis.io/topics/notifications
 
notify-keyspace-events ""
 
############### ADVANCED CONFIG 高级配置 ###############
 #数据量小于等于hash-max-ziplist-entries的用ziplist,大于hash-max-ziplist-entries用
hash
hash-max-ziplist-entries 512
 #value大小小于等于hash-max-ziplist-value的用ziplist,大于hash-max-ziplist-value用
hash。
hash-max-ziplist-value 64
 
#数据量小于等于list-max-ziplist-entries用ziplist,大于list-max-ziplist-entries用
list。
list-max-ziplist-entries 512
 #value大小小于等于list-max-ziplist-value的用ziplist,大于list-max-ziplist-value用
list。
list-max-ziplist-value 64
 
#数据量小于等于set-max-intset-entries用iniset,大于set-max-intset-entries用set。
set-max-intset-entries 512
 
#数据量小于等于zset-max-ziplist-entries用ziplist,大于zset-max-ziplist-entries用
zset。
zset-max-ziplist-entries 128
 #value大小小于等于zset-max-ziplist-value用ziplist,大于zset-max-ziplist-value用
zset。
zset-max-ziplist-value 64
 
#value大小小于等于hll-sparse-max-bytes使用稀疏数据结构(sparse),大于hll-sparse-max
bytes使用稠密的数据结构(dense)。一个比16000大的value是几乎没用的,建议的value大概为
3000。如果对CPU要求不高,对空间要求较高的,建议设置到10000左右。
hll-sparse-max-bytes 3000
 
 
10.redis持久化 
Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中
的数据库状态也会消失。所以 Redis 提供了持久化功能!
持久化过程保存什么
1.将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据  (RDB)
2.将数据的操作过程进行保存,日志形式,存储操作过程,关注点在数据的操作过程(AOF)
 
10.1 RDB方式 
概念:
在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将
快照文件直接读到内存里
 
RDB手动 
save指令
命令 :save
作用 :手动执行一次保存操作
 
save指令相关配置
#Redis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash,可以降低内存的使用。
当你的使用场景中,有非常严格的实时性需要,不能够接受Redis时不时的对请求有2毫秒的延迟的话,把这
项配置为no。如果没有这么严格的实时性要求,可以设置为yes,以便能够尽可能快的释放内存。
activerehashing yes
 
##对客户端输出缓冲进行限制可以强迫那些不从服务器读取数据的客户端断开连接,用来强制关闭传输缓慢的
客户端。
#对于normal client,第一个0表示取消hard limit,第二个0和第三个0表示取消soft limit,
normal client默认取消限制,因为如果没有寻问,他们是不会接收数据的。
client-output-buffer-limit normal 0 0 0
 #对于slave client和MONITER client,如果client-output-buffer一旦超过256mb,又或者超过
64mb持续60秒,那么服务器就会立即断开客户端连接。
client-output-buffer-limit slave 256mb 64mb 60
 #对于pubsub client,如果client-output-buffer一旦超过32mb,又或者超过8mb持续60秒,那么服
务器就会立即断开客户端连接。
client-output-buffer-limit pubsub 32mb 8mb 60
 
#redis执行任务的频率为1s除以hz。
hz 10
 
#在aof重写的时候,如果打开了aof-rewrite-incremental-fsync开关,系统会每32MB执行一次
fsync。这对于把文件写入磁盘是有帮助的,可以避免过大的延迟峰值。
aof-rewrite-incremental-fsync yes


常用功能

事物

中途因为某些原因失败,导致已经执行成功的依然成功,不提供回滚机制。由单线程机制和对命令的组装来实现。
Flushes all previously queued commands in a transaction and restores the connection state to normal.
If WATCH was used, DISCARD unwatches all keys watched by the connection.

multi: 声明事物
exec:开始事物

https://redis.io/docs/latest/commands/mset/
Sets the given keys to their respective values. MSET replaces existing values with new values, just as regular SET. See MSETNX if you don’t want to overwrite existing values.

import redis
 
r = redis.Redis(host='localhost', port=6379, db=0)
# 开启事务
multi = r.multi()
multi.set('name', 'John')
multi.set('age', 25)
multi.set('email', 'john@example.com')
# 执行事务
results = multi.execute()

pipline

import redis
 
r = redis.Redis(host='localhost', port=6379, db=0)
 
pipe = r.pipeline()
pipe.set('name', 'John')
pipe.set('age', 25)
pipe.set('email', 'john@example.com')
results = pipe.execute()

Lua 脚本

package mytools

import (
	"fmt"
	"testing"

	"github.com/go-redis/redis"
)

func TestRedisLua(m *testing.T) {

	// 连接到Redis
	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379", // 你的Redis地址
		Password: "",               // 密码,如果有的话
		DB:       0,                // 使用默认DB
	})

	// 定义Lua脚本
	luaScript := `
	local key = KEYS[1]
	local value = ARGV[1]
	redis.call('SET', key, value)
	return 1
`

	// 创建Lua脚本执行器
	executor := redis.NewScript(luaScript)

	// 执行Lua脚本
	key := "mykey"
	value := "myvalue"
	result, err := executor.Run(rdb, []string{key}, []string{value}).Int()
	if err != nil {
		panic(err)
	}

	fmt.Printf("Lua script executed, result: %d\n", result)

}

悲观锁

watch

执行业务计算逻辑

multi

执行 redis 相关操作

exec 提交事务

Lua 实现

-- Lua脚本实现悲观锁
local key = KEYS[1]
local value = ARGV[1]
local expire_time = ARGV[2]
 
-- 使用GET命令获取当前值,并在此基础上设置新值和过期时间
local current_value = redis.call('GET', key)
if current_value == false then
    -- 键不存在,表示没有其他线程持有锁
    redis.call('SET', key, value)
    redis.call('EXPIRE', key, expire_time)
else
    -- 键存在,表示其他线程已经持有锁
    return 0
end
 
-- 返回1表示获取锁成功
return 1

定时任务/监听键过期事件

利用 redis 中的键值过期提醒,可以实现定时任务的策略,但是不适合海量数据的场景,需要考虑消息通知丢失的问题,需要考虑容忍一定的消息丢失的问题。

  1. config set notify-keyspace-events Ex , 默认事件通知是禁用的,设置为空字符串可以禁用,因为事件通知也会消耗一部分cpu;
  2. 使用 pub/sub 功能订阅事件,命令、事件对应名称
 package main
 
import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
)
 
var ctx = context.Background()
 
func main() {
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379", // Redis地址
        Password: "",               // Redis密码,如果有的话
        DB:       0,                // 使用默认DB
    })
 
    pubsub := rdb.Subscribe(ctx, "__keyevent@0__:expired") // 订阅mychannel频道
    defer pubsub.Close()
 
    for {
        msg, err := pubsub.ReceiveMessage(ctx)
        if err != nil {
            fmt.Println("Receive failed:", err)
            return
        }
        fmt.Printf("Received: %s\n", msg.Payload)
    }
}

常见问题

缓存失效

  • 缓存击穿:热点 key 失效,设置合理的时间,增加监控措施。
  • 缓存穿透:key 不存在,通常是被恶意访问,增加特殊值、同mysql交互的后台限流/限制线程数
  • 雪崩:大量key失效,导致来自不同的请求大量访问到了 mysql,失效时间要随机化,注意限流、熔断策略。
  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值