Python 读写 Redis 缓存数据库:写给 Python 初学者的入门案例
很多学习 Python 的小伙伴,在学完文件读写、SQLite、MySQL 之后,都会接触到一个新的数据库:Redis。
Redis 和传统关系型数据库不太一样。MySQL、SQLite 更擅长长期保存结构化数据,而 Redis 更常用于缓存、计数器、排行榜、验证码、分布式锁、消息队列等高性能场景。
这篇文章会先简单介绍 Redis 是什么,然后通过 Python 代码演示如何连接 Redis,并完成常见的读写操作。
一、Redis 是什么
Redis 全称是 Remote Dictionary Server,可以理解为一个高性能的内存型 Key-Value 数据库。
它的核心特点是:
- 数据主要存放在内存中,读写速度非常快
- 使用 Key-Value 形式保存数据
- 支持丰富的数据结构,不只是简单字符串
- 支持设置过期时间,适合做缓存
- 支持持久化,可以把内存数据保存到磁盘
- 常用于缓存、计数、排行榜、会话、限流等场景
我们可以把 Redis 想象成一个超快的字典:
python
cache = {
"user:1:name": "Alice",
"article:100:view_count": 358,
"login:code:13800138000": "829316"
}
只不过这个字典不是保存在 Python 程序里,而是保存在 Redis 服务中。多个程序、多个接口、多个服务器都可以一起访问它。
二、Redis 和 MySQL、SQLite 有什么区别
简单对比一下:
| 对比项 | Redis | MySQL / SQLite |
|---|---|---|
| 存储方式 | 主要在内存中 | 主要在磁盘中 |
| 数据模型 | Key-Value 和多种数据结构 | 表、行、列 |
| 读写速度 | 非常快 | 相对较慢 |
| 常见用途 | 缓存、计数、排行榜、验证码 | 业务数据持久化 |
| 查询方式 | 通过 key 访问 | SQL 查询 |
| 是否适合复杂查询 | 不适合 | 适合 |
所以 Redis 通常不是用来完全替代 MySQL 或 SQLite 的,而是和它们配合使用。
例如:
- 用户资料保存在 MySQL 中
- 热门用户资料缓存到 Redis 中
- 下次查询时先查 Redis
- Redis 没有命中,再查 MySQL
- 查到后再写回 Redis
这个模式通常叫缓存旁路,也叫 Cache Aside。
三、准备 Redis 环境
如果你本机已经安装 Redis,可以直接启动 Redis 服务。
如果没有安装,也可以使用 Docker 快速启动:
bash
docker run --name redis-demo -p 6379:6379 -d redis
测试 Redis 是否可用:
bash
redis-cli ping
如果返回:
bash
PONG
说明 Redis 服务已经启动成功。
四、安装 Python Redis 客户端
Python 操作 Redis 常用的库是 redis。
安装命令:
bash
pip install redis
安装完成后,可以在 Python 中导入:
python
import redis
五、连接 Redis
新建一个文件 redis_demo.py:
python
import redis
r = redis.Redis(
host="localhost",
port=6379,
db=0,
decode_responses=True
)
print(r.ping())
运行:
bash
python redis_demo.py
如果输出:
bash
True
说明 Python 已经成功连接到 Redis。
这里的几个参数含义如下:
| 参数 | 说明 |
|---|---|
| host | Redis 服务器地址 |
| port | Redis 端口,默认 6379 |
| db | Redis 数据库编号,默认 0 |
| decode_responses | 是否把返回值自动解码成字符串 |
如果不设置 decode_responses=True,Redis 返回的字符串可能是字节类型:
python
b'Alice'
对初学者来说,建议先加上 decode_responses=True,这样输出更直观。
六、字符串 String:最基础的缓存读写
Redis 最简单的数据类型是 String。
可以用它保存用户名、验证码、Token、简单配置、缓存结果等。
python
import redis
r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)
# 写入字符串
r.set("user:1:name", "Alice")
# 读取字符串
name = r.get("user:1:name")
print(name)
输出:
bash
Alice
设置过期时间
缓存通常不应该永久存在,所以 Redis 支持给 key 设置过期时间。
例如保存短信验证码,60 秒后自动删除:
python
r.set("login:code:13800138000", "829316", ex=60)
code = r.get("login:code:13800138000")
print(code)
其中 ex=60 表示 60 秒后过期。
也可以先写入,再单独设置过期时间:
python
r.set("temp:data", "hello")
r.expire("temp:data", 30)
查看剩余过期时间:
python
ttl = r.ttl("temp:data")
print(ttl)
七、数字自增:计数器案例
Redis 很适合做计数器,例如文章阅读量、点赞数、接口访问次数。
python
article_key = "article:100:view_count"
r.set(article_key, 0)
r.incr(article_key)
r.incr(article_key)
r.incr(article_key)
count = r.get(article_key)
print(count)
输出:
bash
3
也可以一次增加指定数量:
python
r.incrby(article_key, 10)
print(r.get(article_key))
对应减少:
python
r.decr(article_key)
r.decrby(article_key, 5)
计数器是 Redis 非常常见的使用场景,因为这类操作简单、高频,而且对速度要求高。
八、Hash:保存对象信息
如果要保存一个用户对象,用多个 String 也可以:
text
user:1:name
user:1:age
user:1:city
但更推荐使用 Hash:
python
r.hset("user:1", mapping={
"name": "Alice",
"age": 20,
"city": "Shanghai"
})
name = r.hget("user:1", "name")
print(name)
user = r.hgetall("user:1")
print(user)
输出:
bash
Alice
{'name': 'Alice', 'age': '20', 'city': 'Shanghai'}
修改 Hash 中的某个字段:
python
r.hset("user:1", "city", "Beijing")
删除某个字段:
python
r.hdel("user:1", "age")
判断字段是否存在:
python
exists = r.hexists("user:1", "name")
print(exists)
Hash 非常适合保存用户资料、商品摘要、文章基础信息等对象型数据。
九、List:保存列表数据
Redis 的 List 可以理解为一个有序列表。
常见用途:
- 最新消息列表
- 简单队列
- 最近浏览记录
- 日志缓冲
示例:
python
key = "user:1:recent_articles"
r.delete(key)
r.lpush(key, "article:100")
r.lpush(key, "article:101")
r.lpush(key, "article:102")
articles = r.lrange(key, 0, -1)
print(articles)
输出:
bash
['article:102', 'article:101', 'article:100']
lpush 是从左侧插入,所以最后插入的元素排在最前面。
如果想从右侧插入,可以使用:
python
r.rpush(key, "article:103")
从列表中弹出一个元素:
python
item = r.lpop(key)
print(item)
限制列表长度,只保留最近 3 条:
python
r.ltrim(key, 0, 2)
这个操作很适合做"最近浏览记录"。
十、Set:保存不重复集合
Set 是无序且不重复的集合。
常见用途:
- 用户标签
- 点赞用户集合
- 已签到用户集合
- 去重统计
示例:记录给文章点赞的用户。
python
key = "article:100:liked_users"
r.delete(key)
r.sadd(key, "user:1")
r.sadd(key, "user:2")
r.sadd(key, "user:1")
users = r.smembers(key)
print(users)
is_liked = r.sismember(key, "user:1")
print(is_liked)
输出:
bash
{'user:1', 'user:2'}
True
虽然添加了两次 user:1,但 Set 会自动去重。
统计集合元素数量:
python
count = r.scard(key)
print(count)
取消点赞:
python
r.srem(key, "user:1")
十一、Sorted Set:排行榜案例
Sorted Set 是有序集合,每个元素都有一个分数。
它非常适合做排行榜。
例如游戏积分排行榜:
python
key = "game:rank"
r.delete(key)
r.zadd(key, {
"Alice": 1500,
"Bob": 1800,
"Cindy": 1700,
"David": 1200
})
top3 = r.zrevrange(key, 0, 2, withscores=True)
print(top3)
输出:
bash
[('Bob', 1800.0), ('Cindy', 1700.0), ('Alice', 1500.0)]
给用户增加积分:
python
r.zincrby(key, 200, "Alice")
查询 Alice 的排名:
python
rank = r.zrevrank(key, "Alice")
print(rank)
注意:Redis 的排名从 0 开始。如果返回 0,表示第一名。
十二、保存 JSON 数据
有时我们希望把一个 Python 字典直接缓存起来。
可以使用 json.dumps() 转成字符串保存,读取时再用 json.loads() 转回来。
python
import json
import redis
r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)
user = {
"id": 1,
"name": "Alice",
"age": 20,
"roles": ["admin", "editor"]
}
r.set("cache:user:1", json.dumps(user, ensure_ascii=False), ex=300)
cached_text = r.get("cache:user:1")
cached_user = json.loads(cached_text)
print(cached_user["name"])
print(cached_user["roles"])
这种方式很适合缓存接口返回结果。
不过要注意:Redis 本身保存的是字符串。如果你要频繁修改对象中的某个字段,Hash 可能比 JSON 字符串更方便。
十三、完整案例:使用 Redis 缓存用户资料
下面做一个更贴近实际项目的案例。
假设我们有一个"慢查询函数",它模拟从数据库查询用户资料。为了提高速度,我们先查 Redis;如果 Redis 中没有,再查数据库,并把结果写入 Redis。
python
import json
import time
import redis
r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)
def query_user_from_database(user_id):
"""模拟数据库查询。"""
print("正在查询数据库...")
time.sleep(2)
fake_database = {
1: {"id": 1, "name": "Alice", "age": 20},
2: {"id": 2, "name": "Bob", "age": 22},
3: {"id": 3, "name": "Cindy", "age": 19},
}
return fake_database.get(user_id)
def get_user(user_id):
"""优先从 Redis 缓存读取用户资料。"""
cache_key = f"cache:user:{user_id}"
cached_user = r.get(cache_key)
if cached_user:
print("命中 Redis 缓存")
return json.loads(cached_user)
user = query_user_from_database(user_id)
if user is None:
return None
r.set(cache_key, json.dumps(user, ensure_ascii=False), ex=60)
print("已写入 Redis 缓存")
return user
if __name__ == "__main__":
print(get_user(1))
print(get_user(1))
第一次运行时,输出类似:
bash
正在查询数据库...
已写入 Redis 缓存
{'id': 1, 'name': 'Alice', 'age': 20}
命中 Redis 缓存
{'id': 1, 'name': 'Alice', 'age': 20}
第一次查询 Redis 没有数据,所以会访问"数据库",并等待 2 秒。
第二次查询时,Redis 中已经有缓存,所以直接返回,速度明显更快。
这就是 Redis 最典型的缓存使用方式。
十四、封装一个简单的 Redis 工具类
在项目中,我们通常不会把 Redis 操作散落在各个地方。可以先封装一个简单工具类:
python
import json
import redis
class RedisCache:
def __init__(self, host="localhost", port=6379, db=0):
self.client = redis.Redis(
host=host,
port=port,
db=db,
decode_responses=True
)
def set_json(self, key, value, expire_seconds=300):
text = json.dumps(value, ensure_ascii=False)
self.client.set(key, text, ex=expire_seconds)
def get_json(self, key):
text = self.client.get(key)
if text is None:
return None
return json.loads(text)
def delete(self, key):
return self.client.delete(key)
def exists(self, key):
return self.client.exists(key) == 1
cache = RedisCache()
cache.set_json("user:1", {"id": 1, "name": "Alice"}, expire_seconds=60)
user = cache.get_json("user:1")
print(user)
print(cache.exists("user:1"))
cache.delete("user:1")
这样后续业务代码会更清晰。
十五、连接池:更适合项目使用
在 Web 项目中,不建议每次请求都重新创建 Redis 连接。
可以使用连接池:
python
import redis
pool = redis.ConnectionPool(
host="localhost",
port=6379,
db=0,
decode_responses=True,
max_connections=20
)
r = redis.Redis(connection_pool=pool)
r.set("site:name", "Python Redis Demo")
print(r.get("site:name"))
连接池可以复用连接,减少频繁创建连接带来的开销。
如果你以后使用 Flask、FastAPI、Django,也可以在应用启动时创建 Redis 客户端,然后在接口中复用它。
十六、删除 key 和清理数据库
删除一个 key:
python
r.delete("user:1")
判断 key 是否存在:
python
exists = r.exists("user:1")
print(exists)
查看匹配的 key:
python
keys = r.keys("user:*")
print(keys)
清空当前数据库:
python
r.flushdb()
清空所有数据库:
python
r.flushall()
注意:flushdb() 和 flushall() 都是危险操作,真实项目中不要随便执行。
十七、初学者常见问题
1. 连接失败
常见错误:
bash
redis.exceptions.ConnectionError
可能原因:
- Redis 服务没有启动
- host 或 port 写错了
- Docker 容器没有映射 6379 端口
- Redis 设置了密码,但代码中没有配置
如果 Redis 设置了密码,需要这样连接:
python
r = redis.Redis(
host="localhost",
port=6379,
password="your_password",
decode_responses=True
)
2. 读取结果是字节类型
如果看到:
python
b'Alice'
可以在连接时加上:
python
decode_responses=True
3. JSON 读取报错
如果执行:
python
json.loads(text)
报错,通常说明 Redis 中保存的内容不是合法 JSON 字符串。
建议保存和读取都封装成统一方法,避免同一个 key 有时存普通字符串,有时存 JSON。
4. 缓存数据不是最新的
缓存会带来一个新问题:数据库更新了,但 Redis 里还是旧数据。
常见处理方式:
- 更新数据库后删除缓存
- 设置较短的过期时间
- 对一致性要求很高的数据,不要随便缓存
例如:
python
def update_user_name(user_id, new_name):
update_database_user_name(user_id, new_name)
r.delete(f"cache:user:{user_id}")
这样下次查询时会重新从数据库读取最新数据,再写入 Redis。
十八、Redis key 命名建议
Redis 的 key 最好有清晰的命名规则。
推荐格式:
text
业务名:对象名:对象ID:字段名
例如:
text
user:1:profile
user:1:recent_articles
article:100:view_count
article:100:liked_users
login:code:13800138000
game:rank
好的 key 命名可以让排查问题更容易。
不推荐随意命名:
text
a
test
data1
abc
项目变大后,这类 key 很难维护。
十九、什么时候适合使用 Redis
适合使用 Redis 的场景:
- 高频读取的数据
- 可以设置过期时间的数据
- 允许短时间不一致的数据
- 计数器、排行榜、点赞集合
- 验证码、Token、Session
- 接口限流、简单队列
不适合使用 Redis 的场景:
- 复杂 SQL 查询
- 强事务业务
- 长期保存的核心数据
- 需要大量关联查询的数据
一句话总结:核心业务数据放 MySQL、PostgreSQL、SQLite 等数据库中,Redis 更适合放热点数据和临时数据。
二十、完整练习代码
最后给出一个可以直接运行的综合练习:
python
import json
import redis
def main():
r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)
print("连接状态:", r.ping())
print("\n=== String 示例 ===")
r.set("app:name", "Redis Python Demo", ex=300)
print(r.get("app:name"))
print("\n=== Counter 示例 ===")
r.delete("article:1:view_count")
for _ in range(5):
r.incr("article:1:view_count")
print(r.get("article:1:view_count"))
print("\n=== Hash 示例 ===")
r.hset("user:1", mapping={"name": "Alice", "age": 20, "city": "Shanghai"})
print(r.hgetall("user:1"))
print("\n=== List 示例 ===")
r.delete("user:1:recent")
r.lpush("user:1:recent", "article:100", "article:101", "article:102")
print(r.lrange("user:1:recent", 0, -1))
print("\n=== Set 示例 ===")
r.delete("article:1:likes")
r.sadd("article:1:likes", "user:1", "user:2", "user:1")
print(r.smembers("article:1:likes"))
print("\n=== Sorted Set 示例 ===")
r.delete("rank:score")
r.zadd("rank:score", {"Alice": 98, "Bob": 86, "Cindy": 95})
print(r.zrevrange("rank:score", 0, -1, withscores=True))
print("\n=== JSON 缓存示例 ===")
user = {"id": 1, "name": "Alice", "skills": ["Python", "Redis"]}
r.set("cache:user:1", json.dumps(user, ensure_ascii=False), ex=60)
cached_user = json.loads(r.get("cache:user:1"))
print(cached_user)
if __name__ == "__main__":
main()
运行前确保:
- Redis 服务已经启动
- 已安装 Python 依赖:
pip install redis - 代码中的 host、port、password 配置正确
总结
Python 操作 Redis 的核心流程并不复杂:
text
安装 redis 库 -> 连接 Redis -> 选择数据结构 -> 写入数据 -> 读取数据 -> 设置过期时间
常用方法可以简单记住:
| 方法 | 作用 |
|---|---|
set() / get() |
读写字符串 |
incr() / decr() |
计数器 |
hset() / hgetall() |
操作 Hash |
lpush() / lrange() |
操作 List |
sadd() / smembers() |
操作 Set |
zadd() / zrevrange() |
操作排行榜 |
expire() / ttl() |
设置和查看过期时间 |
delete() |
删除 key |
对 Python 初学者来说,学习 Redis 的重点不是一开始就背所有命令,而是先理解它适合解决什么问题。
如果你能掌握缓存用户资料、验证码、计数器、排行榜这几个案例,就已经可以在很多真实项目中使用 Redis 了。