ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Redis入门指南:五大数据结构、持久化机制与高频应用场景

Redis入门指南:五大数据结构、持久化机制与高频应用场景 如果你在技术社区里问“Redis是什么”十个人里至少有七个会回答“一个缓存工具。”这句话不能说错但多少有点可惜——Redis在我的实际使用里更像一台跑在内存里的键值数据库缓存只是它最出名的应用之一。这篇内容写给刚准备接触 Redis、或者学了一阵子但概念还很零散的同学。我会从“基本特性”这个角度切入把底层设计、五种常用数据类型、持久化机制以及缓存治理、分布式锁这些高频场景串一遍争取让你在读完以后脑子里能浮现一张完整的 Redis 地图而不是一堆孤立的名词。你不需要有很深的分布式基础只要用过命令行、见过 MySQL就够了。1. 先认识Redis它不只是一个“缓存工具”1.1 内存里的键值数据库到底意味着什么Redis 的全称是 Remote Dictionary Server翻译过来是“远程字典服务”。这名字挺直白它把数据以 key-value键值对的形式保存在内存里客户端通过网络连接远程读写这些键值。你可以把内存想象成超市门口一排排的临时储物柜顾客把东西存进去报一个号码就能立刻取出来。Redis 就是这样一个储物柜系统只不过它存的不是行李而是各种业务数据。由于数据放在内存而不是磁盘上读写的速度几乎是纳秒、微秒级别的传统关系型数据库一次随机磁盘读往往要花几十毫秒两者差了不止一个数量级。但“快”不是 Redis 唯一的标签。一个合格的老手在介绍 Redis 时通常会列出下面这些基本特性内存存储与高速读写延迟极低键值模型理论上支持海量 key丰富的数据结构包括 String、Hash、List、Set、ZSet 等支持过期时间天然适合做缓存提供 RDB、AOF 两种持久化方案具备单线程模型下的原子操作可用于分布式锁、计数器支持主从复制、哨兵、集群方便搭建高可用架构。这串特性放在一起才是 Redis 的全貌。1.2 初识阶段必须记住的几个基本特性如果你是初学者不用逼自己一次记住所有细节但要先建立几个关键印象。第一个印象是“内存”。Redis 的数据默认全部放在内存里所以它很快但也意味着内存大小就是数据规模的天花板。你不能指望一台 2G 内存的机器装下几十 GB 数据因此 Redis 更适合放“热数据”也就是被高频访问的那部分数据。第二个印象是“结构化”。Redis 不只是缓存一个字符串它提供 Hash、List、Set、ZSet 等结构这让它能做排行榜、消息队列、抽奖系统、共同关注等事情。很多人以为 Redis 只能SET name tom再GET name这是最低配的用法。第三个印象是“可靠性”。虽然 Redis 快但它也有持久化机制可以把内存数据定期保存到磁盘或者把写操作追加到日志里。进程重启后数据还能找回来。这个特性在初学阶段很容易被忽略但生产环境几乎离不开。第四个印象是“分布式能力”。Redis 可以主从复制也可以搭哨兵集群实现自动故障转移还能通过集群分片扩展容量。这部分偏进阶但你要知道 Redis 并不是只能单机运行。1.3 和关系型数据库的分工边界很多新手会有个疑问既然 Redis 快能不能用它替代 MySQL我的答案是不能。MySQL 擅长的是持久化存储、事务一致性、复杂 SQL 查询和关联分析Redis 擅长的是极速读写、原子操作和灵活的数据结构。二者不是替代关系而是配合关系。最典型的架构是“MySQL 负责落全量数据Redis 负责扛热点流量”。比如一个商品详情页数据库里存商品原始信息Redis 里缓存序列化后的详情请求来了先查 Redis命中就直接返回没命中再去 MySQL 加载然后回填 Redis降低数据库压力。所以你可以把 Redis 理解成一层“加速带”而不是“第二个数据库”。初识阶段把这个边界画清楚后面学缓存治理、分布式锁的时候思路会顺很多。2. Redis为什么能这么快三个底层设计拆给你看2.1 数据全在内存把磁盘搬到家门口Redis 快的第一个原因最简单也最容易被忽视它把数据直接放在内存里。磁盘随机读写的延迟大概是 10 毫秒左右而内存随机访问的延迟在 100 纳秒量级。也就是说内存读写比磁盘快大约十万倍。当然实际业务里还要算上网络传输、命令解析、序列化等等但 Redis 单实例每秒处理几万到十几万次读操作在入门场景下已经很常见。不过这个设计也带来资源限制内存比磁盘贵得多。一台机器能装的 Redis 数据量取决于机器内存大小。所以在业务设计上你需要给 key 设置过期时间或者开启淘汰策略比如maxmemory-policy allkeys-lfu避免让 Redis 存着大量永远没人访问的“垃圾数据”把内存吃光。这一点从初识阶段就得记住。2.2 单线程模型没有锁竞争的执行效率Redis 处理命令的执行业务逻辑是单线程的。很多人第一次听到“单线程”都会疑惑单线程怎么会快CPU 不是多核吗这里的关键在于Redis 的瓶颈通常不在 CPU而在内存和网络。成熟的 C 代码在单线程下执行命令本身非常快线程切换和并发锁竞争反而会成为额外开销。单线程模型让 Redis 省去了锁竞争、线程切换、多线程导致的 cache miss 等问题命令执行顺序也变得可预测每条命令天然原子不需要额外加锁。更准确地说Redis 6.0 之后虽然引入了多线程但多线程主要处理网络读写的协议解析真正执行命令的核心流程仍然保持单线程。所以“单线程模型”这个说法在命令执行层面依然成立。但单线程也意味着“一个慢命令阻塞所有命令”。比如在几百万 key 的实例里执行KEYS *会让整个 Redis 卡住生产环境几乎把它当禁忌命令。需要遍历 key 时应该用SCAN命令配合游标分批扫。2.3 IO多路复用用一位服务员接待所有客人第三个底层原因是网络模型。Redis 自己实现了基于事件驱动的事件循环用操作系统的多路复用机制Linux 上就是 epoll同时监视大量客户端连接。你可以想象一家小饭馆只有一位服务员他不是同时扑到每张桌子前而是手里拿着本子记住哪桌菜好了、哪桌要加茶然后轮着处理。Redis 的主线程就是这位服务员网络请求来了先靠事件系统“倾听”哪个连接可读、可写再触发对应的处理逻辑。这样一来几千上万个连接不需要每个都开一个线程系统资源消耗自然低。纯内存操作加上事件驱动让 Redis 可以承受非常高的并发连接。初学阶段不需要把 epoll 源码啃透但理解“单线程 多路复用”是为了让内存极速没有被网络拖累这一点对你判断 Redis 的性能边界很有帮助。3. 五种基础数据结构命令与场景的入门地图3.1 String缓存、计数器和分布式锁的起点String 是 Redis 最基础的数据类型value 可以是字符串、数字甚至二进制数据最大能存 512MB。它太常用以至于很多初学者觉得 String 就是“Redis 的全部”。先用命令感受一下SET user:1 {name:tom,age:18} GET user:1 SETEX verify_code:137 300 8888 INCR page_view SET lock:order:123 unique_value NX PX 30000第一句是典型的缓存对象把 JSON 字符串塞进 Redis。SETEX是“设置值并指定过期时间”的便捷命令非常适合验证码、临时 token 这类有时效的数据。INCR做自增计数后面讲计数器时会展开。SET ... NX PX则是在「key 不存在」时才设置并带上过期时间这是实现 Redis 分布式锁的雏形。3.2 Hash对象数据的天然容器Hash 在 Redis 里像一个微缩的表格或者说是“一个 key 下面挂一串 field-value”。比如缓存用户信息时你可以把user:1作为 keyname、age、city作为字段HSET user:1 name tom age 18 city beijing HGET user:1 name HINCRBY user:1 age 1 HGETALL user:1对比直接用 String 存 JSON 整对象Hash 最大的优势是能单独修改某个字段而不需要把整个 JSON 取出来、反序列化、改完再塞回去。比如用户年龄变化一条HINCRBY user:1 age 1就完成了代码干净性能也更好。需要注意HGETALL会把一个 key 下所有字段都取出来如果字段非常多开销会很大。真实项目里可以把大 Hash 拆成多个小 Hash或者用HSCAN分批取避免阻塞 Redis。3.3 List消息排队与最新列表List 是双向链表结构可以从左或者右插入、弹出元素。典型命令LPUSH news:list {id:1001} LRANGE news:list 0 9 LPOP news:list比较常见的用法有两个。第一个是“最新列表”比如最新文章、最新通知每次有新数据就LPUSH到左侧再用LTRIM只保留前 N 条相当于自动裁剪老数据。第二个是“简单消息队列”生产者LPUSH消费者RPOP任务先进先出。如果需要阻塞式消费可以用BRPOP让消费者在没有消息时等待而不是不断轮询。但要特别提醒List 实现的消息队列没有消费确认机制消费者处理到一半挂了消息就可能丢。初学阶段可以拿它练习生产环境需要可靠投递的话优先考虑 Redis 5.0 引入的 Stream 类型或者单独引入专业消息队列。3.4 Set去重、交集和随机抽奖Set 是“无序、自动去重”的集合。常见命令SADD tag:java java基础 SADD tag:java jvm SISMEMBER tag:java jvm SMEMBERS tag:java SINTER tag:java tag:spring SPOP tag:javaSet 很擅长做“集合运算”。比如一个用户关注了多个话题另一个用户也关注了多个话题用SINTER就能直接算出共同关注比在数据库里用 JOIN 简单得多。再比如抽奖活动把所有参与者SADD进集合SPOP随机弹出一个成员并移出集合天然支持“抽完不重复”。SMEMBERS取所有成员时如果集合特别大也会阻塞 Redis生产环境推荐用SSCAN分批遍历。SPOP和SRANDMEMBER的区别也要记牢前者会把成员从集合里移除后者只是随机看一眼不会删除。3.5 ZSet排行榜的默认答案ZSet全称 Sorted Set是在 Set 基础之上给每个成员附了一个分数scoreRedis 会按照分数自动排序。常用命令ZADD rank:2024 100 alice ZADD rank:2024 90 bob ZINCRBY rank:2024 5 alice ZREVRANGE rank:2024 0 9 WITHSCORES排行榜、积分榜这类需求ZSet 几乎是默认答案。想给谁加分就用ZINCRBY想看 Top10 就用ZREVRANGE从高到低想看某人当前名次就用ZRANK一个命令搞定不用自己在代码里排序。它还可以做延迟队列把任务执行时间当成分数周期性ZRANGEBYSCORE取出到点要执行的任务。需要注意ZSet 的 score 是浮点数别拿它存精确金额容易踩精度坑。分数相同的时候Redis 会按成员的字典序排序。3.6 数据结构选择的经验作为一个过来人我给新手的建议是选数据类型不要看“哪种名字好听”要看“这次操作是什么”。需要简单读写、做计数用 String。需要改某个对象的局部字段用 Hash。需要排队、先进先出、最新列表用 List。需要去重、交集、并集、随机抽取用 Set。需要按分数排序、维护 TopN用 ZSet。画成一张小表会更清楚业务诉求推荐结构原因热点数据缓存String / Hash读写简单延迟最低对象局部更新Hash单字段操作不反序列化整体消息队列/最新动态List / Stream天然支持入队出队抽奖/共同关注Set集合运算方便自动去重排行榜ZSet按分数排序是内置能力如果你能把这张表背下来至少已经超过一半的 Redis 新手了。4. 持久化内存特性之外的另一半安全网4.1 为什么缓存也需要持久化既然 Redis 是内存数据库很多新手会问进程一重启数据不就全丢了吗没错如果不做任何持久化配置这是必然的。所以 Redis 提供了持久化机制目的就是让内存里的数据在进程重启、机器宕机之后能恢复回来。有人觉得“反正是缓存丢了再从数据库加载就行”这话只对了一半。如果你的缓存里放的是热点数据Redis 一重启大量请求会同时落到数据库数据库很可能被压垮。而且对于计数器、分布式锁这类数据丢了一样会造成业务异常。所以持久化是 Redis 基本特性里很重要的一块不是可有可无的功能。4.2 RDB快照简单粗暴的“拍照存档”RDB 是 Redis 默认开启的持久化方式。它的思路很简单定期把内存里的全量数据生成一份二进制快照保存到磁盘上的dump.rdb文件。RDB 的触发规则在 redis.conf 里配置最常见的是这句save 900 1 save 300 10 save 60 10000意思是900 秒内至少有 1 次写操作就生成快照300 秒内至少有 10 次写操作就生成快照60 秒内至少有 10000 次写操作也生成快照。你可以把这理解为“Redis 自己判断什么时候值得花成本存一次全量快照”。RDB 的优点是文件紧凑恢复时直接加载还原速度很快。缺点是两次快照之间可能出现数据丢失。比如 60 秒前刚生成快照下一秒来了大量写操作还没到触发条件进程就崩了那这 60 秒的数据就没了。此外BGSAVE虽然通过 fork 子进程生成快照不阻塞主线程但在大内存实例上 fork 依然可能造成短暂停顿。手动触发的话SAVE是同步阻塞式生成BGSAVE是后台异步生成。生产环境基本只用BGSAVE。4.3 AOF日志把每次写操作记下来AOFAppend Only File换了一种思路不拍全量快照而是把每条写命令追加到日志文件里。Redis 重启时重新执行这些写命令就能把数据恢复出来。AOF 有三种刷盘策略appendfsync 配置行为特点always每条写命令都同步刷到磁盘最安全性能开销最大everysec每秒刷新一次最多丢 1 秒数据性能好no交给操作系统决定性能最好数据丢失风险最高生产环境最常见的配置是everysec兼顾性能和可靠性。AOF 的优点是可以把数据丢失窗口压缩到很小缺点是日志文件会越来越大恢复时需要重放大量命令恢复速度比 RDB 慢。因此 Redis 会有 AOF 重写机制新写一个精简过的 AOF 文件把历史操作合并成最小集合减少文件体积。4.4 初学阶段怎么选如果你是刚接触 Redis直接开着默认的 RDB 就能满足大部分学习需求。如果希望数据丢失窗口更小可以把 AOF 也打开。Redis 4.0 以后还支持“混合持久化”也就是 AOF 文件以 RDB 快照打底再追加少量增量命令。这样恢复时不用一条条重放全部命令速度比纯 AOF 快数据丢失也少。一个比较稳妥的入门配置可以是这样save 900 1 appendonly yes appendfsync everysec aof-use-rdb-preamble yes我的建议是不要急着把持久化玩出花来先把 RDB 和 AOF 各自的特点搞清楚。你在改配置的时候脑子里要能回答一个问题这台 Redis 重启之后到底能找回多少数据5. 基本特性如何撑起真实场景缓存治理、分布式锁、计数器5.1 缓存穿透、击穿与雪崩缓存治理的入门认知在很多项目里Redis 的定位是给 MySQL 挡流量。但“加了缓存”不等于“万事大吉”如果姿势不对反而会惹出三个经典问题缓存穿透、缓存击穿、缓存雪崩。缓存穿透是请求查了一个“根本不存在”的数据。缓存里没有数据库里也没有。由于 Redis 缓存不存在请求每次都穿透到数据库如果被恶意刷接口数据库很容易被打挂。常见的治理思路有两个一是把不存在的查询结果也缓存起来设置一个较短的过期时间防止恶意 key 反复穿透二是在缓存前面加一层布隆过滤器把不存在的 key 直接拦截在 Redis 之前。缓存击穿和穿透容易混淆。击穿指的是某个“热点 key”正好在过期的一瞬间失效巨大流量在同一时刻打进数据库数据库直接被打穿。解决思路是互斥锁让重建缓存的操作只有一个线程去做其他线程等待或者使用逻辑过期也就是缓存里存一个逻辑过期时间字段后台异步刷新避免真正把 key 删除。缓存雪崩比击穿范围更大。它指大量 key 在同一时间内集中过期或者 Redis 服务整体不可用导致大量请求直接落到数据库。最朴素也最有效的方案是把过期时间打散比如在基础过期时间上再加一个随机值避免 key 成群结队地失效同时在架构上做高可用避免单点。这三个问题的说法很像初学者容易混。你可以这样记穿透是“查了一个不存在的东西”击穿是“一个热点 key 坏了事”雪崩是“很多 key 一起出了事”。5.2 分布式锁用SETNX实现最简单的互斥Redis 的另外一个高频应用是分布式锁。普通单机程序里可以用锁来保证并发安全但多个服务实例部署在不同的机器上时进程内的锁就互相隔离了大家同时操作同一份库存、同一条订单就可能出问题。Redis 可以充当跨进程的“锁服务器”。最简单的加锁命令是SET lock:order:123 unique_value NX PX 30000这里的NX表示只有 key 不存在时才设置PX 30000表示锁的过期时间是 30 秒。如果多个实例同时执行这条命令只有一个能成功其他人返回失败锁就拿到手了。释放锁时不能直接DEL否则可能把别人后来持有的锁误删掉。正确做法是先判断锁的 value 是不是自己设置的再删除。为了保证判断和删除是原子操作要用 Lua 脚本if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end这段脚本的含义是只有当 Redis 里存的 value 和我自己的唯一标识相等时才删除这个 key。分布式锁还有一个常见坑锁的过期时间如果设置太短业务逻辑还没执行完锁就自动释放了其他线程就能趁虚而入。真实项目里往往用 Redisson 这类客户端让看门狗线程自动续期这就是更进阶的内容了。5.3 计数器INCR的原子性为什么可靠Redis 做计数器也很常见文章点击量、点赞数、接口限流次数都可以用INCR一行命令完成。INCR article:click:1001 GET article:click:1001INCR之所以可靠是因为 Redis 命令执行是单线程的多个客户端同时发INCRRedis 会把它们排好队逐个执行不存在“两个请求同时读到同一个值然后各自 1 写回去导致少算一次”的问题。很多人反馈“redis incr 不准”多半是自己用了“先 GET 再 SET”这种两步操作两步之间其他请求插了进来计数就丢了。直接用INCR就没事。计数器还可以结合过期时间做限流比如限制某个用户 60 秒内最多请求 30 次。先判断 key 是否存在如果不存在就SET一个计数并设置过期时间然后INCR当计数值超过阈值就拒绝请求。这一整套操作在 Redis 里都是高并发安全的比用数据库做限流高效得多。5.4 哨兵与集群高可用特性留待下一步Redis 的基本特性里还有一块“高可用与分布式”的内容包括主从复制、哨兵和集群。初识阶段不用抠细节但至少要知道它们分别解决什么问题机制解决的核心问题主从复制把数据复制到多台节点从库可以分担读压力哨兵模式监听主库状态主库挂了自动选举新主库保证可用集群模式把数据分片到多个节点解决单机容量和吞吐瓶颈主从复制是基础哨兵是在主从之上加了自动故障转移集群则是把数据按槽位分散到多台机器让 Redis 能横向扩展。这些内容单拿出来都可以写很多篇初学者先把这里的示意图记在脑子里后面深入学习时就会发现它们都建立在“Redis 能做可靠数据存储和复制”这个基本特性之上。6. 从入门到动手安装、客户端工具与必备命令6.1 本机安装体验Windows、Linux与Docker学习 Redis 最好的方式就是本地跑起来。Linux 环境下通常是直接安装sudo apt update sudo apt install redis-server sudo systemctl start redis-server redis-cli ping如果你用 macOSbrew install redis也挺方便。Docker 是另一种很干净的方式不污染宿主机环境docker run --name redis-demo -p 6379:6379 -d redis:7 docker exec -it redis-demo redis-cli pingWindows 用户可能会在网上搜“redis windows 下载”。要说明的是Redis 官方并不提供原生 Windows 服务端最省心的方式是用 WSLWindows 的 Linux 子系统或者 Docker Desktop 来跑。如果一定要宿主机原生运行可以找 Memurai 这类兼容 Redis 协议的 Windows 实现或者社区移植的旧版本。生产环境我还是建议一律用 Linux。6.2 可视化客户端从命令行到图形界面很多新手刚接触 Redis 时被满屏的黑框命令行吓到想找可视化工具。这没问题但我强烈建议你先跟着命令行敲一遍常见命令再上图形界面。常用可视化客户端有三个RedisInsightRedis 官方出的免费 GUI功能全面支持内存分析、慢查询查看Another Redis Desktop Manager免费、轻量跨平台很多开发者习惯用这个Redis Desktop Manager老牌工具部分版本收费。无论用哪一款连接配置都类似填主机地址、端口默认 6379、如果有密码还要填密码。图形界面对初学阶段的帮助是“能看到 key 长什么样、过期时间还剩多久”这比纯命令行直观。但别因此忽略了命令行的学习生产服务器上不一定允许你装 GUI。6.3 高频命令与日志查看速查最后给一份入门阶段的高频命令够你用很久命令作用PING测试 Redis 是否存活SET/GET设置 / 读取字符串DEL删除 keyEXPIRE key 秒数设置过期时间TTL key查看剩余存活时间TYPE key查看 key 的数据类型DBSIZE查看总 key 数量INFO查看 Redis 运行状态和统计信息SCAN cursor分批遍历 key代替KEYS *日志排查也是实际工作中绕不开的。Redis 默认会输出运行日志Linux 上比较常见的位置是/var/log/redis/redis-server.log如果用 Docker直接docker logs redis-demo就能看到启动和运行日志。遇到启动失败、连接超时、慢命令问题先翻日志再往前查配置。想排查是否有慢命令拖慢了 Redis可以看SLOWLOG GET 10它会列出最近执行时间较长的命令这对后续调优很有帮助。我个人在实际操作中的体会是Redis 入门最忌讳“只看不敲”。五种数据结构、RDB 和 AOF 配置、INCR做计数器、SET ... NX PX做分布式锁这些内容光靠眼睛看第二天就忘了八成。找一台机器把命令敲一遍再故意把数据删掉重启一次 Redis看看哪些数据还在、哪些丢了比读十篇介绍文章都管用。等你亲手经历过“内存快照丢失一次数据”和“AOF 重放恢复”的差别才算真正摸到了 Redis 基本特性的门道。
返回列表