了解Redis

了解Redis 一、Redis 是什么精准定义RedisRemote Dictionary Server即远程字典服务。它是一款开源、基于内存、高性能、键值对Key-ValueNoSQL 数据库。核心特征一句话总结数据主要存在内存中读写极快支持多种数据结构、支持持久化、支持高可用集群的分布式缓存数据库。1.1 Redis 核心特性纯内存操作绝大多数数据读写在内存完成速度极快丰富数据结构String、List、Hash、Set、ZSet、Geo、Bitmap、HyperLogLog 等,基于丰富的数据结构实现复杂的业务逻辑支持持久化RDB/AOF重启不丢数据高可用主从、哨兵、Cluster集群原子性操作单条命令天然具备原子性无需额外加锁可快速实现分布式锁、并发计数等场景是Redis高并发安全操作的核心基础高性能、低延迟单机QPS可达10W二、Redis 的核心作用企业真实场景Redis 不是简单缓存它在企业中承担八大核心场景2.1 热点数据缓存最核心缓存高频查询、低变动数据减少 MySQL 压力。例如首页banner、商品列表、用户信息、配置信息。2.2 分布式锁利用 SET NX EX 原子命令实现分布式锁解决并发抢资源问题。场景秒杀、下单、幂等控制、防止重复提交。2.3 限流 防刷利用过期key、计数器、漏斗算法,窗口统计算法实现接口限流、IP防刷。2.4 排行榜、延时队列、消息队列ZSet 实现排行榜List 实现简单消息队列ZSet 实现延时队列2.5 会话共享、登录态存储分布式系统中统一存储 Session、Token实现多服务登录共享。2.6 计数器 统计数据文章阅读量、点赞数、访问次数高性能自增。2.7 地理位置计算Geo 实现附近的人、附近门店、距离计算。2.8 位图统计Bitmap 实现签到统计、活跃用户、状态标记极度省内存。三、Redis 单线程模型重点、面试必考3.1 Redis 为什么是单线程Redis 核心命令执行模型是单线程。指的是所有客户端的读写命令、数据解析、键值操作都由同一个主线程串行执行。3.2 单线程核心机制一个线程负责接收连接、解析命令、执行命令、返回结果所有命令排队执行不存在多线程竞争天然线程安全不需要锁、无锁竞争、无上下文切换开销3.3 单线程为什么还能这么快纯内存操作没有磁盘IO速度本身极快单线程无锁开销不需要加锁、解锁、线程切换IO多路复用epoll 实现单线程监听上万连接命令执行轻量Redis 命令都是简单内存运算避免CPU上下文切换效率极高底层数据结构更加贴近机器语言:运行无需复杂转换数据类型,运行效率高3.4 单线程的缺点核心痛点CPU 单核瓶颈、大命令阻塞单线程只能利用一个CPU核心遇到大Key、慢命令keys、flushall、超大hash遍历会阻塞整个线程一旦阻塞所有请求全部卡顿四、Redis6.0 / 7.0 多线程原理高频误区Redis 6 改成多线程了是不是不再单线程了标准答案核心命令执行依旧是单线程没有改变4.1 Redis 多线程到底多了什么Redis6 引入的是IO 多线程不是命令执行多线程。多线程只负责网络请求读取read网络响应写入write命令解析、数据读写、业务执行依然是单线程串行执行。4.2 为什么不把命令改成多线程如果命令多线程会出现多线程竞争内存数据必须加锁锁冲突、上下文切换性能反而下降失去 Redis 简单、高效、线程安全的优势4.3 多线程带来的提升解决了网络IO瓶颈在高并发连接场景下吞吐量更高但是命令执行效率不变、线程安全机制不变。五、Redis 线程模型总结终极结论5.1 Redis 4.x 及以前完全单线程IO 命令执行全部单线程5.2 Redis 4.0 开始后台多线程异步任务4.0 引入bio 线程池Background I/O专门做 “耗时、可异步、不阻塞主线程” 的事1. 惰性删除Lazy FreeUNLINK替代DEL大 key过期键、被淘汰键的异步内存释放大 Hash/List/ZSet 直接删会卡主线程交给 bio 线程慢慢删2. 持久化相关后台线程 / 子进程BGSAVERDBfork()子进程做主线程继续干活BGREWRITEAOF子进程重写 AOF避免 AOF 膨胀AOF 刷盘appendfsync everysec时有后台线程负责 fsync3. 其他后台任务主从复制RDB 传输、全量同步子进程 / 线程做集群槽位迁移、数据同步多线程 / 异步处理关闭时的数据落盘、资源回收5.2 Redis 6.x / 7.x / 8.xIO多线程 命令单线程网络读写多线程提速命令执行单线程保证安全、无锁、稳定分工主线程epoll监听连接、分发 socket 给 IO 线程IO 线程池并行做 →读 socket、解析 RESP、写响应回 socket主线程拿到解析好的命令串行执行再丢回 IO 线程写回重点多线程只做I/O 解析命令执行还是单线程无锁、安全。六、Redis 原子性操作深度详解6.1 原子性核心定义原子性指一个操作不可分割、要么全部执行成功要么全部执行失败不会出现中间状态。Redis 依托自身线程模型拥有天然的命令原子性这也是Redis能实现分布式锁、并发控制的核心根本。6.2 Redis原子性底层原理结合前文线程模型可完美解释Redis的原子性本质Redis 核心命令执行为单线程串行机制所有客户端的命令请求会进入统一队列排队执行同一时刻只会执行一条命令不存在多条命令并发执行、抢占资源的情况。这就意味着单条Redis命令从解析、执行、数据写入到返回结果全程不会被其他命令打断天然规避了并发竞争问题无需依赖操作系统锁、业务锁即可保证操作原子性。6.3 原子性覆盖范围重点误区✅ 单条命令完全原子性set、get、incr、hset、zadd 等所有单指令均具备完整原子性❌ 多条普通命令不具备原子性多条独立命令如先get再set会被其他请求插队无法保证事务一致性存在并发安全问题6.4 扩展实现多命令原子性方案针对多条命令需要保证原子性的业务场景Redis提供两种官方解决方案弥补单命令局限性6.4.1 Redis事务MULTI/EXEC通过 MULTI 开启事务批量录入多条命令最后通过 EXEC 一次性执行执行过程中不会被其他客户端命令打断保证批量命令串行隔离执行。重点核心Redis 事务不支持数据库级别的自动回滚这是Redis事务与MySQL事务最大的区别。Redis事务两种异常场景 回滚解决方案生产必知1. 入队阶段异常语法错误、参数错误场景MULTI开启事务后录入的命令存在语法错误、参数缺失、非法指令。机制Redis 会直接标记事务失败本次事务所有命令全部作废自动不执行等同于隐性回滚。示例127.0.0.1:6379 MULTI OK 127.0.0.1:6379 set k1 1 QUEUED 127.0.0.1:6379 # 输入错误指令 127.0.0.1:6379 set (error) ERR wrong number of arguments for set command 127.0.0.1:6379 EXEC (error) EXECABORT Transaction discarded because of previous errors.结论语法错误 →事务整体作废无需手动回滚。Redis事务可以通过Watch机制进⼀步保证在某个事务执⾏前某⼀个key不被修改。2. 执行阶段异常运行时错误、数据类型错误场景命令语法正确、成功入队但EXEC执行时出现运行时异常例如对String类型执行List操作、数值溢出等。机制错误命令执行失败不影响其他正常命令事务不会自动回滚成功的命令会永久写入数据造成数据不一致这是Redis事务最大的坑也是生产不推荐普通事务做一致性操作的核心原因。3. 事务异常手动回滚方案生产落地由于Redis无自动回滚机制业务必须自行实现补偿回滚标准解决方案前置数据备份执行事务前读取本次要修改的Key旧值缓存至内存执行结果校验EXEC执行后捕获异常、校验执行结果失败逆向补偿若出现运行时异常利用备份的旧值重新覆盖恢复数据完成手动回滚最优替代方案复杂一致性场景放弃Redis原生事务统一使用Lua脚本。Lua脚本执行出错时整体终止、不保留中间数据天然实现事务回滚效果无需手动补偿。6.4.2 Lua脚本生产首选、终极原子方案Lua脚本是Redis官方提供的服务端脚本编程语言也是企业生产中解决Redis复杂多命令原子操作、并发竞争、数据一致性问题的终极方案。相比于原生事务和管道Lua脚本兼具原子性、高性能、无中间脏数据、支持复杂逻辑四大优势是面试最高频、生产最常用的核心功能。6.7.1 Lua脚本底层执行原理Redis 主线程在执行Lua脚本时会将整个脚本代码视为一条完整、不可分割的命令执行全程独占主线程资源。执行核心机制脚本执行期间禁止其他任何客户端命令插队脚本内所有逻辑、多命令、条件判断、循环操作串行执行脚本未执行完毕不会切换任务不会被抢占脚本执行结束后统一释放执行权。依托Redis单线程模型Lua脚本天然拥有强事务原子性。6.7.2 Lua脚本原子性与异常回滚机制重点这是Lua脚本吊打原生事务的核心原因也是面试必背考点。1. 执行成功脚本内所有命令全部执行生效数据一致性完整保障。2. 执行异常语法错误、运行时报错、逻辑异常Lua脚本天然支持整体回滚脚本一旦出现异常脚本内已经执行的所有操作全部作废、自动回滚不会遗留脏数据不存在Redis原生事务“部分成功、部分失败”的数据不一致问题。一句话区别Redis事务运行报错不回滚 → 脏数据Lua脚本运行报错整体回滚 → 数据一致6.7.3 Lua脚本核心优势对比事务/管道强原子性自动回滚唯一可以在Redis中实现完整事务一致性的方案减少网络IO开销多命令逻辑封装在服务端执行无需多次网络往返媲美Pipeline性能服务端计算可在Redis端做判断、比对、计算减少客户端数据传输与逻辑压力避免并发插队彻底杜绝多客户端并发导致的数据覆盖、超卖、计数错乱问题可复用、可缓存支持脚本SHA1缓存重复执行无需重复传参性能更高6.7.4 Lua脚本生产实操案例秒杀扣库存、防超卖经典场景高并发秒杀校验库存 扣减库存杜绝超卖原生事务无法完美实现Lua脚本可极简落地。Lua脚本代码-- 获取当前库存 local stock tonumber(redis.call(get, seckill:stock)) -- 库存判断库存不足直接返回0不扣减 if not stock or stock 0 then return 0 end -- 库存充足扣减库存 redis.call(decr, seckill:stock) -- 返回成功标识 return 1Java调用执行public boolean seckill() { String luaScript local stock tonumber(redis.call(get, seckill:stock)) if not stock or stock 0 then return 0 end redis.call(decr, seckill:stock) return 1; // 执行Lua脚本 Long result (Long) jedis.eval(luaScript, 0); // 1成功 0失败 return result 1; }原理整个校验扣库存逻辑在Redis服务端原子执行无中间状态、无并发插队、无超卖异常自动回滚。6.7.5 Lua脚本SHA1缓存优化频繁执行长脚本会增加网络传输开销Redis支持脚本缓存script load加载脚本返回SHA1摘要evalsha通过SHA1值执行缓存脚本大幅减少流量损耗适合高频秒杀、限流场景。6.7.6 Lua脚本生产严格注意事项避坑禁止Lua脚本写死循环Lua独占主线程死循环会直接导致Redis全局阻塞、服务不可用禁止超大耗时逻辑复杂计算、大量遍历放在业务端不要压入Lua脚本尽量短小精悍保证快速执行、快速释放主线程集群环境保证Key在同一槽位Cluster集群下Lua操作多Key必须保证槽位一致否则报错统一异常处理利用Lua自动回滚特性无需业务手动补偿数据6.7.7 Lua脚本、事务、Pipeline 终极选型规范仅批量提速、无需原子→ 优先 Pipeline简单多命令、低并发、可容忍少量不一致→ 可选原生事务MULTI/EXEC高并发、强一致性、防超卖、分布式锁、限流→必选Lua脚本6.5 原子性核心生产场景分布式锁SET key value NX EX 原子命令并发场景下只有一个客户端能创建成功避免锁竞争失效并发计数incr/decr 原子自增自减实现点赞、浏览量、订单计数杜绝并发计数错乱接口限流防刷通过原子计数过期key精准统计单位时间请求次数无并发统计误差秒杀扣库存结合Lua脚本原子校验库存、扣减库存超卖问题彻底解决6.6 原子性常见面试误区总结误区1Redis所有操作都原子性 → 纠正仅单命令、事务、Lua脚本具备原子性多条普通命令不保证误区2Redis多线程会破坏原子性 → 纠正IO多线程仅负责网络读写命令执行依旧单线程原子性完全不受影响误区3Redis事务支持自动回滚 → 纠正Redis事务无自动回滚机制仅语法错误整体作废运行时错误数据会脏写需业务手动补偿回滚Lua脚本支持天然自动回滚是生产首选七、Redis Pipeline 管道操作深度详解在Redis高频优化手段中管道Pipeline是提升批量命令执行效率的核心方案常和原子性、Lua脚本对比考察是面试生产高频知识点。很多开发者混淆管道与原子事务的区别本节彻底讲透管道原理、使用场景与实操。7.1 管道操作核心定义Redis Pipeline管道是一种批量命令优化机制允许客户端一次性将多条命令打包发送给Redis服务端服务端批量执行后统一一次性返回所有执行结果无需每条命令进行一次网络交互。简单来说打包发送、批量执行、统一返回大幅减少网络IO开销。7.2 传统单条命令 VS 管道命令核心差异7.2.1 普通逐条执行每一条命令都需要经历客户端发送网络请求 → 服务端处理 → 服务端返回结果100条命令就需要100次网络往返RTT。网络延迟是Redis批量操作的最大瓶颈而非命令执行速度。7.2.2 Pipeline管道批量执行客户端将多条命令缓存至本地一次性发送给Redis服务端按顺序逐条执行最后统一返回全部结果。无论多少条命令仅需1次网络RTT极大降低网络开销批量操作性能提升数倍至数十倍。7.3 管道底层执行原理结合Redis单线程模型管道执行流程清晰易懂客户端本地缓存多条Redis命令不单独发送所有命令打包一次性发送至Redis服务端Redis单线程串行、按顺序逐条执行所有命令全部命令执行完毕后统一返回所有结果客户端统一解析结果完成批量操作。7.4 管道的核心特性重点面试考点7.4.1 无原子性最核心特点Pipeline 不保证原子性这是和事务、Lua脚本最大的区别。管道内的多条命令允许被其他客户端命令插队命令之间不隔离、不独占主线程。如果中间某条命令执行失败不会回滚后续命令会继续执行仅终止当前错误命令不影响整体流程。7.4.2 有序执行管道内的命令严格按照客户端提交顺序串行执行不会乱序保证执行顺序一致性。7.4.3 仅优化网络IO不提升命令执行速度管道只是减少了网络往返次数单条命令的执行速度没有任何提升提速的本质是规避了频繁网络IO的耗时损耗。7.5 Pipeline 实操代码示例Java以下为Jedis管道实操案例批量写入1000条数据对比逐条写入性能差异// 获取Redis连接 Jedis jedis new Jedis(127.0.0.1, 6379); // 开启管道 Pipeline pipeline jedis.pipelined(); // 批量打包命令本地缓存无网络请求 for (int i 1; i 1000; i) { pipeline.set(user:info: i, user_ i); pipeline.expire(user:info: i, 3600); } // 一次性提交执行统一返回结果 pipeline.sync(); // 关闭资源 pipeline.close(); jedis.close();实操结论1000条数据写入逐条执行耗时数百毫秒管道执行仅需十几毫秒性能提升巨大。7.6 管道、事务、Lua脚本 三者核心对比特性Pipeline管道Redis事务(MULTI/EXEC)Lua脚本原子性❌ 不支持✅ 支持无回滚✅ 完全支持命令插队允许其他命令插入不允许插队不允许插队性能优势减少网络IO批量极速无网络优化仅保证原子兼具原子性批量高性能异常回滚不回滚后续继续执行语法错误失效运行错误不回滚出错整体终止无中间状态适用场景批量增删改、无需原子一致性简单多命令原子操作复杂并发、强原子性场景7.7 管道生产适用场景批量数据初始化批量写入缓存数据、预热热点数据批量过期设置统一为大量key设置过期时间批量删除数据清理过期缓存、无效数据非一致性批量统计批量查询、批量计数无需强事务7.8 管道生产禁忌与注意事项禁止超大批量打包单次管道命令过多会导致缓存溢出、服务端一次性处理压力过大建议拆分批次单次100-500条最佳强一致性场景禁用秒杀、锁竞争、库存扣减等需要原子性的场景不能用管道管道内禁止穿插复杂逻辑所有命令必须提前打包不可边执行边判断业务逻辑失败无重试机制管道命令出错不会自动回滚业务需自行处理异常数据7.9 管道高频面试误区总结误区1Pipeline 具备原子性 → 纠正管道无原子性仅做网络优化命令可被插队、出错不回滚误区2管道提升命令执行速度 → 纠正仅减少网络往返命令执行本身速度不变误区3管道可以替代事务/Lua → 纠正无原子性无法替代强一致性事务场景八、生产核心注意事项禁止慢命令keys、hgetall、flushall、大量循环遍历禁止大Key大String、超大Hash/List极易阻塞主线程利用多线程优势高并发场景开启io-threads提升网络吞吐CPU单核高频Redis 占用CPU100%通常是主线程阻塞导致规范原子性使用并发场景优先使用单命令或Lua脚本杜绝先查后改的非原子操作防止并发Bug批量操作优先管道无原子需求的批量读写统一使用Pipeline大幅优化Redis性能区分管道与原子组件批量提速用Pipeline、简单原子用事务、复杂高并发原子用Lua脚本九、总结Redis 是一款高性能内存键值对NoSQL数据库主要用于热点缓存、分布式锁、限流、计数器、消息队列等场景。Redis 核心命令执行为单线程模型依托单线程串行执行的特性天然实现单命令原子性无需额外加锁规避并发竞争问题基于内存操作、IO多路复用、无锁竞争实现超高并发Redis6.0之后引入IO多线程仅优化网络读写流程命令执行依旧保持单线程既保证了线程安全和操作原子性又提升了高并发网络吞吐能力复杂并发场景可通过事务或Lua脚本实现多命令原子操作。