SET、GET、INCR、DECR、APPEND、GETRANGE等
String是Redis最基础也是最常用的数据类型。虽然名为"字符串",但它实际上是二进制安全的,可以存储任何类型的数据,包括文本、数字、序列化对象、甚至图片。一个String类型的值最大可以存储512MB的数据。
Redis使用SDS(Simple Dynamic String,简单动态字符串)来实现String类型,相比C语言的字符串,SDS有以下优势:
# 基本的设置和获取
127.0.0.1:6379> SET name "张三"
OK
127.0.0.1:6379> GET name
"张三"
# SET命令的选项
# NX: 键不存在时才设置(Not eXists)
127.0.0.1:6379> SET lock:order:123 "locked" NX
OK
127.0.0.1:6379> SET lock:order:123 "locked" NX
(nil) # 键已存在,设置失败
# XX: 键存在时才设置(eXists)
127.0.0.1:6379> SET config:version "1.0" XX
(nil) # 键不存在,设置失败
127.0.0.1:6379> SET name "李四" XX
OK # 键存在,设置成功
# EX: 设置过期时间(秒)
127.0.0.1:6379> SET session:user123 "token_abc" EX 3600
OK
# PX: 设置过期时间(毫秒)
127.0.0.1:6379> SET cache:data "value" PX 60000
OK
# EXAT: 设置过期时间戳(秒)
127.0.0.1:6379> SET reminder "meeting" EXAT 1675234567
OK
# GET选项:设置新值并返回旧值
127.0.0.1:6379> SET counter 100
OK
127.0.0.1:6379> SET counter 200 GET
"100"
# SETNX: SET if Not eXists(原子操作)
127.0.0.1:6379> SETNX lock:resource:1 "process_a"
(integer) 1 # 设置成功
127.0.0.1:6379> SETNX lock:resource:1 "process_b"
(integer) 0 # 设置失败,键已存在
# SETEX: SET with EXpire(原子操作)
127.0.0.1:6379> SETEX verification:code 300 "123456"
OK
127.0.0.1:6379> TTL verification:code
(integer) 295
# PSETEX: 毫秒级过期时间
127.0.0.1:6379> PSETEX flash:message 5000 "操作成功"
OK
# MSET: 批量设置(原子操作)
127.0.0.1:6379> MSET user:1:name "Alice" user:1:age 28 user:1:city "北京"
OK
# MGET: 批量获取
127.0.0.1:6379> MGET user:1:name user:1:age user:1:city
1) "Alice"
2) "28"
3) "北京"
# MSETNX: 批量设置(仅当所有键都不存在时)
127.0.0.1:6379> MSETNX key1 "value1" key2 "value2" key3 "value3"
(integer) 1 # 全部设置成功
127.0.0.1:6379> MSETNX key1 "new1" key4 "value4"
(integer) 0 # key1已存在,全部设置失败
# 性能对比:批量操作 vs 单次操作
# 单次操作:需要3次网络往返
SET user:1:name "Alice"
SET user:1:age 28
SET user:1:city "北京"
# 批量操作:只需1次网络往返
MSET user:1:name "Alice" user:1:age 28 user:1:city "北京"
# INCR: 自增1(原子操作)
127.0.0.1:6379> SET page:views 1000
OK
127.0.0.1:6379> INCR page:views
(integer) 1001
127.0.0.1:6379> INCR page:views
(integer) 1002
# DECR: 自减1(原子操作)
127.0.0.1:6379> SET stock:product:123 50
OK
127.0.0.1:6379> DECR stock:product:123
(integer) 49
# INCRBY: 增加指定值
127.0.0.1:6379> INCRBY page:views 100
(integer) 1102
# DECRBY: 减少指定值
127.0.0.1:6379> DECRBY stock:product:123 5
(integer) 44
# INCRBYFLOAT: 增加浮点数
127.0.0.1:6379> SET price:product:456 99.99
OK
127.0.0.1:6379> INCRBYFLOAT price:product:456 10.01
"110"
127.0.0.1:6379> INCRBYFLOAT price:product:456 -5.5
"104.5"
# 键不存在时,默认从0开始
127.0.0.1:6379> INCR new:counter
(integer) 1
# 1. 网站访问统计
127.0.0.1:6379> INCR stats:page:home:views
(integer) 1
127.0.0.1:6379> INCR stats:page:home:views
(integer) 2
# 2. 文章点赞数
127.0.0.1:6379> INCR article:1001:likes
(integer) 1
# 3. 库存扣减(秒杀场景)
127.0.0.1:6379> SET product:888:stock 100
OK
127.0.0.1:6379> DECR product:888:stock
(integer) 99
127.0.0.1:6379> GET product:888:stock
"99"
# 4. 限流器(固定窗口)
127.0.0.1:6379> SET rate:user:1001:20240101 0 EX 60
OK
127.0.0.1:6379> INCR rate:user:1001:20240101
(integer) 1
# 检查是否超过限制
127.0.0.1:6379> GET rate:user:1001:20240101
"1"
# 5. 分布式ID生成器
127.0.0.1:6379> INCR global:order:id
(integer) 1
127.0.0.1:6379> INCR global:order:id
(integer) 2
# APPEND: 追加字符串
127.0.0.1:6379> SET message "Hello"
OK
127.0.0.1:6379> APPEND message " World"
(integer) 11 # 返回追加后的长度
127.0.0.1:6379> GET message
"Hello World"
# 键不存在时,APPEND等同于SET
127.0.0.1:6379> APPEND new:key "value"
(integer) 5
# STRLEN: 获取字符串长度
127.0.0.1:6379> STRLEN message
(integer) 11
# 中文字符长度(UTF-8编码)
127.0.0.1:6379> SET chinese "你好世界"
OK
127.0.0.1:6379> STRLEN chinese
(integer) 12 # 每个中文字符占3个字节
# GETRANGE: 获取子字符串(索引从0开始)
127.0.0.1:6379> SET text "Hello Redis World"
OK
127.0.0.1:6379> GETRANGE text 0 4
"Hello"
127.0.0.1:6379> GETRANGE text 6 10
"Redis"
# 负数索引表示从末尾开始
127.0.0.1:6379> GETRANGE text -5 -1
"World"
# 获取全部内容
127.0.0.1:6379> GETRANGE text 0 -1
"Hello Redis World"
# SETRANGE: 替换字符串的一部分
127.0.0.1:6379> SETRANGE text 6 "Python"
(integer) 18 # 返回修改后的长度
127.0.0.1:6379> GET text
"Hello Python World"
# 超出原字符串长度时,中间用\x00填充
127.0.0.1:6379> SET short "Hi"
OK
127.0.0.1:6379> SETRANGE short 10 "End"
(integer) 13
127.0.0.1:6379> GET short
"Hi\x00\x00\x00\x00\x00\x00\x00\x00End"
# GETSET: 设置新值并返回旧值(原子操作)
127.0.0.1:6379> SET counter 100
OK
127.0.0.1:6379> GETSET counter 200
"100"
127.0.0.1:6379> GET counter
"200"
# 推荐使用SET GET选项(Redis 6.2+)
127.0.0.1:6379> SET counter 300 GET
"200"
# 获取锁(使用SET NX EX)
127.0.0.1:6379> SET lock:order:123 "uuid-abc-123" NX EX 30
OK # 获取锁成功
# 其他进程尝试获取锁
127.0.0.1:6379> SET lock:order:123 "uuid-def-456" NX EX 30
(nil) # 获取锁失败
# 释放锁(需要验证是否是自己的锁)
# 使用Lua脚本保证原子性
127.0.0.1:6379> EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock:order:123 "uuid-abc-123"
(integer) 1 # 释放成功
# Python实现示例
import redis
import uuid
r = redis.Redis()
lock_key = "lock:order:123"
lock_value = str(uuid.uuid4())
# 获取锁
if r.set(lock_key, lock_value, nx=True, ex=30):
try:
# 执行业务逻辑
print("处理订单...")
finally:
# 释放锁
lua_script = """
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
"""
r.eval(lua_script, 1, lock_key, lock_value)
# 存储用户对象
127.0.0.1:6379> SET user:1001 '{"id":1001,"name":"张三","age":25,"city":"北京"}'
OK
# 获取用户对象
127.0.0.1:6379> GET user:1001
"{\"id\":1001,\"name\":\"张三\",\"age\":25,\"city\":\"北京\"}"
# Python实现
import redis
import json
r = redis.Redis(decode_responses=True)
# 存储对象
user = {"id": 1001, "name": "张三", "age": 25, "city": "北京"}
r.set("user:1001", json.dumps(user, ensure_ascii=False))
# 获取对象
user_json = r.get("user:1001")
user = json.loads(user_json)
print(user["name"]) # 输出: 张三
# 存储Session(带过期时间)
127.0.0.1:6379> SETEX session:abc123def 3600 '{"user_id":1001,"username":"zhangsan","login_time":1675234567}'
OK
# 获取Session
127.0.0.1:6379> GET session:abc123def
"{\"user_id\":1001,\"username\":\"zhangsan\",\"login_time\":1675234567}"
# 检查Session是否存在
127.0.0.1:6379> EXISTS session:abc123def
(integer) 1
# 续期Session
127.0.0.1:6379> EXPIRE session:abc123def 3600
(integer) 1
# 删除Session(登出)
127.0.0.1:6379> DEL session:abc123def
(integer) 1
# 固定窗口限流(每分钟最多100次请求)
import redis
import time
r = redis.Redis()
def is_allowed(user_id, max_requests=100, window=60):
key = f"rate:user:{user_id}:{int(time.time() // window)}"
current = r.incr(key)
if current == 1:
r.expire(key, window)
return current <= max_requests
# 测试
user_id = 1001
if is_allowed(user_id):
print("请求通过")
else:
print("请求被限流")
# Redis命令演示
127.0.0.1:6379> INCR rate:user:1001:28087057
(integer) 1
127.0.0.1:6379> EXPIRE rate:user:1001:28087057 60
(integer) 1
127.0.0.1:6379> INCR rate:user:1001:28087057
(integer) 2
127.0.0.1:6379> GET rate:user:1001:28087057
"2"
| 命令 | 功能 | 时间复杂度 | 应用场景 |
|---|---|---|---|
| SET/GET | 设置/获取值 | O(1) | 缓存、配置 |
| MSET/MGET | 批量设置/获取 | O(N) | 批量缓存 |
| INCR/DECR | 自增/自减 | O(1) | 计数器、库存 |
| APPEND | 追加字符串 | O(1) | 日志追加 |
| GETRANGE | 获取子串 | O(N) | 分页内容 |
| SETRANGE | 替换子串 | O(1) | 部分更新 |
| STRLEN | 获取长度 | O(1) | 长度验证 |
本课程深入讲解了Redis String类型的各种操作命令:
String是Redis最基础的数据类型,掌握其各种操作命令和应用场景,是使用Redis的基础。在实际开发中,要根据业务需求选择合适的命令,注意原子性、过期时间和性能优化。