Redis 7 天学习笔记 · Day 1
Redis 7 天学习笔记 · Day 1
主题:Redis 是什么 + 跑通第一个缓存
环境:Ubuntu 22.04/24.04 虚拟机 + Python 3 + Redis
一句话总结:Redis 是内存里的键值数据库,挡在 MySQL 前面当缓存,用空间换时间。
一、今日核心认知
- Redis 是内存里的“键值字典”:给一个 key,还一个 value。
- 它和 MySQL 不是替代关系:MySQL 是真相来源(硬盘,不能丢),Redis 是加速层(内存,丢了可重建)。
- 缓存必须有过期时间(TTL):否则数据库改了,缓存还是旧值,用户一直看到旧数据。
- 缓存本身只有三行代码:
get查缓存 → 未命中查库 →setex写回。其余全是脚手架。
二、概念速记
1. 为什么需要 Redis
同一个数据被反复从硬盘读,硬盘慢、数据库扛不住。把查过的结果临时放内存,下次直接拿,不打扰数据库。
2. 内存 vs 硬盘
| 硬盘(MySQL) | 内存(Redis) | |
|---|---|---|
| 速度 | 毫秒级 | 微秒级 |
| 断电 | 数据还在 | 数据消失 |
| 容量 | 大 | 小 |
| 定位 | 真相来源 | 加速副本 |
3. 键值对长什么样
"user:1001" → {"id":"1001","name":"用户1001"} "hello" → "world" "article:42:views" → "1532"常用命令:
SET key value # 存 GET key # 取 DEL key # 删 SETEX key 60 value # 存 + 60 秒后自动过期 TTL key # 看还剩几秒 # ≥0 剩余秒数 # -1 key 存在,但没设过期时间(永不过期) # -2 key 已不存在4. 请求链路
用户 → Redis(快,先查) ↓ 没有(未命中) MySQL(慢,再查) ↓ 查到了 写回 Redis(下次就快)“加个缓存” = 在数据库前面加一层 Redis。
5. 为什么必须过期
- 内存贵且小,不能全放。
- 数据会过时:数据库改了,缓存不失效就会返回旧值。
- 过期不是缺陷,是缓存保持“大致正确”的机制。
三、环境与安装(Ubuntu)
# 1. 更新 + 基础工具sudoaptupdate&&sudoaptupgrade-ysudoaptinstall-ycurlvimnet-tools# 2. 安装 Redis(22.04 给 6.0.16,24.04 给 7.0.15,Day1–6 够用)sudoaptinstall-yredis-serversudosystemctlenable--nowredis-server# 3. 验证redis-cli PING# 期望 PONGredis-cli INFO server|grepredis_version关键路径:
| 内容 | 路径 |
|---|---|
| 配置文件 | /etc/redis/redis.conf |
| 日志 | /var/log/redis/redis-server.log |
| 数据目录 | /var/lib/redis/ |
启动时
overcommit_memory、THP、TCP backlog的 warning 今天不用管。
Python 环境:
mkdir-p~/redis-7days&&cd~/redis-7days python3-mvenv venvsourcevenv/bin/activate pipinstallredisUbuntu 24.04 有 PEP 668 保护,必须用 venv,否则
pip install报externally-managed-environment。每次新终端:
cd ~/redis-7days && source venv/bin/activate。
四、代码笔记:day1_demo.py
importredis,time,json# host 用 127.0.0.1 而非 localhost:避免解析到 ::1 而 Redis 只监听 IPv4r=redis.Redis(host="127.0.0.1",port=6379,db=0,decode_responses=True)try:r.ping()print("[OK] Redis 连接成功\n")exceptredis.ConnectionError:print("[ERROR] 连不上 Redis")print(" 排查顺序:")print(" 1. sudo systemctl status redis-server # 服务是否在跑")print(" 2. redis-cli PING # 命令行能否连上")print(" 3. ss -lntp | grep 6379 # 端口是否在监听")print(" 4. sudo ufw status # 本机防火墙是否拦截(概率极低)")raisedefslow_database_query(user_id:str)->dict:time.sleep(2)# 模拟硬盘上的 MySQL 查询return{"id":user_id,"name":f"用户{user_id}","email":f"user{user_id}@example.com"}defget_user(user_id:str)->dict:cache_key=f"user:{user_id}"cached=r.get(cache_key)# ① 先查内存ifcachedisnotNone:print(f" [缓存命中] key={cache_key}")returnjson.loads(cached)print(f" [缓存未命中] key={cache_key},去查数据库...")data=slow_database_query(user_id)# ② 未命中才查库r.setex(cache_key,60,json.dumps(data))# ③ 写回内存,60 秒过期print(f" [已回写缓存] key={cache_key},TTL=60s")returndataif__name__=="__main__":uid="1001"start=time.perf_counter();get_user(uid);t1=time.perf_counter()-start start=time.perf_counter();get_user(uid);t2=time.perf_counter()-startprint(f"\n第一次:{t1:.3f}s 第二次:{t2:.3f}s 加速比:{t1/t2:.1f}倍")真正要记住的只有三行:
| 代码 | 作用 | 要点 |
|---|---|---|
r.get(cache_key) | 查缓存 | 未命中返回None,不是报错 |
slow_database_query() | 查库 | 真实项目里是 SQL |
r.setex(key, 60, val) | 回写 + 过期 | 每个缓存 key 都要有 TTL |
r.ping()失败时,最可能是 Redis 服务没启动;其次检查端口是否被监听;防火墙或bind配置导致127.0.0.1不通的概率极低,Day 1 通常不用处理。
五、验证实验记录
redis-cliKEYS user:* # 期望:1) "user:1001" TTL user:1001 # 期望:≤60 的数字,倒计时中 # -1 说明没设过期时间(本 Demo 不该出现) # -2 说明 key 已不存在(等 60 秒后即为此值) GET user:1001 # 期望:与 Python 输出的 JSON 一致等 60 秒后再执行TTL user:1001→ 返回-2,缓存自动消失。
→ 第一次亲眼看到“过期”发生。
六、问题与处理
| 问题 | 处理 |
|---|---|
pip install报externally-managed-environment | 没进 venv,source venv/bin/activate |
Python 报ConnectionRefusedError | 先redis-cli PING确认服务活着;host 用127.0.0.1 |
| 两次都“未命中” | key 之前跑过已过期,或换了user_id,等 60 秒或换 ID |
| 宿主机 RedisInsight 连不上虚拟机 | 正常,默认bind 127.0.0.1 |
| 重启后 Redis 没起来 | sudo systemctl enable redis-server |
七、交付物 Checklist
- 截图 1:Demo 终端输出,第一次 ≥1.5s,第二次 ≤0.05s,加速比 ≥30 倍
- 截图 2:
redis-cli里KEYS/TTL/GET的输出 - 一句话:100 字内解释“第二次为什么快”,必须含“内存”“没查数据库”
- 示例:“第一次没命中,执行了
sleep(2)的假查库并把结果写回 Redis;第二次直接从本机内存拿到 JSON,绕过了那段 2 秒等待。”
- 示例:“第一次没命中,执行了