Redis 数据结构详解 什么是 Redis? Redis(Remote Dictionary Server)是一个开源的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。Redis 支持多种数据结构,这使得它非常灵活和强大。
Redis 支持的数据结构 Redis 支持以下主要数据结构:
字符串(String)
列表(List)
集合(Set)
有序集合(Sorted Set / ZSet)
哈希表(Hash)
位图(Bitmap)
HyperLogLog
地理空间索引(Geospatial Index)
详细介绍每个数据结构 1. 字符串(String) 字符串是 Redis 最基本的数据结构,可以存储任何类型的字符串,包括二进制数据。单个字符串的最大长度为 512 MB。
常用命令及时间复杂度
命令
功能
时间复杂度
SET key value
设置键值对
O(1)
GET key
获取值
O(1)
INCR key
自增 1
O(1)
INCRBY key increment
自增指定值
O(1)
DECR key
自减 1
O(1)
DECRBY key decrement
自减指定值
O(1)
APPEND key value
追加字符串
O(1)
STRLEN key
获取字符串长度
O(1)
GETRANGE key start end
获取子字符串
O(1)
SETRANGE key offset value
设置子字符串
O(1)
使用场景
2. 列表(List) Redis 列表是简单的字符串列表,按照插入顺序排序。你可以在列表的头部或尾部添加元素。
常用命令及时间复杂度
命令
功能
时间复杂度
LPUSH key value [value ...]
从左侧插入元素
O(1) per element
RPUSH key value [value ...]
从右侧插入元素
O(1) per element
LPOP key
从左侧弹出元素
O(1)
RPOP key
从右侧弹出元素
O(1)
LLEN key
获取列表长度
O(1)
LRANGE key start stop
获取指定范围的元素
O(S+N),S 是偏移量,N 是元素数量
LINDEX key index
获取指定索引的元素
O(N),N 是索引位置
LSET key index value
设置指定索引的元素
O(N),N 是索引位置
LREM key count value
删除指定值的元素
O(N)
LTRIM key start stop
保留指定范围的元素
O(N)
使用场景
消息队列
最新消息列表
任务队列
排行榜(简单版)
3. 集合(Set) Redis 集合是字符串的无序集合,集合中的元素是唯一的,不允许重复。
常用命令及时间复杂度
命令
功能
时间复杂度
SADD key member [member ...]
添加元素
O(1) per member
SREM key member [member ...]
删除元素
O(1) per member
SMEMBERS key
获取所有元素
O(N),N 是集合大小
SISMEMBER key member
检查元素是否存在
O(1)
SCARD key
获取集合大小
O(1)
SPOP key [count]
随机弹出元素
O(1)
SRANDMEMBER key [count]
随机获取元素
O(1)
SINTER key [key ...]
交集
O(N),N 是所有集合的大小之和
SUNION key [key ...]
并集
O(N),N 是所有集合的大小之和
SDIFF key [key ...]
差集
O(N),N 是所有集合的大小之和
使用场景
标签系统
去重
好友/关注列表
抽奖系统
交集/并集/差集操作
4. 有序集合(Sorted Set / ZSet) Redis 有序集合与集合类似,也是唯一元素的集合,但每个元素都关联了一个分数(score),Redis 根据分数对元素进行排序。
常用命令及时间复杂度
命令
功能
时间复杂度
ZADD key score member [score member ...]
添加元素及分数
O(log N) per member
ZREM key member [member ...]
删除元素
O(log N) per member
ZSCORE key member
获取元素的分数
O(1)
ZINCRBY key increment member
增加元素的分数
O(log N)
ZCARD key
获取集合大小
O(1)
ZRANGE key start stop [WITHSCORES]
获取指定范围的元素(按分数升序)
O(log N + M),M 是返回的元素数量
ZREVRANGE key start stop [WITHSCORES]
获取指定范围的元素(按分数降序)
O(log N + M)
ZRANK key member
获取元素的排名(按分数升序)
O(log N)
ZREVRANK key member
获取元素的排名(按分数降序)
O(log N)
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
按分数范围获取元素
O(log N + M)
ZCOUNT key min max
统计分数范围内的元素数量
O(log N)
使用场景
排行榜(如游戏分数、网站访问量)
带权重的队列
范围查询
时间序列数据
5. 哈希表(Hash) Redis 哈希表是键值对的集合,适合存储对象。每个哈希表可以存储多个字段和值。
常用命令及时间复杂度
命令
功能
时间复杂度
HSET key field value [field value ...]
设置字段值
O(1) per field
HGET key field
获取字段值
O(1)
HMSET key field value [field value ...]
设置多个字段值
O(1) per field
HMGET key field [field ...]
获取多个字段值
O(1) per field
HGETALL key
获取所有字段和值
O(N),N 是字段数量
HDEL key field [field ...]
删除字段
O(1) per field
HEXISTS key field
检查字段是否存在
O(1)
HLEN key
获取字段数量
O(1)
HINCRBY key field increment
增加字段的整数值
O(1)
HINCRBYFLOAT key field increment
增加字段的浮点数值
O(1)
HKEYS key
获取所有字段名
O(N)
HVALS key
获取所有字段值
O(N)
使用场景
存储用户信息
存储对象
配置信息
商品信息
计数器
6. 位图(Bitmap) Redis 位图是一种特殊的字符串,它允许你对字符串的单个位进行操作。
常用命令及时间复杂度
命令
功能
时间复杂度
SETBIT key offset value
设置指定位置的位
O(1)
GETBIT key offset
获取指定位置的位
O(1)
BITCOUNT key [start end]
统计设置为 1 的位的数量
O(N)
BITOP operation destkey key [key ...]
对多个位图执行位操作
O(N)
BITPOS key bit [start] [end]
查找第一个设置为指定值的位
O(N)
使用场景
用户在线状态
打卡记录
布隆过滤器
权限控制
统计活跃用户
7. HyperLogLog HyperLogLog 是一种概率性数据结构,用于估计集合的基数(元素数量),具有极低的内存消耗。
常用命令及时间复杂度
命令
功能
时间复杂度
PFADD key element [element ...]
添加元素
O(1) per element
PFCOUNT key [key ...]
估计基数
O(1)
PFMERGE destkey sourcekey [sourcekey ...]
合并多个 HyperLogLog
O(N),N 是 HyperLogLog 的数量
使用场景
网站访问量统计(UV)
独立用户数统计
搜索词统计
大数据集的基数估计
8. 地理空间索引(Geospatial Index) Redis 地理空间索引用于存储地理位置信息,并支持基于地理位置的查询。
常用命令及时间复杂度
命令
功能
时间复杂度
GEOADD key longitude latitude member [longitude latitude member ...]
添加地理位置
O(log N) per member
GEODIST key member1 member2 [unit]
计算两个位置之间的距离
O(log N)
GEOHASH key member [member ...]
获取位置的 Geohash 编码
O(log N) per member
GEOPOS key member [member ...]
获取位置的坐标
O(log N) per member
`GEORADIUS key longitude latitude radius m
km
ft
`GEORADIUSBYMEMBER key member radius m
km
ft
使用场景
附近的人/地点
地理位置搜索
地图应用
基于位置的服务
性能优化建议
选择合适的数据结构 :根据具体的使用场景选择最合适的数据结构,以获得最佳性能。
合理设置过期时间 :对于缓存数据,设置合理的过期时间,避免内存占用过高。
批量操作 :使用批量命令(如 MSET、HMSET 等)减少网络往返时间。
管道操作 :使用管道(Pipeline)批量执行命令,提高吞吐量。
避免大键 :避免在一个键中存储过多数据,如大型列表或哈希表。
使用分片 :对于大规模应用,考虑使用 Redis 集群进行数据分片。
监控内存使用 :定期监控 Redis 的内存使用情况,及时发现和解决内存问题。
使用合适的序列化方式 :对于复杂对象,选择高效的序列化方式,如 Protocol Buffers 或 MessagePack。
优化命令执行 :避免在循环中执行 Redis 命令,尽量使用批量操作。
合理使用持久化 :根据业务需求选择合适的持久化方式(RDB 或 AOF)。
数据结构选择指南
场景
推荐数据结构
原因
简单键值存储
String
最基本的数据结构,操作简单高效
计数器
String
INCR/DECR 命令原子操作,性能优秀
消息队列
List
LPUSH/RPOP 操作,符合队列特性
最新消息
List
LPUSH + LRANGE,保持最新消息
唯一元素集合
Set
自动去重,高效的成员检查
好友关系
Set
支持交集、并集、差集操作
排行榜
Sorted Set
自动排序,支持分数范围查询
带权重的任务
Sorted Set
可以根据权重排序
存储对象
Hash
字段值对,适合存储结构化数据
用户信息
Hash
可以只修改部分字段,节省带宽
在线状态
Bitmap
占用空间小,位操作高效
访问量统计
HyperLogLog
极低内存消耗,适合大数据集
地理位置
Geospatial Index
专门的地理位置查询功能
总结 Redis 提供了丰富多样的数据结构,每种数据结构都有其特定的使用场景和优势。通过合理选择和使用这些数据结构,可以显著提高应用的性能和可扩展性。
Redis 的数据结构设计体现了其”简单而强大”的理念,通过提供基本但灵活的数据结构,Redis 能够适应各种复杂的应用场景。无论是作为缓存、数据库还是消息中间件,Redis 都能以其高性能、低延迟的特点为应用提供强大的支持。
掌握 Redis 的数据结构及其使用方法,对于构建高性能、可伸缩的应用系统至关重要。希望本文能帮助你更好地理解和使用 Redis 的数据结构。
多语言使用示例 Python 使用示例 Python 中使用 Redis 通常通过 redis-py 库,这是 Python 中最流行的 Redis 客户端。
安装依赖
字符串操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import redisr = redis.Redis(host='localhost' , port=6379 , db=0 ) r.set ('name' , 'John' ) name = r.get('name' ) print (f'Name: {name.decode()} ' ) r.set ('counter' , 10 ) r.incr('counter' ) counter = r.get('counter' ) print (f'Counter: {counter.decode()} ' )
列表操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 r.delete('mylist' ) r.rpush('mylist' , 'a' , 'b' , 'c' ) r.lpush('mylist' , 'x' , 'y' , 'z' ) length = r.llen('mylist' ) print (f'List length: {length} ' ) items = r.lrange('mylist' , 0 , -1 ) print ('List items:' )for item in items: print (item.decode())
集合操作 1 2 3 4 5 6 7 8 9 10 11 12 13 r.delete('myset' ) r.sadd('myset' , 'a' , 'b' , 'c' , 'd' ) size = r.scard('myset' ) print (f'Set size: {size} ' ) exists = r.sismember('myset' , 'a' ) print (f'Is \'a\' exists: {exists} ' )
有序集合操作 1 2 3 4 5 6 7 8 9 10 11 r.delete('myzset' ) r.zadd('myzset' , {'a' : 1 , 'b' : 2 , 'c' : 3 , 'd' : 4 , 'e' : 5 }) print ('Elements in descending order:' )elements = r.zrevrange('myzset' , 0 , -1 , withscores=True ) for element, score in elements: print (f'{element.decode()} : {score} ' )
哈希表操作 1 2 3 4 5 6 7 8 9 10 11 12 13 r.delete('user:1001' ) r.hset('user:1001' , 'name' , 'John' ) r.hset('user:1001' , 'age' , 30 ) r.hset('user:1001' , 'email' , 'john@example.com' ) all_fields = r.hgetall('user:1001' ) print ('All fields and values:' )for field, value in all_fields.items(): print (f'{field.decode()} : {value.decode()} ' )
Java 使用示例 Java 中使用 Redis 通常通过 Jedis 库,这是 Java 中最流行的 Redis 客户端之一。
安装依赖 Maven 依赖:
1 2 3 4 5 <dependency > <groupId > redis.clients</groupId > <artifactId > jedis</artifactId > <version > 3.7.0</version > </dependency >
字符串操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import redis.clients.jedis.Jedis;public class RedisStringExample { public static void main (String[] args) { Jedis jedis = new Jedis ("localhost" , 6379 ); jedis.set("name" , "John" ); String name = jedis.get("name" ); System.out.println("Name: " + name); jedis.set("counter" , "10" ); jedis.incr("counter" ); String counter = jedis.get("counter" ); System.out.println("Counter: " + counter); jedis.close(); } }
列表操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import redis.clients.jedis.Jedis;import java.util.List;public class RedisListExample { public static void main (String[] args) { Jedis jedis = new Jedis ("localhost" , 6379 ); jedis.del("mylist" ); jedis.rpush("mylist" , "a" , "b" , "c" ); jedis.lpush("mylist" , "x" , "y" , "z" ); long length = jedis.llen("mylist" ); System.out.println("List length: " + length); List<String> items = jedis.lrange("mylist" , 0 , -1 ); System.out.println("List items:" ); for (String item : items) { System.out.println(item); } jedis.close(); } }
集合操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import redis.clients.jedis.Jedis;import java.util.Set;public class RedisSetExample { public static void main (String[] args) { Jedis jedis = new Jedis ("localhost" , 6379 ); jedis.del("myset" ); jedis.sadd("myset" , "a" , "b" , "c" , "d" ); long size = jedis.scard("myset" ); System.out.println("Set size: " + size); boolean exists = jedis.sismember("myset" , "a" ); System.out.println("Is 'a' exists: " + exists); jedis.close(); } }
有序集合操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import redis.clients.jedis.Jedis;import java.util.Set;public class RedisSortedSetExample { public static void main (String[] args) { Jedis jedis = new Jedis ("localhost" , 6379 ); jedis.del("myzset" ); jedis.zadd("myzset" , 1 , "a" ); jedis.zadd("myzset" , 2 , "b" ); jedis.zadd("myzset" , 3 , "c" ); jedis.zadd("myzset" , 4 , "d" ); jedis.zadd("myzset" , 5 , "e" ); System.out.println("Elements in descending order:" ); Set<String> elementsDesc = jedis.zrevrange("myzset" , 0 , -1 ); for (String element : elementsDesc) { Double s = jedis.zscore("myzset" , element); System.out.println(element + ": " + s); } jedis.close(); } }
哈希表操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import redis.clients.jedis.Jedis;import java.util.Map;public class RedisHashExample { public static void main (String[] args) { Jedis jedis = new Jedis ("localhost" , 6379 ); jedis.del("user:1001" ); jedis.hset("user:1001" , "name" , "John" ); jedis.hset("user:1001" , "age" , "30" ); jedis.hset("user:1001" , "email" , "john@example.com" ); Map<String, String> allFields = jedis.hgetAll("user:1001" ); System.out.println("All fields and values:" ); for (Map.Entry<String, String> entry : allFields.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } jedis.close(); } }
C# 使用示例 C# 中使用 Redis 通常通过 StackExchange.Redis 库,这是 C# 中最流行的 Redis 客户端。
安装依赖 NuGet 包:
1 Install-Package StackExchange.Redis
字符串操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using StackExchange.Redis;using System;class RedisStringExample { static void Main () { ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379" ); IDatabase db = redis.GetDatabase(); db.StringSet("name" , "John" ); string name = db.StringGet("name" ); Console.WriteLine("Name: " + name); db.StringSet("counter" , "10" ); db.StringIncrement("counter" ); string counter = db.StringGet("counter" ); Console.WriteLine("Counter: " + counter); redis.Close(); } }
列表操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 using StackExchange.Redis;using System;class RedisListExample { static void Main () { ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379" ); IDatabase db = redis.GetDatabase(); db.KeyDelete("mylist" ); db.ListRightPush("mylist" , "a" ); db.ListRightPush("mylist" , "b" ); db.ListRightPush("mylist" , "c" ); db.ListLeftPush("mylist" , "x" ); db.ListLeftPush("mylist" , "y" ); db.ListLeftPush("mylist" , "z" ); long length = db.ListLength("mylist" ); Console.WriteLine("List length: " + length); RedisValue[] items = db.ListRange("mylist" , 0 , -1 ); Console.WriteLine("List items:" ); foreach (var item in items) { Console.WriteLine(item); } redis.Close(); } }
集合操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 using StackExchange.Redis;using System;class RedisSetExample { static void Main () { ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379" ); IDatabase db = redis.GetDatabase(); db.KeyDelete("myset" ); db.SetAdd("myset" , "a" ); db.SetAdd("myset" , "b" ); db.SetAdd("myset" , "c" ); db.SetAdd("myset" , "d" ); long size = db.SetLength("myset" ); Console.WriteLine("Set size: " + size); bool exists = db.SetContains("myset" , "a" ); Console.WriteLine("Is 'a' exists: " + exists); redis.Close(); } }
有序集合操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 using StackExchange.Redis;using System;class RedisSortedSetExample { static void Main () { ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379" ); IDatabase db = redis.GetDatabase(); db.KeyDelete("myzset" ); db.SortedSetAdd("myzset" , "a" , 1 ); db.SortedSetAdd("myzset" , "b" , 2 ); db.SortedSetAdd("myzset" , "c" , 3 ); db.SortedSetAdd("myzset" , "d" , 4 ); db.SortedSetAdd("myzset" , "e" , 5 ); Console.WriteLine("Elements in descending order:" ); SortedSetEntry[] elementsDesc = db.SortedSetRangeByRankWithScores("myzset" , 0 , -1 , Order.Descending); foreach (var entry in elementsDesc) { Console.WriteLine(entry.Element + ": " + entry.Score); } redis.Close(); } }
哈希表操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using StackExchange.Redis;using System;class RedisHashExample { static void Main () { ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379" ); IDatabase db = redis.GetDatabase(); db.KeyDelete("user:1001" ); db.HashSet("user:1001" , "name" , "John" ); db.HashSet("user:1001" , "age" , "30" ); db.HashSet("user:1001" , "email" , "john@example.com" ); HashEntry[] allFields = db.HashGetAll("user:1001" ); Console.WriteLine("All fields and values:" ); foreach (var entry in allFields) { Console.WriteLine(entry.Name + ": " + entry.Value); } redis.Close(); } }
多语言客户端对比
语言
推荐客户端
优势
Python
redis-py
轻量级,API 友好,支持管道操作
Java
Jedis
简单直接,功能完整,广泛使用
C#
StackExchange.Redis
性能优秀,支持异步操作,线程安全
客户端选择建议
根据项目语言选择 :选择与项目开发语言匹配的客户端库
考虑性能需求 :对于高并发场景,选择性能更好的客户端
功能需求 :根据项目需要的功能选择支持相应特性的客户端
社区活跃度 :选择社区活跃、维护良好的客户端库
文档完善度 :选择文档完善、使用示例丰富的客户端库
通过本文的介绍和示例,相信你已经对 Redis 的数据结构及其在不同编程语言中的使用有了更全面的了解。在实际项目中,根据具体需求选择合适的数据结构和客户端库,将能够充分发挥 Redis 的性能优势,构建高性能、可扩展的应用系统。