赞
踩
Redis 是一个高性能的键值存储,它支持多种丰富的数据类型。每种数据类型都有其特定的用途和底层实现。下面我将介绍 Redis 支持的主要数据类型及其背后的数据结构。
本人这里还有几篇详细的Redis用法文章,可以用来进阶康康!
字符串是 Redis 中最基本的数据类型,可以是文本、数字、二进制数据等。它在 Redis 内部是以动态字符串 (SDS, Simple Dynamic String) 实现的。SDS 除了字符串本身,还有额外的属性来缓存长度。
Jedis jedis = new Jedis("localhost");
jedis.set("key", "value"); // 设置键值对
String value = jedis.get("key"); // 获取值
System.out.println(value);
jedis.close();
哈希是键值对的集合,适合表示对象的属性。最常用的是用来存储对象。内部实现主要有两种:
Jedis jedis = new Jedis("localhost");
jedis.hset("user:1000", "name", "John");
jedis.hset("user:1000", "age", "30");
String name = jedis.hget("user:1000", "name");
System.out.println(name);
jedis.close();
列表是一组有序的字符串,可以从列表的头部或尾部添加或删除元素。内部实现有以下两种:
Jedis jedis = new Jedis("localhost");
jedis.lpush("tasks", "Task1"); // 从头部插入
jedis.rpush("tasks", "Task2"); // 从尾部插入
String task = jedis.lpop("tasks"); // 从头部弹出
System.out.println(task);
jedis.close();
集合是无序且唯一的字符串集合。内部采用哈希表 (hashtable) 实现,哈希表的键是集合的值,值是 null。
Jedis jedis = new Jedis("localhost");
jedis.sadd("tags", "java");
jedis.sadd("tags", "redis");
jedis.sadd("tags", "java"); // 不会重复添加
Set<String> tags = jedis.smembers("tags");
System.out.println(tags);
jedis.close();
有序集合类似集合,但每个元素都会关联一个分数(score),元素按分数排序。内部使用一种结构:跳表 (skiplist) 和哈希表 (hashtable) 结合。
Jedis jedis = new Jedis("localhost");
jedis.zadd("leaderboard", 100, "Player1");
jedis.zadd("leaderboard", 200, "Player2");
Set<String> topPlayers = jedis.zrange("leaderboard", 0, -1); // 按分数排序获取元素
System.out.println(topPlayers);
jedis.close();
位图将字符串值视为一个位数组,可以进行位操作。内部存储采用字符串,最大长度可达 512 MB。
Jedis jedis = new Jedis("localhost");
jedis.setbit("user:active", 1, true); // 设置第2位为1
boolean isActive = jedis.getbit("user:active", 1);
System.out.println(isActive);
jedis.close();
HyperLogLog 是基数估算数据结构,用于估算不重复元素的数量。利用概率算法实现,误差率约 0.81%。用于大规模数据去重计数。
Jedis jedis = new Jedis("localhost");
jedis.pfadd("unique_visitors", "user1");
jedis.pfadd("unique_visitors", "user2");
jedis.pfadd("unique_visitors", "user1");
long uvCount = jedis.pfcount("unique_visitors");
System.out.println(uvCount);
jedis.close();
Redis 的地理空间扩展允许存储地理坐标,并提供地理范围查询、距离计算等功能。内部使用有序集合 (Sorted Set) 数据类型实现,利用 GeoHash 编码。
Jedis jedis = new Jedis("localhost");
jedis.geoadd("locations", 13.361389, 38.115556, "Palermo");
jedis.geoadd("locations", 15.087269, 37.502669, "Catania");
List<GeoCoordinate> coordinates = jedis.geopos("locations", "Palermo");
System.out.println(coordinates);
double distance = jedis.geodist("locations", "Palermo", "Catania", GeoUnit.KM);
System.out.println(distance);
jedis.close();
Redis 提供了丰富的数据结构,每种数据结构都有其特定的应用场景和优势。在实践中,可以根据具体需求选择合适的数据类型,提高系统性能与效率。了解这些数据结构的底层实现有助于更好地理解和使用 Redis,优化数据存储和操作。
另(面试突出加分项):
跳表(SkipList)是一种用于有序集合的高效数据结构,支持快速的插入、删除和查找操作。它的时间复杂度为 O(log N),接近于平衡二叉树,但实现相对简单。跳表是 Redis 中 Sorted Set(有序集合)数据类型的主要底层实现之一。
跳表由多层有序链表组成,下层链表包含所有元素,每一层链表是其下一层链表的抽样。跳表的最底层是一条完整的有序链表,从头到尾包含所有的元素。每一层往上减少一部分元素,通过跳过一些元素使操作更高效。
每个节点包含多个指针,每个指针指向该层中的下一个节点。节点结构如下:
跳表支持插入、删除和查找操作,具体步骤如下:
以下是 Redis 跳表的数据结构和主要操作的实现:
typedef struct zskiplistNode {
struct zrobj *obj; // 存储成员对象(value)
double score; // 分数,用于排序
struct zskiplistNode *backward; // 后退指针
struct zskiplistLevel {
struct zskiplistNode *forward; // 前进指针
unsigned int span; // 跨度
} level[]; // 层指针数组
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail; // 头尾指针
unsigned long length; // 节点数量
int level; // 当前最大层数
} zskiplist;
zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj) { // 临时存储每一层的前一个节点 zskiplistNode *update[ZSKIPLIST_MAXLEVEL]; zskiplistNode *x; int i, level; x = zsl->header; for (i = zsl->level-1; i >= 0; i--) { while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && compareStringObjects(x->level[i].forward->obj, obj) < 0))) { x = x->level[i].forward; } update[i] = x; } level = zslRandomLevel(); // 随机生成节点层数 if (level > zsl->level) { for (i = zsl->level; i < level; i++) { update[i] = zsl->header; } zsl->level = level; } x = zslCreateNode(level, score, obj); // 创建新节点 for (i = 0; i < level; i++) { x->level[i].forward = update[i]->level[i].forward; update[i]->level[i].forward = x; } x->backward = (update[0] == zsl->header ? NULL : update[0]); if (x->level[0].forward) x->level[0].forward->backward = x; else zsl->tail = x; zsl->length++; return x; }
int zslDelete(zskiplist *zsl, double score, robj *obj) { zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; int i; x = zsl->header; for (i = zsl->level-1; i >= 0; i--) { while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && compareStringObjects(x->level[i].forward->obj, obj) < 0))) { x = x->level[i].forward; } update[i] = x; } x = x->level[0].forward; if (x && score == x->score && compareStringObjects(x->obj, obj) == 0) { for (i = 0; i < zsl->level; i++) { if (update[i]->level[i].forward == x) { update[i]->level[i].forward = x->level[i].forward; } } if (x->level[0].forward) { x->level[0].forward->backward = x->backward; } else { zsl->tail = x->backward; } while (zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL) { zsl->level--; } zsl->length--; zslFreeNode(x); return 1; } return 0; }
zskiplistNode *zslFind(zskiplist *zsl, double score, robj *obj) { zskiplistNode *x = zsl->header; int i; for (i = zsl->level-1; i >= 0; i--) { while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && compareStringObjects(x->level[i].forward->obj, obj) < 0))) { x = x->level[i].forward; } } x = x->level[0].forward; if (x && score == x->score && compareStringObjects(x->obj, obj) == 0) { return x; } return NULL; }
跳表主要用于 Redis 的有序集合 (Sorted Set) 数据类型。通过跳表,可以高效实现按分数排序的多种操作,如范围查询、排名查询等。它与哈希表结合使用,实现了元素的快速访问和分数范围内的高效查询。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。