![[Redis] redis常见命令和String字符串解析](http://pic.xiahunao.cn/yaotu/[Redis] redis常见命令和String字符串解析)
开篇为什么要先讲“预备知识”很多初学者看到 Redis 命令时会直接陷入“命令太多要背不完”的状态。但实际上Redis 命令背后有几条非常稳定的主线key-value 模型、数据结构、内部编码、单线程执行、命令复杂度和使用场景。学 Redis 不能只学命令怎么敲还要理解命令背后的运行方式。这样后面学习 String、Hash、List、Set、Zset 时才能知道为什么某些命令很快为什么某些命令要谨慎使用以及为什么 Redis 在高并发场景下表现这么好。String 是 Redis 最基础的数据类型但它并不只是“保存一段文本”这么简单。它还能保存数字、JSON、二进制数据并且可以配合过期时间、条件写入、原子计数等能力实现缓存、计数器、Session、验证码限流等常见业务。一, 预备知识全局命令所有数据类型都会用到的 key 操作Redis 有多种数据类型但它们都围绕 key-value 展开。不同类型主要区别在 value而 key 本身有一批通用命令。常见全局命令包括命令作用复习重点KEYS pattern查找符合模式的 key会遍历全部 key时间复杂度O(N)生产环境慎用EXISTS key [key ...]判断 key 是否存在返回存在的 key 数量DEL key [key ...]删除一个或多个 key返回实际删除的 key 数量EXPIRE key seconds设置秒级过期时间缓存场景非常常用TTL key查看 key 剩余过期时间-1表示无过期时间-2表示 key 不存在TYPE key查看 key 的数据类型可返回string、list、hash、set、zset等其中最需要警惕的是KEYS。它看起来很方便可以用通配符搜索 key例如KEYS user:*但它的时间复杂度是O(N)也就是会扫描 Redis 当前库中的所有 key。如果线上 Redis 数据量很大执行KEYS可能导致 Redis 阻塞影响其他请求。因此它更适合开发、测试或数据量很小的场景。EXPIRE和TTL则是缓存设计的基础。一个 key 被设置过期时间后到期会被 Redis 淘汰。使用TTL查看剩余时间时要记住两个特殊值TTL -1key 存在但没有设置过期时间 TTL -2key 不存在数据结构和内部编码Redis 对外简单对内灵活Redis 对外提供的数据结构是我们平时使用的类型例如 String、Hash、List、Set、Zset。但 Redis 内部并不是只用一种固定方式保存这些结构而是会根据数据规模和内容选择不同的内部编码。常见关系如下对外数据结构常见内部编码stringint、embstr、rawhashziplist、hashtablelistziplist、linkedlist、新版本常见quicklistsetintset、hashtablezsetziplist、skiplist可以使用下面的命令查看某个 key 的内部编码OBJECT ENCODING key内部编码的意义在于Redis 可以在不改变外部命令的情况下优化底层实现。比如同样是列表小数据量时可以使用更节省内存的编码数据量变大后再切换到更适合大量元素操作的编码。这对使用者来说是无感的。我们仍然使用相同的 Redis 命令但 Redis 会尽量选择更合适的存储方式。这也是 Redis 兼顾易用性和性能的重要原因之一。单线程模型快但怕慢命令Redis 的命令执行采用单线程模型。这里的单线程主要指命令执行阶段多个客户端可以同时连接 Redis但 Redis 实际处理命令时会把命令排队然后按顺序一个个执行。这带来一个好处单条命令天然具有顺序执行的原子性。比如两个客户端同时执行INCR counter最终counter一定会被加两次不会出现多个线程同时修改同一个变量时的竞争问题。因为 Redis 不会同时执行两条命令。那为什么单线程还能这么快主要原因有三点Redis 数据主要存放在内存中内存访问速度远快于磁盘。Redis 使用非阻塞 I/O 和 I/O 多路复用机制可以高效处理大量客户端连接。单线程避免了线程切换、锁竞争、共享数据同步等额外开销。不过单线程也意味着一个明显风险如果某条命令执行很久后面的所有命令都必须等待。因此使用 Redis 时要特别小心这些情况大量 key 场景下执行KEYS一次MGET/MSET传入过多 key操作大 key使用高复杂度命令处理大量数据可以把 Redis 理解为一个非常快的单窗口服务台。它处理每个请求都很快所以整体吞吐很高但如果某个请求特别耗时后面排队的人都会被卡住。二, String 字符串String 是 Redis 最基础的数据类型String 是 Redis 中最基础、最常用的数据类型。需要注意的是Redis 中所有 key 本身都是字符串而 String 类型说的是 value 也是字符串结构。Redis 的 String 不只可以保存普通文本还可以保存普通字符串例如helloJSON 或 XML 文本整数或浮点数二进制数据例如图片、音频、视频片段一个 String 最大不能超过512 MB。Redis 内部按照二进制安全的方式保存字符串不主动处理字符集编码。也就是说客户端传入什么字节Redis 就保存什么字节。基础读写命令SET、GET、MSET、MGETString 最基础的读写命令是SET和GET。SET name James GET name如果 key 已经存在SET会直接覆盖旧值。这里有一个容易忽略的点SET覆盖旧值时会清除原来的 TTL。例如某个缓存 key 原本 10 分钟后过期如果再次执行普通SET key value它就会变成永不过期除非重新指定过期时间。因此缓存场景更推荐直接写成SET user:1001 {...json...} EX 3600SET还支持几个常用选项写法含义SET key value EX seconds设置值并设置秒级过期时间SET key value PX milliseconds设置值并设置毫秒级过期时间SET key value NXkey 不存在时才设置SET key value XXkey 存在时才设置MGET和MSET用于批量读写MSET k1 v1 k2 v2 k3 v3 MGET k1 k2 k3批量命令的优势是减少网络往返。如果执行 1000 次GET客户端和 Redis 之间要来回通信 1000 次如果使用一次MGET获取 1000 个 key就只需要一次网络请求。但批量不是越大越好。因为 Redis 单线程执行命令一次批量操作太大也会让这条命令执行时间过长从而阻塞后续请求。条件写入SETNX 和 SET NXSETNX的含义是只有 key 不存在时才设置成功。SETNX lock:order:1001 1成功返回1失败返回0。现在更常见的写法是使用SET key value NXSET lock:order:1001 1 NX EX 30这种写法可以同时完成“只有不存在才写入”和“设置过期时间”在很多限流、去重、简单锁场景中都很有用。计数命令String 不只是字符串也能做数字String 可以保存数字因此 Redis 提供了一组计数命令命令作用INCR key数值加 1INCRBY key n数值加 nDECR key数值减 1DECRBY key n数值减 nINCRBYFLOAT key n浮点数加 nn 可以是负数如果 key 不存在Redis 会把它当作0处理。比如INCR article:1001:view如果article:1001:view不存在执行后结果就是1。由于 Redis 命令是单线程顺序执行的INCR这类命令天然适合做高并发计数。它不需要像一些多线程系统那样额外使用 CAS 机制来保证计数正确。但要注意如果 key 对应的 value 不是合法整数或浮点数计数命令会报错。字符串追加和范围操作String 还支持一些偏底层的字符串操作命令作用APPEND key value在原字符串末尾追加内容GETRANGE key start end获取指定范围的子串SETRANGE key offset value从指定偏移位置开始覆盖字符串STRLEN key获取字符串长度例如SET greeting Hello World SETRANGE greeting 6 Redis GET greeting最终结果是Hello Redis这些命令适合处理较短字符串。如果字符串很大范围读取或覆盖也会产生更高的处理成本。String 的内部编码String 的内部编码主要有三种编码使用场景int可以用 8 字节长整型表示的整数embstr小于等于 39 字节的短字符串raw大于 39 字节的长字符串例如SET key 6379 OBJECT ENCODING key可能返回int如果保存的是短字符串例如hello可能使用embstr如果字符串较长则使用raw。这再次说明Redis 对外提供的是统一的 String 类型但内部会根据内容自动选择更合适的编码。三, String 的典型业务场景场景一缓存String 最经典的用法是缓存。常见架构是 Redis 作为缓存层MySQL 等数据库作为存储层。基本流程如下业务请求先查询 Redis。如果 Redis 命中直接返回缓存结果。如果 Redis 未命中再查询数据库。将数据库结果序列化成 JSON写入 Redis 并设置过期时间。返回数据。示例SET user:info:1001 {...json...} EX 3600这种方式可以让热点数据大部分时间从 Redis 返回减少数据库压力。设计缓存 key 时推荐使用有层次的命名方式业务名:对象名:唯一标识:属性例如shop:user_info:1001 shop:user_info:1001:namekey 名要清晰但也不要过长。过长的 key 会增加内存占用和网络传输成本。场景二计数器视频播放量、文章阅读量、点赞数、接口调用次数都可以用 String 的计数命令实现。INCR video:5253:play_count这种计数方式简单、高效、原子性好。真实项目中还需要进一步考虑防刷、异步落库、按时间维度统计、数据持久化等问题。场景三共享 Session在分布式 Web 服务中如果每台服务器各自保存 Session用户请求被负载均衡到另一台服务器时可能会出现登录状态丢失。把 Session 集中存储到 Redis 后所有 Web 服务器都从 Redis 读取和更新 Session就能解决这个问题。常见形式SET session:token:abc123 {...user info...} EX 7200这里的过期时间也很重要。Session 不应该永久存在通常需要设置合理的有效期。场景四手机验证码和简单限流验证码场景可以组合使用SET NX、EXPIRE、INCR和GET。一个常见思路是用SET phone:limit:手机号 1 EX 60 NX创建一分钟发送窗口。如果 key 已存在说明一分钟内已经请求过再用INCR增加请求次数。如果次数超过限制例如超过 5 次则拒绝发送。生成验证码后用SET phone:code:手机号 验证码 EX 300保存 5 分钟。用户提交验证码时用GET取出并比对。示例SET sms:limit:13800138000 1 EX 60 NX INCR sms:limit:13800138000 SET sms:code:13800138000 637928 EX 300这个场景很好地体现了 String 的组合能力它本身很简单但配合过期时间、条件写入和原子计数就能解决不少高频业务问题。四, 复习时最该记住的点Redis 的数据类型是 value 的类型key 本身都是字符串。全局命令作用于 keyEXPIRE、TTL、TYPE是复习重点。KEYS是O(N)命令生产环境要谨慎。Redis 对外是数据结构对内是内部编码内部编码会自动适配数据情况。Redis 单线程执行命令所以单条命令具有顺序执行的原子性。单线程模型很快但怕慢命令大 key、大批量、高复杂度命令都可能阻塞。String 可以保存文本、数字、JSON、二进制数据最大 512 MB。SET会覆盖旧值并清除原 TTL缓存场景要特别注意。MGET/MSET能减少网络开销但批量过大也会造成阻塞。INCR/DECR系列是实现高并发计数器的核心。总结不是记住几个零散命令而是要建立 Redis 的基础理解所有操作都围绕 key-value 展开Redis 对外提供清晰的数据结构对内通过不同编码优化性能并通过单线程模型实现简单而高效的命令执行。理解 String 的基础地位。它既是最简单的数据类型也是很多业务能力的起点。缓存、计数器、共享 Session、验证码限流这些场景本质上都是在使用 String 的存储、过期、条件写入和原子计数能力。