Redis介绍
早期的互联网服务主要通过MySQL等传统数据库提供服务,随着应用的访问量逐渐增大,数据库的性能瓶颈也越来越明显。这主要是磁盘I/O所导致,磁盘I/O的读写速度相较于内存是非常慢的。如果可以把这些数据存储在内存中,就可以大大增加性能,于是有了redis。Redis是一个开源的,基于内存的存储系统,它可以用作数据库、缓存和消息队列,是当前最热门的NoSQL数据库。
Redis支持多种类型的数据结构,包括5种基本数据类型和5种高级数据类型:
- 字符串(strings)
- 哈希(hashes)
- 列表(lists)
- 集合(sets)
- 有序集合(sorted sets)
5种高级数据类型:
- 消息队列(stream)
- 位图(bitmaps)
- 位域(bitfield)
- 超级日志(hyperloglogs)
- 地理空间索引(geospatial indexes with radius queries)
Redis的核心特性包括:
- 速度快:Redis将所有数据保存在内存中,访问数据的速度非常快,可以支持每秒万级别的读写操作。
- 支持丰富的数据类型:Redis不仅仅支持简单的key-value类型的数据,同时还支持list、set、zset(sorted set)和hash等数据结构的存储。
- 持久化:Redis支持两种持久化方式,RDB(快照)和AOF(日志)方式,可以根据需要选择合适的持久化策略。
- 事务:Redis支持事务,通过MULTI、EXEC、WATCH等命令来实现。
- 高可用和分布式:通过Redis Sentinel来实现高可用的主从复制方案,而Redis Cluster则提供了自动分片的分布式数据库解决方案。
- 发布/订阅:支持发布/订阅消息模式,可以用于消息队列、聊天室等场景。
Redis的常用使用场景包括:
- 缓存系统:利用Redis的高速读写特性,常用作数据库缓存,减轻后端数据库的压力,提高整体访问速度。
- 会话缓存(Session Cache):用于存储用户会话信息,提高网站的访问速度和用户体验。
- 消息队列系统:Redis的发布/订阅模式和列表数据结构可以用来实现消息队列系统,支持消息的发布和订阅。
- 排行榜/计数器:Redis的有序集合(sorted set)非常适合实现排行榜应用,同时它的原子操作也可以用来做各种计数器应用。
- 全页缓存(FPC):对于静态内容的网站,可以利用Redis缓存页面输出,实现快速的全页缓存。
- 社交网络:利用Redis的数据结构和操作,可以方便地实现社交网络中的各种功能,如好友关系、共同好友、消息传递等。
由于Redis是基于内存的存储系统,因此它的数据规模受到物理内存大小的限制。虽然Redis提供了持久化功能,但在极端情况下,如果不恰当地配置或使用,仍有数据丢失的风险。相比基于磁盘的数据库,Redis使用的是内存资源,可能会带来更高的成本。但Redis以其出色的性能和灵活的数据结构支持,已经成为了现代Web应用不可或缺的组件之一。
安装Redis
Redis支持Linux、Mac OS和Windows,官方安装手册提供了详细的安装步骤。
Ubuntu/Debian的安装步骤如下:
1
2
3
4
5
6
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install redis
安装成功后中,在命令行中输入redis-server
并按下回车键即可启动Redis服务器的默认实例(默认配置),通常是监听本机的6379端口,等待来自客户端的连接和命令。如果想要以非默认配置启动Redis服务器,可以创建一个配置文件(通常是.conf
文件),自定义监听的端口号、设置密码(requirepass
指令)、开启AOF或RDB持久化策略等。然后在启动Redis服务器时指定这个配置文件。例如:
1
redis-server /path/to/your/redis.conf
Redis服务器启动后,可以使用Redis客户端工具(redis-cli
)进行交互。外部程序使用 TCP 套接字和 Redis 特定协议与 Redis 进行通信,该协议在不同编程语言的 Redis 客户端库中均有实现(API)。检查Redis是否正常工作的第一件事是使用redis-cli
发送 PING 命令。
1
2
3
4
5
6
7
$ redis-cli ping
PONG
# 或者使用交互模式
$ redis-cli
redis 127.0.0.1:6379> ping
PONG
除了通过CLI和API与redis服务进行交互,redis官方还提供了一个叫做redis-insight的GUI工具,下载链接。
Redis中的数据类型
String
字符串(String)是最基本的数据类型,也是最常用的一种。它可以存储任何形式的字符串,包括文本数据、二进制数据(比如图片或序列化的对象),最大可以存储512MB。
String的基本操作包括:
-
设置值:
SET mykey "Hello"
-
获取值:
GET mykey
-
删除键:
DEL mykey
-
追加值:
APPEND mykey " World"
-
为键设置过期时间:
EXPIRE key seconds
- 同时设置键的值和过期时间(以秒为单位):
SETEX mykey 600 "Hello"
- 查看键还有多少秒过期:
TTL key
,如果键不存在或没有设置过期时间,返回-1
。如果键已经过期,返回-2
。 - 移除键的过期时间,使其变成永久的:
PERSIST key
String的高级功能包括:
-
计数器:Redis的字符串可以用作原子计数器
-
` SET mycounter 100`
-
` INCR mycounter
:加
1,如果
key不存在,则先设置其值为
0` -
` DECR mycounter
:减
1,如果
key不存在,则先设置其值为
0` -
` INCRBY mycounter 10`:加特定的数值
-
DECRBY mycounter 5
:减特定的数值
-
-
范围操作
-
SETRANGE mykey 6 "Redis"
:从mykey
对应的值的第7个字符开始(因为计数是从0开始的),替换为”Redis” -
GETRANGE mykey 0 4
:获取mykey
对应值的第1个字符到第5个字符的子字符串
-
-
位操作
-
SETBIT mykey 7 1
-
GETBIT mykey 7
-
String的应用场景:
- 缓存:由于其高性能,字符串常用作缓存数据,如网页内容、数据库查询结果等。
- 会话存储:在Web应用中,会话信息常常被存储在Redis字符串中。
- 计数器:如网站访问量或社交媒体上的点赞数等。
- 分布式锁:通过SETNX(Set if not exists)命令实现分布式锁的功能。
- 配置存储:存储应用配置信息。
分布式锁
Redis的分布式锁是一种在分布式系统中用来协调多个进程之间访问共享资源的机制。使用Redis来实现分布式锁可以确保在同一时间内,只有一个进程能够获得锁并访问特定的资源。
最简单的Redis分布式锁可以通过SET
命令结合一些选项来实现,例如:
1
SET lock_key random_value NX EX max_lock_time
这个命令的含义是:
lock_key
:锁的键名。random_value
:锁的值,这个值应该是随机生成的,用于在释放锁的时候验证持有者身份。NX
:只有当lock_key
不存在时,才设置这个键。这确保了锁的互斥性。EX
:设置键的过期时间,max_lock_time
是过期时间,单位为秒。这是一个自动释放锁的机制,防止死锁。
当一个进程尝试获取锁时,它会执行上述SET
命令。如果命令返回成功,那么这个进程获得了锁。如果返回失败(因为锁已经被另一个进程持有),进程可以选择等待一段时间后重试。
为了安全释放锁,需要确保只有锁的持有者才能释放它。这可以通过比较lock_key
的值来实现。如果值匹配,持有者可以删除这个键来释放锁。这通常通过一个Lua脚本来原子性地执行:
1
2
3
4
5
if redis.call("get", lock_key) == random_value then
return redis.call("del", lock_key)
else
return 0
end
注意事项:
- 安全性:使用随机值作为锁的值是非常重要的,这样只有锁的持有者才能释放锁。
- 死锁:设置过期时间是为了防止进程在持有锁的状态下崩溃导致的死锁。
- 性能:频繁的锁操作会增加Redis服务器的负载,需要合理设计锁的粒度和持有时间。
List
List是简单的字符串列表,按照插入顺序排序。可以在列表的头部(左侧)或尾部(右侧)添加元素,非常适合用作消息队列、记录日志等场景。
List的基本操作包括:
LPUSH key value1 [value2]
:将一个或多个值插入到列表头部。如果键不存在,会创建一个空列表并执行插入操作。RPUSH key value1 [value2]
:将一个或多个值插入到列表尾部。LPOP key
:移出并获取列表的第一个元素。RPOP key
:移出并获取列表的最后一个元素。LRANGE key start stop
:获取列表指定范围内的元素。LTRIM key start stop
:保留列表指定范围内的元素。LINDEX key index
:通过索引获取列表中的元素。LLEN key
:获取列表长度。LREM key count value
:根据参数count
的值,移除列表中与参数value
相等的元素。
List的应用场景包括:
- 消息队列:利用
LPUSH
/RPUSH
和LPOP
/RPOP
,可以实现简单的消息队列。生产者可以从列表一端插入消息,消费者从另一端取出消息。 - 日志收集:可以将日志信息
RPUSH
到Redis列表,这样日志信息就按照插入顺序存储了。可以定期从列表中LPOP
日志进行处理。 - 数据缓存:列表也可以用来缓存最新的N个数据项。通过
LPUSH
插入新数据,然后使用LTRIM
来保持列表只保留最新的N个元素。 - 实现栈或队列:列表可以用来实现栈(后进先出)或队列(先进先出)。栈可以通过
LPUSH
加入元素,LPOP
移除元素来实现;队列可以通过RPUSH
加入元素,LPOP
移除元素来实现。
注意事项:
- 列表中元素的最大数量是
2^32 - 1
(4294967295个元素)。 - 对于大量数据操作,需要注意性能问题。例如,
LRANGE
命令在处理大范围数据时可能会很慢。 - 当列表变得非常大时,考虑使用其他数据结构如Redis Streams,尤其是在消息队列等场景下。
Set
Set是由字符串组成的无序集合(是的,redis中是无序集合,不要和stl记混了噢)。与List不同,Set中的元素是唯一的,非常适合用来存储没有顺序要求的唯一数据项,例如标签、社交网络中的好友列表等。
Set的基本操作包括:
SADD key member1 [member2]
:向集合添加一个或多个成员,如果成员已经存在于集合中,则忽略。返回值是被添加到集合中的新元素的数量。SREM key member1 [member2]
:移除集合中一个或多个成员,不存在的成员会被忽略。SMEMBERS key
:返回集合中的所有成员。SCARD key
:获取集合的成员数。SISMEMBER key member
:判断成员元素是否是集合的成员。SINTER key1 [key2]
:返回所有给定集合的交集。SUNION key1 [key2]
:返回所有给定集合的并集。SDIFF key1 [key2]
:返回第一个集合与其他集合之间的差集。
Set的应用场景包括:
- 去重:由于集合中的元素必须唯一,这使得set特别适合用来存储需要去重的数据,例如统计网站访问者的IP地址。
- 社交应用:集合可以用来存储用户的好友列表或者群组成员列表,通过集合运算可以方便地实现好友推荐(通过计算两个用户的共同好友(交集))等功能。
- 标签系统:在实现标签系统时,可以使用集合存储每个标签对应的内容ID,也可以存储每个内容对应的标签集合,这样可以方便地实现标签的增加、删除和查询。
- 投票或者点赞功能:集合可以用来实现文章、评论的点赞或者投票功能,每个集合存储点赞或投票的用户ID,可以轻松实现点赞总数统计、判断用户是否已点赞等功能。
Sorted Set
Sorted Set是集合的一种,不仅可以保证集合中元素的唯一性,还能给集合中的每个元素关联一个分数(score),这个分数用来对元素进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。
Sorted Set的基本操作包括:
ZADD key score1 member1 [score2 member2]
:向有序集合添加一个或多个成员,或者更新已存在成员的分数。ZSCORE key member
:获取有序集合中成员的分数值。ZRANGE key start stop [WITHSCORES]
:按照分数值递增(从小到大)顺序返回有序集合中指定区间内的成员。ZREVRANGE key start stop [WITHSCORES]
:按照分数值递减(从大到小)顺序返回有序集合中指定区间内的成员。ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
:返回有序集中,分数值在min和max之间(包括等于min或max)的成员。ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
:返回有序集中,分数值在max和min之间的成员,按分数值递减(从大到小)排序。ZREM key member [member ...]
:移除有序集合中的一个或多个成员。ZCOUNT key min max
:计算在有序集合中指定分数区间的成员数量。ZCARD key
:获取有序集合的成员数。ZINCRBY key increment member
:为有序集合中的成员的分数值加上增量increment。
Sorted Set的应用场景
- 排行榜:有序集合非常适合用来实现各种排行榜,比如游戏分数排行、文章热度排行等。通过分数来进行自动排序,可以轻松地实现排行榜的各种操作。
- 带权重的集合:有序集合中的每个元素都可以关联一个分数,这使得它非常适合用来存储带权重的数据项,比如根据用户的兴趣爱好进行权重排序。
- 时间线:可以使用有序集合来实现社交网络中的时间线功能,将时间戳作为分数,用户发表的内容作为成员,这样可以轻松地按时间顺序获取内容。
- 地理位置信息:通过将地理位置的坐标转换为一个分数来存储在有序集合中,可以实现地理位置的快速排序和检索。
Hash
Hash是一种存储键值对的集合。这与Python中的字典、Java中的HashMap或JavaScript中的对象类似。哈希特别适合用来存储对象或者实现映射表,每个哈希可以存储多达2^32 - 1个键值对。
Hash的基本操作:
HSET key field value
:向哈希表中添加一个键值对。如果field已经存在,它的值将被覆盖。HGET key field
:获取哈希表中指定字段的值。HMGET key field1 [field2]
:获取所有给定字段的值。HGETALL key
:获取哈希表中的所有字段和值。HDEL key field1 [field2]
:删除一个或多个哈希表字段。HEXISTS key field
:查看哈希表key中,指定的字段是否存在。HKEYS key
:获取哈希表中的所有字段名。HVALS key
:获取哈希表中的所有值。HINCRBY key field increment
:为哈希表key中的指定字段的整数值加上增量increment。HINCRBYFLOAT key field increment
:为哈希表key中的指定字段的浮点数值加上增量increment。HLEN key
:获取哈希表中字段的数量。
Hash的应用场景:
- 存储对象:哈希表是存储对象或实体的理想选择。例如,可以使用哈希表存储用户的属性,如姓名、年龄、邮箱等。
- 缓存数据:哈希表可以用来缓存数据,例如,将数据库查询结果缓存起来,以减少数据库的访问次数。
- 计数器:可以利用HINCRBY或HINCRBYFLOAT命令来实现计数器功能,如网站页面的访问次数。
- 会话存储:在Web应用中,可以使用哈希表来存储会话信息。
Stream
Stream是Redis 5.0中引入的一种新的数据类型,可以更好地处理消息流和时间序列数据。Stream可以被视为一个日志类型的数据结构,其中的每个条目都包含一个唯一的序列号(ID)和一组键值对。这使得Stream非常适合用于实现消息队列、事件源、日志记录等场景。
Stream中的基本概念:
- 条目(Entry):Stream中的每条记录,包含一个ID和一组键值对。
- ID:每个条目的唯一标识,通常由Redis自动生成,格式为时间戳-序列号,确保条目的顺序。
- 消费者(Consumer):读取并处理Stream中条目的客户端。
- 消费者组(Consumer Group):允许多个消费者协同处理同一个Stream中的条目,支持消息的负载均衡和消息确认机制。
Stream的基本操作包括:
XADD key ID field string [field string ...]
:向Stream中添加条目。ID可以是*,让Redis自动生成(递增的)。XRANGE key start end [COUNT count]
:获取Stream中指定范围内的条目,按ID升序返回。XREVRANGE key end start [COUNT count]
:与XRANGE相反,按ID降序返回条目。XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
:从一个或多个Stream中读取count个数的条目,如果没有消息就阻塞milliseconds,可以指定开始的ID。XGROUP CREATE key groupname ID [MKSTREAM]
:创建消费者组。XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
:以消费者组的身份读取Stream中的条目。XACK key group ID [ID ...]
:确认条目已被消费组中的消费者成功处理。XDEL key ID [ID ...]
:删除Stream中的一个或多个条目。XTRIM key MAXLEN [~] count
:修剪Stream,只保留最新的N条条目。
Stream的应用场景包括:
- 消息队列:利用Stream实现消息队列,支持发布/订阅、消息持久化、消息确认等特性。
- 事件驱动的应用:作为事件源,记录应用中发生的各种事件,支持后续的事件处理和分析。
- 日志记录:用于记录应用日志,提供日志的顺序存储和查询。
- 时间序列数据:存储时间序列数据,如IoT设备的数据采集、监控数据等。
Geospatial
Redis的地理空间(Geospatial)支持是在Redis 3.2版本中引入的,它允许用户将地理位置信息存储到Redis中,并执行基于地理位置的查询。Geospatial基于地球上的经纬度来存储位置信息。这些功能主要依赖于sorted set,在这个集合中,每个成员都是一个地理位置的名称,而分数(score)则是该位置的经纬度编码值。
Geospatial的基本操作包括:
GEOADD key longitude latitude member [longitude latitude member ...]
:添加一个或多个地理空间位置到指定的key中。GEODIST key member1 member2 [unit]
:计算两个位置之间的距离。单位可以是米(m)、千米(km)、英尺(ft)或英里(mi)。GEOPOS key member [member ...]
:获取一个或多个位置的经纬度。GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
:根据给定的经纬度和半径,查找范围内的位置。GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
:根据指定成员的位置和半径,查找范围内的位置。
Geospatial的应用场景包括:
- 位置存储和查询:可以将地理位置信息(如商店、餐馆、景点等)存储到Redis中,并根据用户的当前位置查询附近的点。
- 距离计算:计算两个地理位置之间的距离,支持不同的单位。
- 范围查询:查找给定范围内的所有地理位置信息,例如查找用户周围10公里内的餐馆。
- 轨迹记录:记录和查询移动对象(如运动员、车辆等)的轨迹。
Hyprloglog
HyperLogLog是Redis用于高效解决基数统计问题的数据结构,通过牺牲一定的精度来极大地节省内存使用,非常适合需要处理大量数据且可以接受一定误差的场景。基数(Cardinality)指的是一个集合中不同元素的数量。例如,一个包含数百万个元素的数据集,可能只有几万个不同的元素。在许多应用场景中,我们需要统计不同元素的数量,如统计网站的独立访客数。传统方法在处理大规模数据时会遇到显著的内存使用问题。HyperLogLog 提供了一种内存使用极少的概率算法,用来估计集合的基数,虽然结果是近似值,但误差率通常在 0.81% 左右。
HyperLogLog的基本命令包括:
PFADD key element [element ...]
:向 HyperLogLog 中添加元素。PFCOUNT key [key ...]
:计算一个或多个 HyperLogLog 的近似基数。PFMERGE destkey sourcekey [sourcekey ...]
:将多个 HyperLogLog 合并为一个。
HyperLogLog的应用场景:
- 独立访客计数(UV):统计网站或应用的独立访客数。
- 数据去重:在大规模数据集中估计不同元素的数量。
- 实时分析:在需要快速响应的系统中,对大量数据进行实时基数统计。
HyperLogLog的优点
- 高效:HyperLogLog 占用极少的内存(约 12KB),可以处理亿级别的数据。
- 快速:添加元素和计算基数的操作都非常快,适合实时数据处理。
- 合并能力:可以将多个 HyperLogLog 合并,便于分布式系统中的数据统计。
注意事项:
- HyperLogLog 提供的是近似值,对于精确度要求极高的场景不适用。
- 在元素数量较少时,误差率可能略高,但随着元素数量的增加,误差率会逐渐稳定。
- HyperLogLog 适用于基数较大的场景,如果需要处理的数据集基数较小,使用传统的 Set 可能更合适。
Bitmap
Redis中的Bitmaps(位图)是一种通过位操作来记录数据的特殊数据结构,实质上是一个字符串(string),但可以将其视为一个由二进制位组成的数组。每个位(bit)可以是0或1,可以单独设置或查询。Bitmaps 提供了一种极为节省空间的方式来存储和操作大量的布尔值。
Bitmap的基本操作包括:
SETBIT key offset value
:在指定的 key 上设置或清除指定偏移量的位(bit),value 只能是 1 或 0。GETBIT key offset
:获取指定 key 的字符串在指定偏移量的位值。BITCOUNT key [start end]
:计算字符串中被设置为 1 的位的数量,可以指定起始和结束位置。BITOP operation destkey key [key ...]
:对一个或多个 Bitmaps 进行 AND、OR、XOR 和 NOT 操作,并将结果存储在 destkey 中。BITPOS key bit [start] [end]
:查找第一个被设置为指定值的位的位置。
Bitmap的应用场景:
- 用户行为跟踪:例如,记录用户每天的登录情况,每个位代表一天,1 表示登录,0 表示未登录。
- 特征标记:用于标记用户或事物的特定特征,如用户的权限、文章的标签等。
- 统计计数:通过BITCOUNT命令快速统计满足特定条件的数量,如一段时间内的活跃用户数。
- 实时数据分析:由于位图操作的高效性,它们非常适合用于需要快速响应的实时数据分析。
Bitmap的优点:
- 空间效率高:Bitmaps 使用非常少的内存就可以存储大量的布尔值。
- 操作高效:对 Bitmaps 的操作通常非常快,适合高性能需求场景。
- 灵活性:通过位操作的组合,可以实现复杂的查询和统计功能。
Bitfield
位域(Bitfields)是一种更高级的方式来操作和存储位级数据。与Bitmaps 类似,位域也是在字符串类型上进行的位操作,但它提供了更复杂和灵活的操作,允许在一个字符串值中定义和操作多个不同大小的位字段。这使得位域成为处理复杂的位级数据结构的强大工具,如打包多个布尔值、枚举或任何需要紧凑存储的小范围整数。
Bitfields的基本命令包括:
-
GET:读取位字段的值。
- SET:设置位字段的值。
- INCRBY:增加位字段的值。
上述操作可以在一个 BITFIELD 命令中组合使用,使得位域操作非常灵活。例如我们要在一个字符串中存储两个字段:一个 4 位的字段和一个 3 位的字段:BITFIELD mykey SET i4 0 15 SET u3 4 4
SET i4 0 15
:在偏移 0 处设置一个 4 位的有符号整数字段为 15。SET u3 4 4
:在偏移 4 处设置一个 3 位的无符号整数字段为 4。
Bitfields的应用场景:
- 紧凑的数据存储:当需要存储大量小范围的数值时,位域可以帮助减少存储空间的需求。
- 复杂的位级操作:位域提供了一种灵活的方式来定义和操作多个位级字段,这在处理复杂的位级数据结构时非常有用。
- 计数器和标志:位域可以用来实现各种计数器或标志集合,特别是当这些计数器或标志需要紧凑存储时。
Redis事务
Redis 的事务功能允许将多个命令打包,然后一次性、顺序地执行。这一系列操作要么全部执行,要么一个也不执行,保证了执行的原子性。不过,需要注意的是,Redis事务的原子性与传统数据库系统中的事务略有不同,特别是在错误处理和隔离级别方面。
Redis事务的流程:
- 开始事务:在Redis中,使用
MULTI
命令来开始一个事务。 - 命令队列:在
MULTI
和EXEC
之间的所有命令都不会立即执行,而是被放入一个队列中。这些命令会在执行EXEC
命令时一次性、顺序地执行。 - 执行事务:使用
EXEC
命令来执行前面队列中的所有命令。如果在调用EXEC
之前连接被关闭或者出现了其他错误,那么这个事务中的所有命令都不会被执行。 - 取消事务:使用
DISCARD
命令来取消事务,放弃执行事务中队列里的所有命令。
Redis 事务中的错误处理分为两类:
-
命令入队错误:如果一个命令在入队时(即在
MULTI
和EXEC
之间)就因为语法错误或其他问题被判断为无法执行,Redis会立即返回错误。这时,可以选择使用DISCARD
命令来取消整个事务。 -
命令执行错误:如果命令成功入队,但在
EXEC
执行时发生错误(如运行时错误),Redis 会尝试执行事务中的其他命令。这意味着事务中的部分命令可能执行成功,而部分命令执行失败。
Redis事务示例
MULTI
INCR foo
INCR bar
EXEC
这个例子中,INCR foo
和 INCR bar
命令被包裹在一个事务中。只有当执行 EXEC
命令时,这两个命令才会被实际执行。
Redis 事务不支持回滚(rollback)。一旦执行了 EXEC
,所有的命令都会被尝试执行,即使某些命令执行失败。Redis 的事务保证了原子性,但并不保证“隔离性”。在执行事务的过程中,其他客户端可以看到事务中间状态。为了解决隔离性问题,可以考虑使用 Lua 脚本来实现更复杂的逻辑,因为 Lua 脚本在 Redis 中以原子方式执行。
Redis持久化
由于Redis是基于内存的数据库系统,如何实现数据持久化显得尤为重要。Redis提供了两种主要的持久化方式:快照(Snapshotting,通过 RDB 文件)和追加文件(Append-Only File,简称 AOF)。这两种方式可以单独使用,也可以同时使用,以达到最佳的数据持久化效果。
RDB
RDB持久化通过创建Redis数据集的内存快照来工作。在Redis的配置文件(/etc/redis/redis.conf
)中可以指定不同的规则来触发快照的创建,例如,每隔M分钟,每当有N个键被改变时。
1
2
3
4
5
6
7
8
9
10
# save ""
#
# Unless specified otherwise, by default Redis will save the DB:
# * After 3600 seconds (an hour) if at least 1 change was performed
# * After 300 seconds (5 minutes) if at least 100 changes were performed
# * After 60 seconds if at least 10000 changes were performed
#
# You can set these explicitly by uncommenting the following line.
#
# save 3600 1 300 100 60 10000
RDB是一个非常紧凑的文件,它包含了某一时刻Redis服务器中所有数据的副本。RDB文件是二进制格式的,因此它们在保存和恢复数据时非常快,这使得RDB成为灾难恢复的理想选择,因为可以将 RDB 文件复制到另一个地方作为备份。
RDB的优点包括:
- 快速的数据恢复。
- 对于大型数据集,RDB可以更高效地保存和加载。
- RDB文件是一个紧凑的单一文件,适合灾难恢复。
RDB的缺点包括:
- 在发生故障时,你可能会丢失最近的数据,因为RDB文件的更新频率可能不够高。
- RDB在保存快照时可能会消耗大量的CPU和内存资源,在大型数据集上尤其如此。
AOF
AOF持久化记录每个写操作命令来工作,这些命令被追加到AOF文件的末尾。这样,当Redis重启时,它可以通过重新执行这些命令来重建原始数据集。相较于二进制格式的RDP,AOF文件的内容是纯文本。AOF提供了几种不同的同步选项,可以在每次写操作后同步,每秒同步一次,或者不同步(也是在配置文件中设置)。这些选项提供了不同级别的耐久性和性能之间的权衡。
AOF 的优点包括:
- 提供了更好的数据安全性,因为你可以配置为更频繁地同步到硬盘。
- 允许更精细的控制持久化过程。
- AOF 文件是自我修复的,即使文件末尾被截断,也不会影响Redis的启动。
AOF 的缺点包括:
- 对于相同的数据集,AOF文件通常比RDB文件大。
- 根据同步频率的不同,AOF可能会比RDB慢。
在实践中,经常同时使用RDB和AOF持久化,以结合两者的优点。可以配置Redis在后台定期创建RDB快照,同时开启AOF来记录每个写操作。这样,在发生故障时,可以使用RDB文件快速恢复大部分数据,然后应用AOF文件中的写操作来恢复最近的数据变更。
Redis主从复制
Redis 的主从复制功能允许多个 Redis 服务器之间进行数据的自动同步。这个过程中,一个 Redis 服务器充当主节点(master),而一个或多个 Redis 服务器充当从节点(slave)。从节点会自动将自己与主节点的数据保持同步。这种机制支持数据的冗余备份、读写分离以及系统的高可用性等场景。
主从复制的工作流程:
- 启动和连接:复制过程开始时,从节点会连接到主节点,并发送一个
SYNC
命令。 - 数据同步:主节点接收到
SYNC
命令后,会开始在后台保存快照(执行 bgsave),同时收集所有接收到的写命令。快照完成后,主节点将快照文件和这期间收集的所有写命令发送给从节点。 - 加载数据:从节点接收到数据后,会加载快照文件,并执行收到的写命令,以此来与主节点的数据状态同步。
- 命令传播:一旦初始的数据同步完成,主节点会继续将所有新的写命令实时发送给从节点,从节点接收并执行这些命令,以保持与主节点的数据一致。
主从复制的特点:
- 自动重连:如果从节点与主节点之间的连接因为某些原因断开,从节点会自动尝试重新连接主节点。
- 断点续传:Redis 2.8 版本及以上支持部分重同步(PSYNC),如果连接断开后重新连接,从节点可以从断开时的位置继续接收数据,而不需要重新同步所有数据。
- 多层复制:从节点也可以被其他 Redis 服务器作为主节点进行连接,形成多层的主从结构。这种结构可以用于构建更复杂的复制和分布式系统架构(每个从节点只有一个主节点)。
- 读写分离:通过主从复制,可以实现读写分离。所有的写操作都在主节点上进行,而读操作可以在一个或多个从节点上进行,以此来分散读操作带来的负载。
配置主从复制:
主从复制的配置相对简单。对于从节点,只需要在配置文件中设置 slaveof
指令,指定主节点的 IP 地址和端口号,或者在运行中通过 SLAVEOF
命令来指定主节点。
例如,在从节点的 redis.conf
文件中添加:
1
slaveof <master-ip> <master-port>
或者在 Redis 命令行中执行:
1
SLAVEOF <master-ip> <master-port>
如果主节点设置了访问密码,从节点还需要通过 masterauth
配置项或 AUTH
命令来提供密码。
主从复制的使用场景
- 数据备份:通过主从复制,可以实现数据的热备份,提高数据的可靠性。
- 故障恢复:当主节点出现故障时,可以快速从从节点提升为新的主节点,以减少服务中断时间。
- 负载均衡:通过将读请求分散到从节点,可以减轻主节点的读负载,提高系统的整体性能。
- 高可用性和灾难恢复:结合哨兵(Sentinel)系统或 Redis Cluster,可以实现更高级的高可用性和灾难恢复方案。
Redis哨兵模式
Redis 哨兵(Sentinel)模式是 Redis 的高可用解决方案之一,它通过使用一个或多个哨兵实例来监控主节点和从节点的运行状态,并在主节点出现故障时自动进行故障转移(failover),选举出新的主节点,同时自动更新其他从节点的配置,使其指向新的主节点。这个过程对客户端来说是透明的,保证了 Redis 服务的高可用性。
哨兵模式的工作原理:
- 监控:哨兵通过发送命令,定期检查主节点和从节点的健康状态。
- 通知:当某个 Redis 实例出现问题时,哨兵可以通过 API 向管理员或其他应用程序发送通知。
- 自动故障转移:当主节点无法正常工作时,哨兵会自动开始故障转移过程,选举出新的主节点。
- 配置提供者:哨兵还可以作为服务发现的角色,客户端可以询问哨兵当前谁是主节点,以及获取主节点的地址和端口信息。
故障转移过程:
- 选举领导者:如果多个哨兵同时监控到主节点失效,它们之间会先进行一次选举,选出一个领导者哨兵来负责这次的故障转移过程。
- 选择新的主节点:领导者哨兵会从现有的从节点中,根据一定的规则(如数据更新的时间、优先级、偏移量等)选举出新的主节点。
- 配置更新:新的主节点被选出后,领导者哨兵会通知其他从节点修改它们的配置,使它们成为新主节点的从节点。
- 通知客户端:最后,哨兵会通知客户端新的主节点的信息,客户端随后可以更新它们的连接配置。
配置哨兵:要配置 Redis 哨兵,需要创建一个哨兵配置文件,文件中至少需要指定要监控的主节点信息和哨兵监听的端口:
1
2
3
4
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 15000
sentinel parallel-syncs mymaster 1
这个配置指定了哨兵监控名为 mymaster
的主节点,该节点的地址是 127.0.0.1:6379
,并且至少需要 2 个哨兵同意才能执行自动故障转移。down-after-milliseconds
指定了主节点视为失效前的超时时间,failover-timeout
是完成故障转移的超时时间,parallel-syncs
是同时重新同步的从节点数量。