本文目录
上一篇文章以认识 Redis 为主, 写了 Redis 系列的第一篇, 现在开启第二部分的学习, 在本文中, 我们将看到以下内容:
Redis 数据结构 String,Hash,List,Set,SortedSet 及相关操作, 提一下 Redis 在 3.2.0 之后有新增了一种 GEO 的数据类型表示地理位置, 不过本文这种数据结构略过
Redis 其他一些常用命令, 分为 Key 操作与服务器操作
Redis 事务机制
主要以实战为主, 希望通过本文可以让大家掌握 Redis 的基本使用.
本来这篇文章还准备加上 Redis 线程模型分析的, 但是写完发现篇幅实在太长, 就把 Redis 线程模型放到最后一篇中了, 也挺好的, 本文专注于对 Redis 命令的讲解.
另外说一下, 本文讲 Redis 中的数据结构, 但是数据结构本身不在本文的讲解范围内, 如果想知道 Hash,List,Set 等数据结构特点及使用场景, 可以自己查阅数据结构相关资料.
String 数据结构的基本操作
首先说一下数据结构 String, 这是 Redis 中最简单的一种数据结构, 和 MemCache 数据结构是一样的, 即 Key-Value 型的数据, 根据 Redis 官方文档, Value 最大值为 512M.
下面用表格来看一下 String 操作的相关命令:
命令 | 描述 | 用法 |
SET | (1)将字符串值 Value 关联到 Key (2)Key 已关联则覆盖,无视类型 (3)原本 Key 带有生存时间 TTL,那么 TTL 被清除 | SET key value [EX seconds] [PX milliseconds] [NX|XX] |
GET | (1)返回 key 关联的字符串值 (2)Key 不存在返回 nil (3)Key 存储的不是字符串,返回错误,因为 GET 只用于处理字符串 | GET key |
MSET | (1)同时设置一个或多个 Key-Value 键值对 (2)某个给定 Key 已经存在,那么 MSET 新值会覆盖旧值 (3)如果上面的覆盖不是希望的,那么使用 MSETNX 命令,所有 Key 都不存在才会进行覆盖 (4)MSET 是一个原子性操作 ,所有 Key 都会在同一时间被设置,不会存在有些更新有些没更新的情况 | MSET key value [key value ...] |
MGET | (1)返回一个或多个给定 Key 对应的 Value (2)某个 Key 不存在那么这个 Key 返回 nil | MGET key [key ...] |
SETEX | (1)将 Value 关联到 Key (2)设置 Key 生存时间为 seconds,单位为秒 (3)如果 Key 对应的 Value 已经存在,则覆盖旧值 (4)SET 也可以设置失效时间,但是不同在于 SETNX 是一个原子操作,即关联值与设置生存时间同一时间完成 | SETEX key seconds value |
SETNX | (1)将 Key 的值设置为 Value,当且仅当 Key 不存在 (2)若给定的 Key 已经存在,SEXNX 不做任何动作 | SETNX key value |
首先, 演示一下 SET,GET,SETEX 的效果:
图中我们应该能看到 SET,GET,SETNX 几个命令的效果了, 在这之外, 专门提两点:
Redis 的命令不区分大小写
Redis 的 Key 区分大小写
接着我们演示一下 SETEX 命令的效果:
这里顺带介绍了 TIME 命令, 它返回的是当前服务器 Unix 时间戳, 但单位为秒(通常 Unix 时间戳取的时间为毫秒). 看到设置 Redis-Expire 这个 Key, 马上获取不失效, 第 26 秒获取的时候失效, 关于失效, Redis 的策略是这样的:
被动触发, 即 GET 的时候检查一下 Key 是否失效
主动触发, 后台每 1 秒跑 10 次定时任务(通过 redis.conf 的 hz 参数配置, 默认为 10, 这个上文没有写), 随机选择 100 个设置了过期时间的 Key, 对过期的 Key 进行失效
最后看一下 MGET 和 MSET 命令:
看到可以同时设置多个 Key-Value, 也可以同时获取多个 Key 对应的 Value, 再次注意, Redis 的 Key 是严格区分大小写的.
特殊的 String 操作: INCR/DECR
前面介绍的是基本的 Key-Value 操作, 下面介绍一种特殊的 Key-Value 操作即 INCR/DECR, 可以利用 Redis 自动帮助我们对一个 Key 对应的 Value 进行加减, 用表格看一下相关命令:
命令 | 描述 | 用法 |
INCR | (1)Key 中存储的数字值 + 1,返回增加之后的值 (2)Key 不存在,那么 Key 的值被初始化为 0 再执行 INCR (3)如果值包含错误类型或者字符串不能被表示为数字,那么返回错误 (4)值限制在 64 位有符号数字表示之内,即 - 9223372036854775808~9223372036854775807 | INCR key |
DECR | (1)Key 中存储的数字值 - 1 (2)其余同 INCR | DECR key |
INCRBY | (1)将 key 所存储的值加上增量返回增加之后的值 (2)其余同 INCR | INCRBY key increment |
DECRBY | (1)将 key 所存储的值减去减量 decrement (2)其余同 INCR | DECRBY key decrement |
下面实际看一下四个命令相关使用:
INCR/DECR 在实际工作中还是非常管用的, 举两个例子:
原先单机环境中统计在线人数, 变成分布式部署之后可以使用 INCR/DECR
由于 Redis 本身极高的读写性能, 一些秒杀的场景库存增减可以基于 Redis 来做而不是直接操作 DB
Hash 数据结构相关操作
接着讲一下 Hash,Hash 本质上和 String 是一样的, 无非 String 是纯粹的 Key-Value,Hash 是外面套了一层东西, 里面还是 Key-Value, 接着我们用表格看一下 Hash 数据结构的相关命令:
命令 | 描述 | 用法 |
HSET | (1)将哈希表 Key 中的域 field 的值设为 value (2)key 不存在,一个新的 Hash 表被创建 (3)field 已经存在,旧的值被覆盖 | HSET key field value |
HGET | (1)返回哈希表 key 中给定域 field 的值 | HGET key field |
HDEL | (1)删除哈希表 key 中的一个或多个指定域 (2)不存在的域将被忽略 | HDEL key filed [field ...] |
HEXISTS | (1)查看哈希表 key 中,给定域 field 是否存在,存在返回 1,不存在返回 0 | HEXISTS key field |
HGETALL | (1)返回哈希表 key 中,所有的域和值 & nbsp; | HGETALL key |
HINCRBY | (1)为哈希表 key 中的域 field 加上增量 increment (2)其余同 INCR 命令 | HINCRYBY key filed increment |
HKEYS | (1)返回哈希表 key 中的所有域 & nbsp; | HKEYS key |
HLEN | (1)返回哈希表 key 中域的数量 & nbsp; | HLEN key |
HMGET | (1)返回哈希表 key 中,一个或多个给定域的值 (2)如果给定的域不存在于哈希表,那么返回一个 nil 值 | HMGET key field [field ...] |
HMSET | (1)同时将多个 field-value 对设置到哈希表 key 中 (2)会覆盖哈希表中已存在的域 (3)key 不存在,那么一个空哈希表会被创建并执行 HMSET 操作 | HMSET key field value [field value ...] |
HVALS | (1)返回哈希表 key 中所有的域和值 | HVALS key |
同样的, 实际看一下这些命令的相关使用:
稍乱, 但是除了 HMSET,HMGET 以外把所有命令都演示到了.
List 数据结构相关操作
接着我们看一下 Redis 中的 List, 相关命令有:
命令 | 描述 | 用法 |
LPUSH | (1)将一个或多个值 value 插入到列表 key 的表头 (2)如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入表头 (3)key 不存在,一个空列表会被创建并执行 LPUSH 操作 (4)key 存在但不是列表类型,返回错误 | LPUSH key value [value ...] |
LPUSHX | (1)将值 value 插入到列表 key 的表头,当且晋档 key 存在且为一个列表 (2)key 不存在时,LPUSHX 命令什么都不做 | LPUSHX key value |
LPOP | (1)移除并返回列表 key 的头元素 | LPOP key |
LRANGE | (1)返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定 (2)start 和 stop 都以 0 位底 (3)可使用负数下标,-1 表示列表最后一个元素,-2 表示列表倒数第二个元素,以此类推 (4)start 大于列表最大下标,返回空列表 (5)stop 大于列表最大下标,stop = 列表最大下标 | LRANGE key start stop |
LREM | (1)根据 count 的值,移除列表中与 value 相等的元素 (2)count>0 表示从头到尾搜索,移除与 value 相等的元素,数量为 count (3)count<0 表示从从尾到头搜索,移除与 value 相等的元素,数量为 count (4)count=0 表示移除表中所有与 value 相等的元素 | LREM key count value |
LSET | (1)将列表 key 下标为 index 的元素值设为 value (2)index 参数超出范围,或对一个空列表进行 LSET 时,返回错误 | LSET key index value |
LINDEX | (1)返回列表 key 中,下标为 index 的元素 | LINDEX key index |
LINSERT | (1)将值 value 插入列表 key 中,位于 pivot 前面或者后面 (2)pivot 不存在于列表 key 时,不执行任何操作 (3)key 不存在,不执行任何操作 | LINSERT key BEFORE|AFTER pivot value |
LLEN | (1)返回列表 key 的长度 (2)key 不存在,返回 0 | LLEN key |
LTRIM | (1)对一个列表进行修剪,让列表只返回指定区间内的元素,不存在指定区间内的都将被移除 | LTRIM key start stop |
RPOP | (1)移除并返回列表 key 的尾元素 | RPOP key |
RPOPLPUSH | 在一个原子时间内,执行两个动作: (1)将列表 source 中最后一个元素弹出并返回给客户端 (2)将 source 弹出的元素插入到列表 desination,作为 destination 列表的头元素 | RPOPLPUSH source destination |
RPUSH | (1)将一个或多个值 value 插入到列表 key 的表尾 | RPUSH key value [value ...] |
RPUSHX | (1)将 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表 (2)key 不存在,RPUSHX 什么都不做 | RPUSHX key value |
接着看一下这些命令的实际使用效果:
工具所限, LSET,LINSERT,RPOPLPUSH 几个命令没法演示, 上面演示了其他的基本命令, 应该足以理解 Redis 的 List 了, 操作 List 千万注意区分 LPUSH,RPUSH 两个命令, 把数据添加到表头和把数据添加到表尾是完全不一样的两种结果.
另外 List 还有 BLPOP,BRPOP,BRPOPLPUSH 三个命令没有说, 它们是几个 POP 的阻塞版本, 即没有数据可以弹出的时候将阻塞客户端直到超时或者发现有可以弹出的元素为止.
SET 数据结构相关操作
接着我们看一下 SET 数据结构的相关操作:
命令 | 描述 | 用法 |
SADD | (1)将一个或多个 member 元素加入到 key 中,已存在在集合的 member 将被忽略 (2)假如 key 不存在,则只创建一个只包含 member 元素做成员的集合 (3)当 key 不是集合类型时,将返回一个错误 & nbsp; | SADD key number [member ...] |
SCARD | (1)返回 key 对应的集合中的元素数量 | SCARD key |
SDIFF | (1)返回一个集合的全部成员,该集合是第一个 Key 对应的集合和后面 key 对应的集合的差集 | SDIFF key [key ...] |
SDIFFSTORE | (1)和 SDIFF 类似,但结果保存到 destination 集合而不是简单返回结果集 (2) destination 如果已存在,则覆盖 | SDIFFSTORE destionation key [key ...] |
SINTER | (1)返回一个集合的全部成员,该集合是所有给定集合的交集 (2)不存在的 key 被视为空集 | SINTER key [key ...] |
SINTERSTORE | (1)和 SINTER 类似,但结果保存早 destination 集合而不是简单返回结果集 (2)如果 destination 已存在,则覆盖 (3)destination 可以是 key 本身 | SINTERSTORE destination key [key ...] |
SISMEMBER | (1)判断 member 元素是否 key 的成员,0 表示不是,1 表示是 & nbsp; | SISMEMBER key member |
SMEMBERS | (1)返回集合 key 中的所有成员 (2)不存在的 key 被视为空集 | SMEMBERS key |
SMOVE | (1)原子性地将 member 元素从 source 集合移动到 destination 集合 (2)source 集合中不包含 member 元素,SMOVE 命令不执行任何操作,仅返回 0 (3)destination 中已包含 member 元素,SMOVE 命令只是简单做 source 集合的 member 元素移除 | SMOVE source desination member |
SPOP | (1)移除并返回集合中的一个随机元素,如果 count 不指定那么随机返回一个随机元素 (2)count 为正数且小于集合元素数量,那么返回一个 count 个元素的数组且数组中的 < span style="color: #ff0000;" ztid="560" ow="84" oh="17"> 元素各不相同 (3)count 为正数且大于等于集合元素数量,那么返回整个集合 (4)count 为负数那么命令返回一个数组,数组中的 < span style="color: #ff0000;" ztid="564" ow="112" oh="17"> 元素可能重复多次 ,数量为 count 的绝对值 | SPOP key [count] |
SRANDMEMBER | (1)如果 count 不指定,那么返回集合中的一个随机元素 (2)count 同上 | SRANDMEMBER key [count] |
SREM | (1)移除集合 key 中的一个或多个 member 元素,不存在的 member 将被忽略 | SREM key member [member ...] |
SUNION | (1)返回一个集合的全部成员,该集合是所有给定集合的并集 (2)不存在的 key 被视为空集 | SUNION key [key ...] |
SUNIONSTORE | (1)类似 SUNION,但结果保存到 destination 集合而不是简单返回结果集 (2)destination 已存在,覆盖旧值 (3)destination 可以是 key 本身 | SUNION destination key [key ...] |
同样, 实际测试一下 Set:
除了 SINTER 没有用到, 其他应该比较全面地展示了 Set 的相关使用.
SortedSet 数据结构相关操作
数据结构最后说一下 SortedSet 相关操作, 最近有一个场景需要实现 Redis 分页 + 高效移除数据, 一下子没找到好的数据结构, 后来想起了 SortedSet 才解决了问题, 看来积累与储备还是非常有用的,
SortedSet 顾名思义, 即有序的 Set, 看下相关命令:
命令 | 描述 | 用法 |
ZADD | (1)将一个或多个 member 元素及其 score 值加入有序集 key 中 (2)如果 member 已经是有序集的成员,那么更新 member 对应的 score 并重新插入 member 保证 member 在正确的位置上 (3)score 可以是整数值或双精度浮点数 | ZADD key score member [[score member] [score member] ...] |
ZCARD | (1)返回有序集 key 的元素个数 | ZCARD key |
ZCOUNT | (1) 返回有序集 key 中,score 值 & gt;=min 且 & lt;=max 的成员的数量 | ZCOUNT key min max |
ZRANGE | (1)返回有序集 key 中指定区间内的成员,成员位置按 score 从小到大排序 (2)具有相同 score 值的成员按字典序排列 (3)需要成员按 score 从大到小排列,使用 ZREVRANGE 命令 (4)下标参数 start 和 stop 都以 0 为底,也可以用负数,-1 表示最后一个成员,-2 表示倒数第二个成员 (5)可通过 WITHSCORES 选项让成员和它的 score 值一并返回 | ZRANGE key start stop [WITHSCORES] |
ZRANK | (1)返回有序集 key 中成员 member 的排名,有序集成员按 score 值从小到大排列 (2)排名以 0 为底,即 score 最小的成员排名为 0 (3)ZREVRANK 命令可将成员按 score 值从大到小排名 | ZRANK key number |
ZREM | (1)移除有序集 key 中的一个或多个成员,不存在的成员将被忽略 (2)当 key 存在但不是有序集时,返回错误 & nbsp; | ZREM key member [member ...] |
ZREMRANGEBYRANK | (1)移除有序集 key 中指定排名区间内的所有成员 & nbsp; | ZREMRANGEBYRANK key start stop |
ZREMRANGEBYSCORE | (1)移除有序集 key 中,所有 score 值 & gt;=min 且 & lt;=max 之间的成员 & nbsp; | ZREMRANGEBYSCORE key min max |
还有若干不是很常用的命令没有写, 就略过了, 有兴趣的可以自己看一下, 接着看一下 SortedSet 实际使用:
这个地方排名的时候稍微注意下, 和我们认为的排名有些微区别, 比如 1 1 2 3, 由于有两个 1, 因此 3 正序的 Rank 应当为 2(以 0 为下标), 但实际上会是 3, 所以 Rank 应当理解为元素在集合中的下标位置更加准确.
Redis 的 Key 相关操作
写完了 Redis 的数据结构, 接着我们看下 Redis 的 Key 相关操作:
命令 | 描述 | 用法 |
DEL | (1)删除给定的一个或多个 key (2)不存在的 Key 将被忽略 | DEL key [key ...] |
EXISTS | (1)检查给定 key 是否存在 | EXISTS key |
EXPIRE | (1)为给定 key 设置生存时间,key 过期时它会被自动删除 (2)对一个已经指定生存时间的 Key 设置执行 EXPIRE,新的值会代替旧的值 | EXPIRE key seconds |
EXPIREAT | (1)同 EXPIRE,但此命令指定的是 UNIX 时间戳,单位为秒 | EXPIRE key timestamp |
KEYS | (1)查找所有符合给定模式 pattern 的 key,下面举一下例子 (2)KEYS * 匹配所有 key (3)KEYS h?llo 匹配 hello、hallo、hxllo 等 (4)KEYS h*llo 匹配 hllo、heeeeello 等 (5)KEYS h[ae]llo 匹配 hello 和 hallo (6)特殊符号想当做查找内容经的使用 \ | KEYS pattern |
MIGRATE | (1)原子性地将 key 从当前实例传送到目标实例指定的数据库上 (2)原数据库 Key 删除,新数据库 Key 增加 (3)阻塞进行迁移的两个实例,直到迁移成功、迁移失败、等待超时三个之一发生 | MIGRATE host port key destination-db timeout [COPY] [REPLACE] |
MOVE | (1)将当前数据库的 key 移动到给定数据库的 db 中 (2)执行成功的条件为当前数据库有 key,给定数据库没有 key | MOVE key db |
PERSIST | (1)移除给定 key 的生存时间,将 key 变为持久的 | PERSIST key |
RANDOMKEY | (1)从当前数据库随机返回且不删除一个 key, | RANDOMKEY |
RENAME | (1)将 key 改名为 newkey (2)当 key 和 newkey 相同或 key 不存在,报错 (3)newkey 已存在,RENAME 将覆盖旧值 | RENAME key newkey |
TTL | (1)以秒为单位,返回给定的 key 剩余生存时间 | TTL key |
PTTL | (1)以毫秒为单位,返回给定的 key 剩余生存时间 | PTTL key |
TYPE | (1)返回 key 锁存储的值的类型 | TYPE key |
简单看一下实际使用:
这里特别注意 KEYS 命令, 虽然 KEYS 命令速度非常快, 但是当 Redis 中百万, 千万甚至过亿数据的时候, 扫描所有 Redis 的 Key, 速度仍然会下降, 由于 Redis 是单线程模型, 这将导致后面的命令阻塞直到 KEYS 命令执行完.
因此当 Redis 中存储的数据达到了一定量级 (经验值从 10W 开始就值得注意了) 的时候, 必须警惕 KEYS 造成 Redis 整体性能下降.
系统相关命令
接着介绍一下部分系统相关命令:
命令 | 描述 | 用法 |
BGREWRITEAOF | (1)手动触发 AOF 重写操作,用于减小 AOF 文件体积 | BGREWRITEAOF |
BGSAVE | (1)后台异步保存当前数据库的数据到磁盘 | BGSAVE |
CLIENT KILL | (1)关闭地址为 ip:port 的客户端 (2)由于 Redis 为单线程设计,因此当当前命令执行完之后才会关闭客户端 | CLIENT KILL ip:port |
CLIENT LIST | (1)以可读的格式,返回所有连接到服务器的客户端信息和统计数据 | CLIENT LIST |
CONFIG GET | (1)取得运行中的 Redis 服务器配置参数 (2)支持 * | CONFIG GET parameter |
CONFIG RESETSTAT | (1)重置 INFO 命令中的某些统计数据,例如 Keyspace hits、Keyspace misses 等 | CONFIG RESETSTAT |
CONFIG REWRITE | (1)对 < span style="color: #ff0000;" ztid="817" ow="205" oh="17"> 启动 Redis 时指定的 redis.conf 文件进行改写 | CONFIG REWRITE |
CONFIG SET | (1)动态调整 Redis 服务器的配置而无需重启 (2)修改后的配置 < span style="color: #ff0000;" ztid="825" ow="56" oh="17"> 立即生效 | CONFIG SET parameter value |
SELECT | (1)切换到指定数据库,数据库索引 index 用数字指定,以 0 作为起始索引值 (2)默认使用 0 号数据库 | SELECT index |
DBSIZE | (1)返回当前数据库的 Key 的数量 | DBSIZE |
DEBUG OBJECT | (1)这是一个调试命令,不应当被客户端使用 (2)key 存在时返回有关信息,key 不存在时返回错误 | DEBUG OBJECT key |
FLUSHALL | (1)清空整个 Redis 服务器的数据 | FLUSHALL |
FLUSHDB | (1)清空当前数据库中的所有数据 | FLUSHDB |
INFO | (1)以一种易于解释且易于阅读的格式,返回 Redis 服务器的各种信息和统计数值 (2)通过给定可选参数 section,可以让命令只返回某一部分信息 | INFO [section] |
LASTSAVE | (1)返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示 | LASTSAVE |
MONITOR | (1)实时打印出 Redis 服务器接收到的命令,调试用 | MONITOR |
SHUTDOWN | (1)停止所有客户端 (2)如果至少有一个保存点在等待,执行 SAVE 命令 (3)如果 AOF 选项被打开,更新 AOF 文件 (4)关闭 Redis 服务器 | SHUTDOWN [SAVE|NOSAVE] |
看下命令的使用演示:
SELECT 命令忘了, 想起来的时候数据库已经清空了就算了, 使用 SELECT 后控制台会变成 "127.0.0.1:6379[3]>", 即带上数据库的 index.
Redis 的事务
最后, 本文简单说一下 Redis 的事务机制, 首先 Redis 的事务是由 DISCARD,EXEC,MULTI,UNWATCH,WATCH 五个命令来保证的:
命令 | 描述 | 用法 |
DISCARD | (1)取消事务 (2)如果正在使用 WATCH 命令监视某个 / 某些 key,那么取消所有监视,等同于执行 UNWATCH | DISCARD |
EXEC | (1)执行所有事务块内的命令 (2)如果某个 / 某些 key 正处于 WATCH 命令监视之下且事务块中有和这个 / 这些 key 相关的命令,那么 < span style="color: #ff0000;" ztid="906" ow="612" oh="38">EXEC 命令只在这个 / 这些 key 没有被其他命令改动的情况下才会执行并生效 ,否则该事务被打断 | EXEC |
MULTI | (1)标记一个事务块的开始 (2)事务块内的多条命令会按照先后顺序被放入一个队列中,最后 < strong ztid="914" ow="166" oh="17"> 由 EXEC 命令原子性地执行 | MULTI |
UNWATCH | (1)取消 WATCH 命令对所有 key 的监视 (2)如果 WATCH 之后,EXEC/DISCARD 命令先被执行了,UNWATCH 命令就没必要执行了 | UNWATCH |
WATCH | (1)监视一个 / 多个 key,如果在事务执行之前这个 / 这些 key 被其他命令改动,那么事务将被打断 | WATCH key [key ...] |
首先我们看一下事务没有被打断的情况:
看到开启事务之后, 所有的命令返回的都是 QUEUED, 即放入队列, 而不是直接执行.
接着模拟一下事务被打断的情况, WATCH 一下 Number 这个 Key, 我另外起了一个 Redis 客户端 INCR 了一下 Number, 结果为:
看到, 并没有命令被执行, 返回 nil 即事务被打断.
接着简单说一下事务, 和数据库类似的, 事务保证的是两点:
隔离, 所有命令序列化, 按顺序执行, 事务执行过程中不会被其他客户端发来的命令打断
原子性, 事务中的命令要么全部执行, 要么全部不执行
另外, Redis 的事务并不支持回滚, 这个其实网上已经说法挺多了, 大致上是两个原因:
Redis 命令只会因为语法而失败(且这些问题不能再入队时被发现), 或是命令用在了错误类型的键上面, 也就是说, 从实用性角度来说, 失败的命令是由于编程错误造成的, 而这些错误应该在开发的过程中被发现而不应该出现在生产环境中
Redis 内部可以保持简单且快速, 因为不需要对回滚进行支持
总而言之, 对 Redis 来说, 回滚无法解决编程错误带来的问题, 因此还不如更简单, 更快速地无回滚处理事务.
下期预告
最后预告一下最后一篇文章会写的内容, 四部分:
Redis 线程模型
Redis 的 RDB
Redis 的 AOF
Redis 的集群方式
喜欢的朋友可以关注一下最后一篇文章.
来源: https://www.cnblogs.com/xrq730/p/8944539.html