一、Redis的五种数据类型
1. 字符串类型
a. 用途
b. 常用命令
- SET key value:设置键值对
- GET key:获取指定键的值
- INCR key:将键对应的值加1
- DECR key:将键对应的值减1
示例代码:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.set('name', 'Lucy')
print(r.get('name'))
r.incr('age')
r.decr('age')
2. 哈希类型
a. 用途
b. 常用命令
- HSET key field value:设置哈希类型数据中某个域的值
- HGET key field:获取哈希类型数据中某个域的值
- HGETALL key:获取哈希类型数据中所有域和值
示例代码:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.hset('user', 'name', 'Lucy')
r.hset('user', 'age', 18)
print(r.hget('user', 'name'))
print(r.hgetall('user'))
3. 列表类型
a. 用途
- 存储一组有序的字符串类型数据,可以用于实现消息队列、任务列表等场景。
b. 常用命令
- LPUSH key value:从列表左侧插入一个值
- RPUSH key value:从列表右侧插入一个值
- LPOP key:从列表左侧弹出一个值
- RPOP key:从列表右侧弹出一个值
示例代码:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.lpush('task', 'task1')
r.lpush('task', 'task2')
r.rpush('task', 'task3')
print(r.lpop('task'))
print(r.rpop('task'))
4. 集合类型
a. 用途
- 存储一组无序的字符串类型数据,常用于去重和快速判断某元素是否存在集合中。
b. 常用命令
- SADD key member:向集合中添加一个元素
- SMEMBERS key:获取集合中所有元素
- SISMEMBER key member:判断某元素是否存在集合中
示例代码:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.sadd('students', 'Lucy', 'Jack')
print(r.smembers('students'))
print(r.sismember('students', 'Lucy'))
5. 有序集合类型
a. 用途
- 存储一组有序的字符串类型数据,每个元素都有一个对应的分数,可用于排行榜等场景。
b. 常用命令
- ZADD key score member:向有序集合中添加一个元素和分数
- ZRANGEBYSCORE key min max:返回有序集合中分数在给定区间内的所有元素
- ZREVRANGE key start stop [WITHSCORES]:返回有序集合中按照分数从大到小排序的一部分元素,可选择同时返回分数
示例代码:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.zadd('score', {'Lucy': 90, 'Jack': 85, 'Mike': 92})
print(r.zrangebyscore('score', min=85, max=92))
print(r.zrevrange('score', start=0, stop=1, withscores=True))
二、Redis的使用场景
Redis 是一个高效的键值对数据库,因其速度快、支持丰富的数据类型和灵活的配置而被广泛应用于以下五种场景:
1. 缓存系统
Redis 可以将常用的数据存储在内存中,并且设置过期时间来实现缓存的功能。当应用需要这些数据时,可以直接从 Redis 中获取,避免了从磁盘读取数据的IO操作,从而提高了系统的响应时间和吞吐量。
import redis
redis_client = redis.Redis(host='localhost', port=6379, db=0)
def get_data_from_cache(key):
"""
从 Redis 的缓存中获取数据
"""
data = redis_client.get(key)
if data is not None:
return data
data = fetch_data_from_database(key)
redis_client.set(key, data, ex=3600)
return data
2. 计数器
Redis 的incr/decr命令可以对一个key进行原子性自增/自减操作,因此经常被用作计数器。比如统计网站访问量、商品的销量、评论的点赞数等。
def increase_counter(key):
"""
自增一个计数器,返回自增后的值
"""
return redis_client.incr(key)
def decrease_counter(key):
"""
自减一个计数器,返回自减后的值
"""
return redis_client.decr(key)
3. 消息队列
Redis提供了消息队列功能,可以作为轻量级的消息中间件使用,支持多个生产者和消费者。生产者可以使用lpush或rpush向一个列表插入消息,而消费者则使用blpop或brpop从列表中弹出消息并进行处理。
import threading
import time
def produce_message(queue_name: str):
"""
生产者向队列中插入消息
"""
for i in range(10):
message = f"Message {i}"
redis_client.lpush(queue_name, message)
print(f"Produce Message: {message}")
time.sleep(0.5)
def consume_message(queue_name: str):
"""
消费者从队列中弹出消息并进行处理
"""
while True:
message = redis_client.brpop(queue_name, timeout=3)
if message is not None:
print(f"Consume Message: {message[1].decode()}")
t1 = threading.Thread(target=produce_message, args=('my_queue',))
t2 = threading.Thread(target=consume_message, args=('my_queue',))
t1.start()
t2.start()
t1.join()
t2.join()
4. 排行榜系统
Redis提供有序集合(SortedSet)结构,支持按照某个字段为成员进行排序,因此可以用来实现排行榜系统。比如统计音乐的播放次数、新闻的阅读量、用户的积分排名等。
def update_ranking(username: str, score: float):
"""
更新排行榜,以 username 为成员、score 为排序指标
"""
redis_client.zadd('ranking', {username: score})
def get_top_n(n: int):
"""
获取排行榜前 n 名
"""
return redis_client.zrevrangebyscore('ranking', '+inf', '-inf', start=0, num=n, withscores=True)
5. 分布式锁
Redis的setnx命令可以在key不存在时设置key的值,因此可以基于此实现分布式锁。一个进程在执行加锁操作时,先尝试使用setnx占据锁的key,如果返回值为1,表示获得了锁;否则表示锁已被其他进程占据,等待一段时间后再次尝试。加锁和解锁操作需要使用相同的key和value才能成功。
def acquire_lock_with_timeout(lockname: str, acquire_timeout: int=10):
"""
获取分布式锁,等待 acquire_timeout 秒后仍未获得锁则放弃
"""
identifier = str(uuid.uuid4())
end = time.time() + acquire_timeout
while time.time() < end:
if redis_client.setnx(lockname, identifier):
return identifier
elif not redis_client.ttl(lockname):
redis_client.expire(lockname, 10)
time.sleep(0.1)
return False
def release_lock(lockname: str, identifier: str):
"""
释放分布式锁,需要传入获取锁时返回的 identifier
"""
pipline = redis_client.pipeline(True)
while True:
try:
pipline.watch(lockname)
if pipline.get(lockname).decode() == identifier:
pipline.multi()
pipline.delete(lockname)
pipline.execute()
return True
pipline.unwatch()
except redis.exceptions.WatchError:
continue
finally:
pipline.reset()
return False