当前位置:首页 > 问答 > 正文

用Redis命令快速搞定程序性能提升,数据获取效率真心不错

日常开发经验及Redis官方文档应用案例)

直接上干货,用最直白的话说说怎么用Redis的几个简单命令让程序跑得更快,咱们不扯那些复杂的概念,就讲实际怎么用。

先把数据缓存起来,这是最快的招

程序慢,十有八九是卡在数据库查询上,尤其是那些经常要读、但又不太经常变的数据,比如网站的商品分类、用户的基本信息、热门文章列表,每次用户点开页面你都去数据库里查一遍,数据库压力大,网络来回传输也费时间。

这时候就用上Redis最核心的命令:SETGET

用Redis命令快速搞定程序性能提升,数据获取效率真心不错

  • 怎么用? 简单得要命,比如用户登录后,你从数据库里查到了他的昵称、头像这些信息,别每次都查,直接扔到Redis里: SET user:12345 '{"name": "张三", "avatar": "1.jpg"}' 这里的 user:12345 就是键(可以理解成数据的唯一标签),后面那一串JSON就是值,你可以给它设个过期时间,比如3600秒(1小时),这样即使数据有更新,一小时后也会自动失效重新从数据库加载,保证数据不会太旧,加个过期时间的命令是 SETEX user:12345 3600 '...'
  • 下次要用的时候,别找数据库了,直接 GET user:12345,速度是微秒级别的,比去数据库快了几个数量级,这就好比你把常用的工具从仓库(数据库)里拿出来,放在手边的工具箱(Redis)里,用的时候伸手就拿,不用每次都跑一趟仓库。 来源:常见Web应用性能优化模式)

对付排行榜,这个命令是神器

做游戏积分榜、文章热度排名什么的,如果每次都让数据库 ORDER BY score DESC LIMIT 100,数据量一大数据库就头疼。

用Redis的 ZSET(有序集合)简直是量身定做。

  • 怎么用? 比如有个小游戏,用户得分了: ZADD game_leaderboard 1500 "玩家A" ZADD game_leaderboard 3200 "玩家B" ZADD game_leaderboard 2750 "玩家C" 命令的意思就是向名叫 game_leaderboard 的排行榜里,添加成员和对应的分数。
  • 要取前十名? 一句命令搞定:ZREVRANGE game_leaderboard 0 9 WITHSCORESZREVRANGE 是按分数从大到小排,0 9 表示取排名0到9的(也就是前十名),WITHSCORES 会把分数也一起显示出来,这个操作复杂度很低,速度极快。
  • 还想看某个玩家的排名? ZREVRANK game_leaderboard "玩家C",直接告诉你他排第几名(从0开始算),这些计算Redis在内部就帮你搞定了,完全不用程序自己费劲排序。 来源:社交类应用数据模型设计实践)

防止重复提交和恶意刷屏,一招搞定

用Redis命令快速搞定程序性能提升,数据获取效率真心不错

用户手快点了一下提交按钮,或者有人想用脚本恶意刷接口,你需要一个简单的机制来判断这个请求在短时间内是不是已经发生过了。

Redis的 SETNX 命令配合过期时间,成本极低。

  • 怎么用? SETNX 是“SET if Not eXists”的缩写,意思是只有当这个键不存在的时候,才设置它,比如用户提交订单,生成一个订单号202405200001,你可以执行: SETNX order:submit:202405200001 1 同时给它设置个过期时间,比如30秒:EXPIRE order:submit:202405200001 30
  • 如果用户是第一次提交SETNX 会成功(返回1),然后程序继续处理订单。
  • 如果用户在30秒内又用同一个订单号提交了一次(可能是快速双击),SETNX 会失败(返回0),因为键已经存在了,这时候你的程序直接返回“请勿重复提交”的提示就行了,这样就用一个非常轻量级的方式挡住了重复请求,比去查数据库判断要省资源得多。

秒杀库存检查,避免超卖

这是经典的并发问题,成百上千人同时抢购10件商品,如果只用数据库的库存字段做 库存 = 库存 - 1,很可能出现库存减成负数(超卖)的情况。

用Redis命令快速搞定程序性能提升,数据获取效率真心不错

Redis是单线程执行命令的,这个特性天生适合做简单的计数和判断。

  • 怎么用? 提前把商品库存加载到Redis里:SET item_stock:1001 10
  • 当用户发起秒杀请求时,使用 DECR 命令:DECR item_stock:1001,这个命令会把键 item_stock:1001 的值减1,并且返回减完之后的值。
  • 关键点来了:因为Redis是单线程处理命令,所以同一时间只有一个 DECR 命令会被执行,不存在并发问题,你只需要判断 DECR 命令的返回值:
    • 如果返回值大于等于0,说明减库存成功,这个用户抢到了。
    • 如果返回值小于0(比如是-1),说明库存已经没了。注意: 这里减到-1了,为了避免库存变成负数,你可以在减之前先用 GET 看一下是不是大于0,或者用更复杂的Lua脚本保证原子性(这里先不展开),但核心思想就是用Redis的单线程原子操作来避免并发冲突。 来源:高并发场景下的缓存设计误区与正确实践)

存储会话信息,告别笨重

传统的Web应用把用户登录状态(Session)存在服务器内存里,一旦服务器重启或者需要做集群时就非常麻烦。

用Redis存Session是现在很常见的做法。

  • 怎么用? 用户登录成功后,生成一个唯一的Session ID,然后把Session内容(比如用户ID、登录时间)存到Redis: SETEX session:abc123def456 1800 '{"userId": 12345}' (30分钟过期)。
  • 这样做的最大好处
    1. 应用服务器可以做成无状态的:无论用户的请求被分配到哪台服务器,这台服务器都可以用Session ID去Redis里把用户数据取出来,轻松实现扩展。
    2. 速度快:相比把Session存在数据库或文件里,Redis的读写速度快得多。
    3. 自动过期:设置好过期时间,Redis会自动清理过期的Session,省心。

说白了,Redis就是一个速度超快的“多功能临时记事本”,你把那些读多写少计算耗时需要临时快速判断的数据放进去,用简单的 GET/SETZADD/ZRANGESETNX/DECR 这些命令操作一下,就能把最耗时的数据库压力卸掉一大块,程序的响应速度立马就能感觉到提升,刚开始不用追求太复杂的用法,就从上面这几个最简单的场景试起,效果绝对是“真心不错”。