1. Redis入门:从缓存概念到实战计数器
第一次接触Redis时,我被它的性能数据震惊了——单机版轻松扛住10万+QPS。但更让我惊讶的是,很多开发者虽然天天用Redis,却说不清它和Memcached的本质区别。今天我们就从最基础的缓存概念开始,手把手带你用Redis实现一个高并发计数器。
2. 缓存系统核心原理解析
2.1 缓存究竟是什么?
简单说,缓存就是数据的临时存储层。当我在电商系统工作时,商品详情页的QPS经常突破5万,MySQL直接查询根本扛不住。这时我们用Redis缓存热点商品数据,响应时间从200ms降到2ms。
缓存的核心价值在于:
- 缓解后端压力(减少数据库查询)
- 加速数据访问(内存比磁盘快100倍)
- 提高系统扩展性(无状态设计)
2.2 Redis的独特优势
相比Memcached,Redis的杀手锏是丰富的数据结构。去年双十一大促时,我们用ZSET实现秒杀排行榜,用HASH存储购物车数据,这些都是Memcached做不到的。
Redis五大核心数据结构对比:
| 结构类型 | 典型场景 | 性能特点 |
|---|---|---|
| STRING | 计数器、缓存 | O(1)读写 |
| HASH | 对象属性存储 | 字段级操作 |
| LIST | 消息队列 | 头部尾部操作快 |
| SET | 标签系统 | 去重+集合运算 |
| ZSET | 排行榜 | 带权重的有序集合 |
3. 高并发计数器实战
3.1 基础版计数器实现
先看最简单的PHP实现:
php复制$count = file_get_contents('counter.txt');
$count++;
file_put_contents('counter.txt', $count);
这个方案在100并发时就会出问题——多个请求同时读取到相同值,导致计数不准。
3.2 Redis原子计数器方案
用Redis的INCR命令轻松解决:
bash复制> INCR article:123:views
(integer) 1
这个命令的原子性由Redis单线程模型保证。我们压测过,单节点轻松实现8万+/秒的计数。
3.3 生产级优化技巧
- 管道化操作:批量执行多个INCR
python复制pipe = redis.pipeline()
for article_id in article_list:
pipe.incr(f"article:{article_id}:views")
pipe.execute()
- 内存优化:对于大Key使用HASH分片
bash复制# 将article:123:views改为article:views:123
HINCRBY article:views 123 1
- 持久化策略:根据业务选择
- RDB适合计数器(容忍少量丢失)
- AOF需要每秒刷盘(性能下降约30%)
4. 高并发场景深度优化
4.1 热点Key处理方案
当某个明星动态的阅读数暴增时,我们遇到过单分片QPS超限的情况。最终方案:
- 客户端本地缓存+定时同步
- 使用Redis集群分散压力
- 关键数据双写MySQL做兜底
4.2 分布式ID生成器
基于Redis的原子性实现分布式ID:
lua复制-- Lua脚本保证原子性
local current = redis.call('GET', KEYS[1])
if not current then
redis.call('SET', KEYS[1], ARGV[2])
return ARGV[2]
end
local nextid = current + ARGV[1]
redis.call('SET', KEYS[1], nextid)
return nextid
5. 踩坑实录与性能调优
5.1 典型问题排查
案例1:计数器突然归零
- 原因:误用了EXPIRE命令
- 解决:区分业务场景设置TTL
案例2:集群环境下计数不准确
- 原因:跨节点事务未生效
- 解决:改用Lua脚本保证原子性
5.2 性能优化参数
关键redis.conf配置:
code复制# 最大内存限制(建议物理内存的3/4)
maxmemory 12gb
# 内存淘汰策略(计数器适用volatile-lru)
maxmemory-policy volatile-lru
# 连接池大小(建议500-1000)
maxclients 1000
6. 扩展应用场景
除了计数器,Redis还能实现:
- 实时排行榜(ZSET)
- 分布式锁(SETNX)
- 秒杀库存(DECR+Lua)
- 用户画像(BITMAP)
我在社交APP项目中,用Redis的HyperLogLog统计UV,误差不到1%的情况下,内存消耗只有传统方案的1/10。