Redis 数据操作:知识点详解与代码实战
2.1 Redis 数据存储
2.1.1 Redis 数据库本质
Redis 是一个键值对数据库,所有的数据都通过唯一的键(Key)来访问。键是二进制安全的,可以是字符串、数字甚至序列化对象。
2.1.2 键的命名规范
bash
复制代码
# 好的命名示例
user:1001:name # 用户ID为1001的姓名
article:9527:title # 文章ID为9527的标题
order:20240316:001 # 订单号
# 命令示例
127.0.0.1:6379> SET user:1001:name "张三"
OK
127.0.0.1:6379> GET user:1001:name
"张三"
2.2 文本数据类型(String)
2.2.1 语法知识点
| 命令 |
语法 |
说明 |
| SET |
`SET key value [EX seconds] [PX milliseconds] [NX |
XX]` |
| GET |
GET key |
获取键对应的值 |
| MSET |
MSET key1 value1 key2 value2 ... |
批量设置多个键值对 |
| MGET |
MGET key1 key2 ... |
批量获取多个键的值 |
| INCR |
INCR key |
将键的值加1(原子操作) |
| DECR |
DECR key |
将键的值减1(原子操作) |
| INCRBY |
INCRBY key increment |
将键的值增加指定整数 |
| DECRBY |
DECRBY key decrement |
将键的值减少指定整数 |
| APPEND |
APPEND key value |
在原有值后追加字符串 |
| STRLEN |
STRLEN key |
获取字符串长度 |
| GETRANGE |
GETRANGE key start end |
获取子字符串 |
| SETEX |
SETEX key seconds value |
设置键值并指定过期时间 |
| SETNX |
SETNX key value |
键不存在时才设置(分布式锁) |
2.2.2 案例代码
bash
复制代码
# 1. 基础设置与获取
127.0.0.1:6379> SET username "张三"
OK
127.0.0.1:6379> GET username
"张三"
# 2. 带过期时间的设置(缓存场景)
# 设置一个验证码,5分钟后过期
127.0.0.1:6379> SETEX verification:13800138000 300 "123456"
OK
# 或者使用SET命令的EX选项
127.0.0.1:6379> SET verification:13800138000 "123456" EX 300
OK
# 3. 批量操作
127.0.0.1:6379> MSET user:1:name "李四" user:1:age "25" user:1:city "北京"
OK
127.0.0.1:6379> MGET user:1:name user:1:age user:1:city
1) "李四"
2) "25"
3) "北京"
# 4. 原子计数器(实现网站访问量统计)
127.0.0.1:6379> SET page:home:views 1000
OK
127.0.0.1:6379> INCR page:home:views # 访问量+1
(integer) 1001
127.0.0.1:6379> INCRBY page:home:views 100 # 批量增加100
(integer) 1101
127.0.0.1:6379> DECR page:home:views # 访问量-1
(integer) 1100
# 5. 分布式锁实现(防止超卖)
127.0.0.1:6379> SETNX lock:product:1001 "thread-001" # 尝试获取锁
(integer) 1 # 返回1表示获取成功
127.0.0.1:6379> SETNX lock:product:1001 "thread-002" # 另一个线程尝试获取
(integer) 0 # 返回0表示获取失败,锁已被占用
# 6. 字符串操作
127.0.0.1:6379> SET message "Hello"
OK
127.0.0.1:6379> APPEND message " World!" # 追加字符串
(integer) 12
127.0.0.1:6379> GET message
"Hello World!"
127.0.0.1:6379> STRLEN message # 获取字符串长度
(integer) 12
127.0.0.1:6379> GETRANGE message 0 4 # 获取子串(0-4位置)
"Hello"
2.3 Hash 数据类型
2.3.1 语法知识点
| 命令 |
语法 |
说明 |
| HSET |
HSET key field value [field value ...] |
设置哈希表中的字段 |
| HGET |
HGET key field |
获取指定字段的值 |
| HMSET |
HMSET key field1 value1 field2 value2 ... |
批量设置多个字段 |
| HMGET |
HMGET key field1 field2 ... |
批量获取多个字段的值 |
| HGETALL |
HGETALL key |
获取所有字段和值 |
| HKEYS |
HKEYS key |
获取所有字段名 |
| HVALS |
HVALS key |
获取所有字段值 |
| HEXISTS |
HEXISTS key field |
判断字段是否存在 |
| HDEL |
HDEL key field [field ...] |
删除一个或多个字段 |
| HLEN |
HLEN key |
获取字段数量 |
| HINCRBY |
HINCRBY key field increment |
为字段值增加整数 |
| HSETNX |
HSETNX key field value |
字段不存在时才设置 |
2.3.2 案例代码
bash
复制代码
# 1. 存储用户信息(对象存储)
127.0.0.1:6379> HSET user:1001 username "王五" age "30" email "wangwu@example.com"
(integer) 3
# 2. 获取单个字段
127.0.0.1:6379> HGET user:1001 username
"王五"
127.0.0.1:6379> HGET user:1001 age
"30"
# 3. 批量获取多个字段
127.0.0.1:6379> HMGET user:1001 username age email
1) "王五"
2) "30"
3) "wangwu@example.com"
# 4. 获取所有字段和值
127.0.0.1:6379> HGETALL user:1001
1) "username"
2) "王五"
3) "age"
4) "30"
5) "email"
6) "wangwu@example.com"
# 5. 获取所有字段名
127.0.0.1:6379> HKEYS user:1001
1) "username"
2) "age"
3) "email"
# 6. 获取所有字段值
127.0.0.1:6379> HVALS user:1001
1) "王五"
2) "30"
3) "wangwu@example.com"
# 7. 更新用户年龄(原子操作)
127.0.0.1:6379> HINCRBY user:1001 age 1 # 年龄加1岁
(integer) 31
127.0.0.1:6379> HGET user:1001 age
"31"
# 8. 购物车实现
127.0.0.1:6379> HSET cart:user:1001 product:101 2 # 商品101加入2件
(integer) 1
127.0.0.1:6379> HSET cart:user:1001 product:102 1 # 商品102加入1件
(integer) 1
127.0.0.1:6379> HGETALL cart:user:1001
1) "product:101"
2) "2"
3) "product:102"
4) "1"
127.0.0.1:6379> HDEL cart:user:1001 product:101 # 删除商品101
(integer) 1
127.0.0.1:6379> HLEN cart:user:1001 # 购物车商品数量
(integer) 1
2.4 List 数据类型
2.4.1 语法知识点
| 命令 |
语法 |
说明 |
| LPUSH |
LPUSH key value [value ...] |
从左侧插入元素 |
| RPUSH |
RPUSH key value [value ...] |
从右侧插入元素 |
| LPOP |
LPOP key |
从左侧移除并返回元素 |
| RPOP |
RPOP key |
从右侧移除并返回元素 |
| LLEN |
LLEN key |
获取列表长度 |
| LRANGE |
LRANGE key start stop |
获取指定范围的元素 |
| LINDEX |
LINDEX key index |
通过索引获取元素 |
| LSET |
LSET key index value |
通过索引设置元素值 |
| LINSERT |
`LINSERT key BEFORE |
AFTER pivot value` |
| LREM |
LREM key count value |
移除指定数量的元素 |
| LTRIM |
LTRIM key start stop |
截取列表保留指定范围 |
| BLPOP |
BLPOP key [key ...] timeout |
阻塞式左侧弹出 |
| BRPOP |
BRPOP key [key ...] timeout |
阻塞式右侧弹出 |
2.4.2 案例代码
bash
复制代码
# 1. 消息队列实现(先进先出)
127.0.0.1:6379> RPUSH message:queue "用户登录" "订单创建" "支付成功" # 右侧入队
(integer) 3
127.0.0.1:6379> LPOP message:queue # 左侧出队(先进先出)
"用户登录"
127.0.0.1:6379> LPOP message:queue
"订单创建"
# 2. 栈实现(后进先出)
127.0.0.1:6379> LPUSH stack:history "/home" "/about" "/contact" # 左侧入栈
(integer) 3
127.0.0.1:6379> LPOP stack:history # 左侧出栈(后进先出)
"/contact"
127.0.0.1:6379> LPOP stack:history
"/about"
# 3. 分页查询(LRANGE)
127.0.0.1:6379> RPUSH article:comments "评论1" "评论2" "评论3" "评论4" "评论5"
(integer) 5
# 获取第1页,每页2条(索引0-1)
127.0.0.1:6379> LRANGE article:comments 0 1
1) "评论1"
2) "评论2"
# 获取第2页,每页2条(索引2-3)
127.0.0.1:6379> LRANGE article:comments 2 3
1) "评论3"
2) "评论4"
# 获取所有元素
127.0.0.1:6379> LRANGE article:comments 0 -1
1) "评论1"
2) "评论2"
3) "评论3"
4) "评论4"
5) "评论5"
# 4. 阻塞队列(实现生产者-消费者模式)
# 消费者A(阻塞等待消息,超时时间10秒)
127.0.0.1:6379> BLPOP task:queue 10
# 如果队列为空,会阻塞等待,直到有消息或超时
# 生产者B(生产消息)
127.0.0.1:6379> RPUSH task:queue "处理订单ID:12345"
(integer) 1
# 此时消费者A立即收到消息
# 5. 列表修剪(固定长度队列)
127.0.0.1:6379> RPUSH recent:search "手机" "电脑" "平板" "相机" "耳机"
(integer) 5
# 只保留最近3条搜索记录
127.0.0.1:6379> LTRIM recent:search -3 -1
OK
127.0.0.1:6379> LRANGE recent:search 0 -1
1) "平板"
2) "相机"
3) "耳机"
# 6. 列表元素操作
127.0.0.1:6379> RPUSH fruits "苹果" "香蕉" "橙子" "苹果" "葡萄"
(integer) 5
127.0.0.1:6379> LLEN fruits # 列表长度
(integer) 5
127.0.0.1:6379> LINDEX fruits 2 # 获取索引2的元素
"橙子"
127.0.0.1:6379> LSET fruits 1 "草莓" # 修改索引1的元素
OK
127.0.0.1:6379> LREM fruits 2 "苹果" # 移除2个"苹果"
(integer) 2
127.0.0.1:6379> LRANGE fruits 0 -1
1) "草莓"
2) "橙子"
3) "葡萄"
2.5 Set 数据类型
2.5.1 语法知识点
| 命令 |
语法 |
说明 |
| SADD |
SADD key member [member ...] |
向集合添加元素 |
| SREM |
SREM key member [member ...] |
移除集合中的元素 |
| SMEMBERS |
SMEMBERS key |
获取集合所有元素 |
| SISMEMBER |
SISMEMBER key member |
判断元素是否在集合中 |
| SCARD |
SCARD key |
获取集合元素数量 |
| SPOP |
SPOP key [count] |
随机移除并返回指定数量元素 |
| SRANDMEMBER |
SRANDMEMBER key [count] |
随机获取指定数量元素 |
| SINTER |
SINTER key [key ...] |
求多个集合的交集 |
| SUNION |
SUNION key [key ...] |
求多个集合的并集 |
| SDIFF |
SDIFF key [key ...] |
求多个集合的差集 |
| SMOVE |
SMOVE source destination member |
将元素从一个集合移动到另一个 |
2.5.2 案例代码
bash
复制代码
# 1. 标签系统实现
127.0.0.1:6379> SADD article:1001:tags "Java" "Redis" "数据库"
(integer) 3
127.0.0.1:6379> SADD article:1002:tags "Python" "Redis" "Web"
(integer) 3
127.0.0.1:6379> SADD article:1003:tags "Java" "Spring" "微服务"
(integer) 3
# 2. 查看文章的标签
127.0.0.1:6379> SMEMBERS article:1001:tags
1) "Java"
2) "Redis"
3) "数据库"
# 3. 判断文章是否有某个标签
127.0.0.1:6379> SISMEMBER article:1001:tags "Redis"
(integer) 1 # 1表示存在
127.0.0.1:6379> SISMEMBER article:1001:tags "Python"
(integer) 0 # 0表示不存在
# 4. 标签数量统计
127.0.0.1:6379> SCARD article:1001:tags
(integer) 3
# 5. 标签推荐(共同标签)
# 查找同时使用Java和Redis的文章(交集)
127.0.0.1:6379> SINTER article:1001:tags article:1002:tags
1) "Redis" # 两篇文章的共同标签
# 6. 推荐系统(共同关注)
127.0.0.1:6379> SADD user:1001:follow "user:1002" "user:1003" "user:1004"
(integer) 3
127.0.0.1:6379> SADD user:1005:follow "user:1002" "user:1006" "user:1007"
(integer) 3
# 查找共同关注的人
127.0.0.1:6379> SINTER user:1001:follow user:1005:follow
1) "user:1002"
# 推荐可能认识的人(差集:user:1001关注了但user:1005没关注的人)
127.0.0.1:6379> SDIFF user:1001:follow user:1005:follow
1) "user:1003"
2) "user:1004"
# 7. 抽奖系统实现
127.0.0.1:6379> SADD lottery:20240316 "用户A" "用户B" "用户C" "用户D" "用户E"
(integer) 5
# 随机抽取1名中奖者(不移除)
127.0.0.1:6379> SRANDMEMBER lottery:20240316 1
1) "用户C"
# 随机抽取3名中奖者(移除)
127.0.0.1:6379> SPOP lottery:20240316 3
1) "用户A"
2) "用户B"
3) "用户E"
# 查看剩余参与者
127.0.0.1:6379> SMEMBERS lottery:20240316
1) "用户C"
2) "用户D"
# 8. 唯一访客统计
127.0.0.1:6379> SADD page:home:visitors:20240316 "192.168.1.1" "192.168.1.2" "192.168.1.1"
(integer) 2 # 重复IP只添加一次
127.0.0.1:6379> SCARD page:home:visitors:20240316 # 独立访客数
(integer) 2
2.6 ZSet 数据类型
2.6.1 语法知识点
| 命令 |
语法 |
说明 |
| ZADD |
ZADD key score member [score member ...] |
添加元素及分值 |
| ZRANGE |
ZRANGE key start stop [WITHSCORES] |
按排名范围获取元素 |
| ZREVRANGE |
ZREVRANGE key start stop [WITHSCORES] |
按排名倒序获取元素 |
| ZRANK |
ZRANK key member |
获取元素排名(正序) |
| ZREVRANK |
ZREVRANK key member |
获取元素排名(倒序) |
| ZSCORE |
ZSCORE key member |
获取元素的分值 |
| ZCARD |
ZCARD key |
获取集合元素数量 |
| ZCOUNT |
ZCOUNT key min max |
统计分值范围内的元素数量 |
| ZINCRBY |
ZINCRBY key increment member |
为元素增加分值 |
| ZREM |
ZREM key member [member ...] |
移除一个或多个元素 |
| ZRANGEBYSCORE |
ZRANGEBYSCORE key min max [WITHSCORES] |
按分值范围获取元素 |
| ZREMRANGEBYRANK |
ZREMRANGEBYRANK key start stop |
按排名范围移除元素 |
| ZREMRANGEBYSCORE |
ZREMRANGEBYSCORE key min max |
按分值范围移除元素 |
2.6.2 案例代码
bash
复制代码
# 1. 游戏排行榜实现
127.0.0.1:6379> ZADD game:leaderboard 1500 "玩家A" 2800 "玩家B" 3200 "玩家C" 2100 "玩家D"
(integer) 4
# 2. 查看排行榜(从高到低)
127.0.0.1:6379> ZREVRANGE game:leaderboard 0 -1 WITHSCORES
1) "玩家C"
2) "3200"
3) "玩家B"
4) "2800"
5) "玩家D"
6) "2100"
7) "玩家A"
8) "1500"
# 3. 查看前3名(Top3)
127.0.0.1:6379> ZREVRANGE game:leaderboard 0 2 WITHSCORES
1) "玩家C"
2) "3200"
3) "玩家B"
4) "2800"
5) "玩家D"
6) "2100"
# 4. 查看玩家排名和分数
127.0.0.1:6379> ZREVRANK game:leaderboard "玩家D" # 排名(从0开始)
(integer) 2 # 第3名
127.0.0.1:6379> ZSCORE game:leaderboard "玩家D"
"2100"
# 5. 玩家分数更新(玩家B胜利加分)
127.0.0.1:6379> ZINCRBY game:leaderboard 500 "玩家B"
"3300"
127.0.0.1:6379> ZREVRANGE game:leaderboard 0 -1 WITHSCORES
1) "玩家B" # 上升到第1名
2) "3300"
3) "玩家C"
4) "3200"
5) "玩家D"
6) "2100"
7) "玩家A"
8) "1500"
# 6. 按分数范围查询(查询2000-3000分的玩家)
127.0.0.1:6379> ZRANGEBYSCORE game:leaderboard 2000 3000 WITHSCORES
1) "玩家D"
2) "2100"
3) "玩家C"
4) "3200" # 注意:3200分超出了范围,说明命令有误
# 正确写法:使用-inf和+inf表示无穷
127.0.0.1:6379> ZRANGEBYSCORE game:leaderboard 2000 3000 WITHSCORES
1) "玩家D"
2) "2100"
# 7. 统计不同分数段的人数
127.0.0.1:6379> ZCOUNT game:leaderboard 0 2000 # 0-2000分人数
(integer) 2
127.0.0.1:6379> ZCOUNT game:leaderboard 2001 3000 # 2001-3000分人数
(integer) 1
127.0.0.1:6379> ZCOUNT game:leaderboard 3001 5000 # 3001-5000分人数
(integer) 1
# 8. 延迟队列实现(使用时间戳作为分数)
# 添加延迟任务(计划在5分钟后执行)
127.0.0.1:6379> ZADD task:delay 1615891200 "send_email:user1001"
(integer) 1
127.0.0.1:6379> ZADD task:delay 1615891500 "send_email:user1002"
(integer) 1
127.0.0.1:6379> ZADD task:delay 1615891800 "send_email:user1003"
(integer) 1
# 查询当前时间(1615891300)之前需要执行的任务
127.0.0.1:6379> ZRANGEBYSCORE task:delay -inf 1615891300
1) "send_email:user1001"
# 9. 热门文章排行榜(根据点击量)
127.0.0.1:6379> ZADD hot:articles 1000 "article:101" 800 "article:102" 1200 "article:103"
(integer) 3
# 文章点击量增加
127.0.0.1:6379> ZINCRBY hot:articles 300 "article:101"
"1300"
# 查看热门文章Top3
127.0.0.1:6379> ZREVRANGE hot:articles 0 2 WITHSCORES
1) "article:101"
2) "1300"
3) "article:103"
4) "1200"
5) "article:102"
6) "800"
2.7 位操作(Bitmap)
2.7.1 语法知识点
| 命令 |
语法 |
说明 |
| SETBIT |
SETBIT key offset value |
设置指定偏移量的位(0或1) |
| GETBIT |
GETBIT key offset |
获取指定偏移量的位 |
| BITCOUNT |
BITCOUNT key [start end] |
统计值为1的位的数量 |
| BITOP |
BITOP operation destkey key [key ...] |
位运算(AND/OR/XOR/NOT) |
| BITPOS |
BITPOS key bit [start] [end] |
查找第一个指定值的位的位置 |
2.7.2 案例代码
bash
复制代码
# 1. 用户签到系统(一年365天,使用365个位)
# 用户1001在2024年第1天、第10天、第100天签到
127.0.0.1:6379> SETBIT user:1001:sign:2024 0 1 # 第1天签到
(integer) 0
127.0.0.1:6379> SETBIT user:1001:sign:2024 9 1 # 第10天签到
(integer) 0
127.0.0.1:6379> SETBIT user:1001:sign:2024 99 1 # 第100天签到
(integer) 0
# 2. 查询用户某天是否签到
127.0.0.1:6379> GETBIT user:1001:sign:2024 0 # 第1天
(integer) 1
127.0.0.1:6379> GETBIT user:1001:sign:2024 1 # 第2天
(integer) 0
# 3. 统计用户2024年签到总天数
127.0.0.1:6379> BITCOUNT user:1001:sign:2024
(integer) 3
# 4. 连续签到天数查询
127.0.0.1:6379> SETBIT user:1002:sign:2024 0 1
127.0.0.1:6379> SETBIT user:1002:sign:2024 1 1
127.0.0.1:6379> SETBIT user:1002:sign:2024 2 1
# 查找第一个0的位置(即断签的第一天)
127.0.0.1:6379> BITPOS user:1002:sign:2024 0
(integer) 3 # 第4天开始断签,说明连续签到了3天
# 5. 活跃用户统计(每天一个Bitmap)
# 记录2024-03-16的活跃用户(用户ID作为偏移量)
127.0.0.1:6379> SETBIT active:20240316 1001 1
(integer) 0
127.0.0.1:6379> SETBIT active:20240316 1002 1
(integer) 0
127.0.0.1:6379> SETBIT active:20240316 1005 1
(integer) 0
# 2024-03-17的活跃用户
127.0.0.1:6379> SETBIT active:20240317 1001 1
(integer) 0
127.0.0.1:6379> SETBIT active:20240317 1003 1
(integer) 0
127.0.0.1:6379> SETBIT active:20240317 1005 1
(integer) 0
# 6. 统计连续两天都活跃的用户(交集)
127.0.0.1:6379> BITOP AND active:both active:20240316 active:20240317
(integer) 126 # 返回字节长度
127.0.0.1:6379> BITCOUNT active:both # 统计连续活跃用户数
(integer) 2 # 用户1001和1005
# 7. 统计任意一天活跃过的用户(并集)
127.0.0.1:6379> BITOP OR active:either active:20240316 active:20240317
(integer) 126
127.0.0.1:6379> BITCOUNT active:either
(integer) 4 # 用户1001、1002、1003、1005
# 8. 在线状态监控(5分钟更新一次)
# 用户1001在线
127.0.0.1:6379> SETBIT online:users 1001 1
(integer) 0
# 30秒后检查用户是否还在线
127.0.0.1:6379> GETBIT online:users 1001
(integer) 1
2.8 HyperLogLog
2.8.1 语法知识点
| 命令 |
语法 |
说明 |
| PFADD |
PFADD key element [element ...] |
添加元素到HyperLogLog |
| PFCOUNT |
PFCOUNT key [key ...] |
返回基数估算值 |
| PFMERGE |
PFMERGE destkey sourcekey [sourcekey ...] |
合并多个HyperLogLog |
2.8.2 案例代码
bash
复制代码
# 1. UV统计(独立访客)- 每天只需12KB内存
# 记录今天访问网站的用户ID
127.0.0.1:6379> PFADD uv:20240316 "user:1001" "user:1002" "user:1003" "user:1001"
(integer) 1 # 重复的user:1001只记录一次
# 2. 获取今天的UV估算值
127.0.0.1:6379> PFCOUNT uv:20240316
(integer) 3 # 近似值,真实值也是3
# 3. 模拟大量数据测试
127.0.0.1:6379> PFADD uv:20240317 "user:1001" "user:1004" "user:1005" "user:1006" "user:1007" "user:1008"
(integer) 1
127.0.0.1:6379> PFCOUNT uv:20240317
(integer) 6
# 4. 合并多天的UV数据(统计月度UV)
127.0.0.1:6379> PFMERGE uv:202403 uv:20240316 uv:20240317
OK
127.0.0.1:6379> PFCOUNT uv:202403 # 合并后的独立访客数
(integer) 7 # user:1001只统计一次,共7个独立用户
# 5. 搜索引擎不同搜索词的独立用户统计
127.0.0.1:6379> PFADD search:redis "userA" "userB" "userC"
(integer) 1
127.0.0.1:6379> PFADD search:redis "userA" "userD" # 继续添加
(integer) 1
127.0.0.1:6379> PFCOUNT search:redis
(integer) 4 # 独立用户:userA、userB、userC、userD
# 6. 不同搜索词的独立用户对比
127.0.0.1:6379> PFADD search:mysql "userA" "userB" "userE"
(integer) 1
127.0.0.1:6379> PFADD search:mongodb "userC" "userF" "userG"
(integer) 1
# 统计所有搜索过的独立用户
127.0.0.1:6379> PFMERGE search:all search:redis search:mysql search:mongodb
OK
127.0.0.1:6379> PFCOUNT search:all
(integer) 7 # 所有不重复用户
2.9 GEO 数据类型
2.9.1 语法知识点
| 命令 |
语法 |
说明 |
| GEOADD |
GEOADD key longitude latitude member [longitude latitude member ...] |
添加地理位置 |
| GEOPOS |
GEOPOS key member [member ...] |
获取位置坐标 |
| GEODIST |
`GEODIST key member1 member2 [m |
km |
| GEORADIUS |
`GEORADIUS key longitude latitude radius m |
km |
| GEORADIUSBYMEMBER |
`GEORADIUSBYMEMBER key member radius m |
km |
| GEOHASH |
GEOHASH key member [member ...] |
获取位置的Geohash值 |
2.9.2 案例代码
bash
复制代码
# 1. 添加店铺位置(经度 纬度 店名)
127.0.0.1:6379> GEOADD shops 116.397128 39.916527 "天安门店" 116.327933 39.980278 "鸟巢店" 116.457951 39.872989 "国贸店"
(integer) 3
# 2. 添加更多餐饮店铺
127.0.0.1:6379> GEOADD restaurants 116.391284 39.907377 "全聚德前门店" 116.326749 39.981315 "全聚德亚运村店" 116.461087 39.913148 "海底捞国贸店"
(integer) 3
# 3. 获取店铺坐标
127.0.0.1:6379> GEOPOS restaurants "全聚德前门店"
1) 1) "116.39128494262695312"
2) "39.90737774454291936"
# 4. 计算两家店之间的距离
127.0.0.1:6379> GEODIST restaurants "全聚德前门店" "全聚德亚运村店" km
"8.3654" # 距离约8.37公里
# 5. 查找附近的人/店(LBS应用)
# 以天安门广场(116.397,39.908)为中心,查找5公里内的店铺
127.0.0.1:6379> GEORADIUS restaurants 116.397 39.908 5 km WITHCOORD WITHDIST
1) 1) "全聚德前门店"
2) "0.3166" # 距离约317米
3) 1) "116.39128494262695312"
2) "39.90737774454291936"
# 6. 查找附近餐厅,按距离排序,限制返回3条
127.0.0.1:6379> GEORADIUS restaurants 116.397 39.908 10 km WITHDIST ASC COUNT 3
1) 1) "全聚德前门店"
2) "0.3166"
2) 1) "海底捞国贸店"
2) "5.2341"
3) 1) "全聚德亚运村店"
2) "8.3654"
# 7. 以指定成员为中心查找(找全聚德前门店附近的餐馆)
127.0.0.1:6379> GEORADIUSBYMEMBER restaurants "全聚德前门店" 3 km WITHDIST
1) 1) "全聚德前门店"
2) "0.0000" # 自身距离为0
2) 1) "海底捞国贸店"
2) "2.9183"
# 8. 获取Geohash值(可用于索引和缓存)
127.0.0.1:6379> GEOHASH restaurants "全聚德前门店"
1) "wx4g0r7k230"
# 9. 找附近的人(社交应用)
127.0.0.1:6379> GEOADD users 116.398 39.909 "user:1001" 116.410 39.920 "user:1002" 116.330 39.980 "user:1003"
(integer) 3
# 查找user:1001附近5公里内的用户
127.0.0.1:6379> GEORADIUSBYMEMBER users "user:1001" 5 km WITHDIST
1) 1) "user:1001"
2) "0.0000"
2) 1) "user:1002"
2) "1.8234" # 约1.82公里
通过本章的学习,你应该能够熟练掌握 Redis 的各种数据结构,并能根据实际业务场景选择合适的数据类型。每种数据类型都有其独特的优势和适用场景:
- String:缓存、计数器、分布式锁
- Hash:对象存储、购物车
- List:消息队列、时间线、栈
- Set:标签系统、社交关系、抽奖
- ZSet:排行榜、延迟队列、优先级队列
- Bitmap:签到统计、活跃用户、布隆过滤器
- HyperLogLog:UV统计、基数估算
- GEO:LBS应用、附近的人、位置服务
在实际开发中,合理选择数据结构可以极大地提升系统性能和开发效率。