ToB企服应用市场:ToB评测及商务社交产业平台

标题: Redis的八种数据类型介绍 [打印本页]

作者: 缠丝猫    时间: 2024-9-25 21:37
标题: Redis的八种数据类型介绍
Redis 是一个高性能的键值存储,它支持多种丰富的数据类型。每种数据类型都有其特定的用途和底层实现。下面我将介绍 Redis 支持的主要数据类型及其背后的数据结构。
本人这里还有几篇详细的Redis用法文章,可以用来进阶康康!
1. 字符串 (String)

数据结构

字符串是 Redis 中最基本的数据类型,可以是文本、数字、二进制数据等。它在 Redis 内部是以动态字符串 (SDS, Simple Dynamic String) 实现的。SDS 除了字符串本身,还有额外的属性来缓存长度。

用法示例

  1. Jedis jedis = new Jedis("localhost");
  2. jedis.set("key", "value");  // 设置键值对
  3. String value = jedis.get("key");  // 获取值
  4. System.out.println(value);
  5. jedis.close();
复制代码
2. 哈希 (Hash)

数据结构

哈希是键值对的集合,得当表现对象的属性。最常用的是用来存储对象。内部实现主要有两种:

用法示例

  1. Jedis jedis = new Jedis("localhost");
  2. jedis.hset("user:1000", "name", "John");
  3. jedis.hset("user:1000", "age", "30");
  4. String name = jedis.hget("user:1000", "name");
  5. System.out.println(name);
  6. jedis.close();
复制代码
3. 列表 (List)

数据结构

列表是一组有序的字符串,可以从列表的头部或尾部添加或删除元素。内部实现有以下两种:

用法示例

  1. Jedis jedis = new Jedis("localhost");
  2. jedis.lpush("tasks", "Task1");  // 从头部插入
  3. jedis.rpush("tasks", "Task2");  // 从尾部插入
  4. String task = jedis.lpop("tasks");  // 从头部弹出
  5. System.out.println(task);
  6. jedis.close();
复制代码
4. 集合 (Set)

数据结构

集合是无序且唯一的字符串集合。内部采用哈希表 (hashtable) 实现,哈希表的键是集合的值,值是 null。
用法示例

  1. Jedis jedis = new Jedis("localhost");
  2. jedis.sadd("tags", "java");
  3. jedis.sadd("tags", "redis");
  4. jedis.sadd("tags", "java");  // 不会重复添加
  5. Set<String> tags = jedis.smembers("tags");
  6. System.out.println(tags);
  7. jedis.close();
复制代码
5. 有序集合 (Sorted Set)

数据结构

有序集合雷同集合,但每个元素都会关联一个分数(score),元素按分数排序。内部使用一种结构:跳表 (skiplist) 和哈希表 (hashtable) 结合。

用法示例

  1. Jedis jedis = new Jedis("localhost");
  2. jedis.zadd("leaderboard", 100, "Player1");
  3. jedis.zadd("leaderboard", 200, "Player2");
  4. Set<String> topPlayers = jedis.zrange("leaderboard", 0, -1);  // 按分数排序获取元素
  5. System.out.println(topPlayers);
  6. jedis.close();
复制代码
6. 位图 (Bitmap)

数据结构

位图将字符串值视为一个位数组,可以进行位操作。内部存储采用字符串,最大长度可达 512 MB。

用法示例

  1. Jedis jedis = new Jedis("localhost");
  2. jedis.setbit("user:active", 1, true);  // 设置第2位为1
  3. boolean isActive = jedis.getbit("user:active", 1);
  4. System.out.println(isActive);
  5. jedis.close();
复制代码
7. HyperLogLog

数据结构

HyperLogLog 是基数估算数据结构,用于估算不重复元素的数量。利用概率算法实现,毛病率约 0.81%。用于大规模数据去重计数。

用法示例

  1. Jedis jedis = new Jedis("localhost");
  2. jedis.pfadd("unique_visitors", "user1");
  3. jedis.pfadd("unique_visitors", "user2");
  4. jedis.pfadd("unique_visitors", "user1");
  5. long uvCount = jedis.pfcount("unique_visitors");
  6. System.out.println(uvCount);
  7. jedis.close();
复制代码
8. 地理空间 (Geospatial)

数据结构

Redis 的地理空间扩展答应存储地理坐标,并提供地理范围查询、间隔计算等功能。内部使用有序集合 (Sorted Set) 数据类型实现,利用 GeoHash 编码。

用法示例

  1. Jedis jedis = new Jedis("localhost");
  2. jedis.geoadd("locations", 13.361389, 38.115556, "Palermo");
  3. jedis.geoadd("locations", 15.087269, 37.502669, "Catania");
  4. List<GeoCoordinate> coordinates = jedis.geopos("locations", "Palermo");
  5. System.out.println(coordinates);
  6. double distance = jedis.geodist("locations", "Palermo", "Catania", GeoUnit.KM);
  7. System.out.println(distance);
  8. jedis.close();
复制代码
结论

Redis 提供了丰富的数据结构,每种数据结构都有其特定的应用场景和优势。在实践中,可以根据详细需求选择符合的数据类型,进步体系性能与效率。了解这些数据结构的底层实现有助于更好地明白和使用 Redis,优化数据存储和操作。
另(口试突出加分项):
跳表(SkipList)是一种用于有序集合的高效数据结构,支持快速的插入、删除和查找操作。它的时间复杂度为 O(log N),靠近于平衡二叉树,但实现相对简朴。跳表是 Redis 中 Sorted Set(有序集合)数据类型的主要底层实现之一。

跳表的数据结构

跳表由多层有序链表组成,下层链表包含全部元素,每一层链表是其下一层链表的抽样。跳表的最底层是一条完整的有序链表,从头至尾包含全部的元素。每一层往上淘汰一部分元素,通过跳过一些元素使操作更高效。

跳表的节点

每个节点包含多个指针,每个指针指向该层中的下一个节点。节点结构如下:

跳表的操作

跳表支持插入、删除和查找操作,详细步骤如下:
插入操作

删除操作

查找操作

跳表在 Redis 中的实现

以下是 Redis 跳表的数据结构和主要操作的实现:
节点结构

  1. typedef struct zskiplistNode {
  2.     struct zrobj *obj; // 存储成员对象(value)
  3.     double score;      // 分数,用于排序
  4.     struct zskiplistNode *backward; // 后退指针
  5.     struct zskiplistLevel {
  6.         struct zskiplistNode *forward; // 前进指针
  7.         unsigned int span;             // 跨度
  8.     } level[]; // 层指针数组
  9. } zskiplistNode;
复制代码
跳表结构

  1. typedef struct zskiplist {
  2.     struct zskiplistNode *header, *tail; // 头尾指针
  3.     unsigned long length;                // 节点数量
  4.     int level;                           // 当前最大层数
  5. } zskiplist;
复制代码
插入操作

  1. zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj) {
  2.     // 临时存储每一层的前一个节点
  3.     zskiplistNode *update[ZSKIPLIST_MAXLEVEL];
  4.     zskiplistNode *x;
  5.     int i, level;
  6.    
  7.     x = zsl->header;
  8.     for (i = zsl->level-1; i >= 0; i--) {
  9.         while (x->level[i].forward &&
  10.                (x->level[i].forward->score < score ||
  11.                (x->level[i].forward->score == score &&
  12.                 compareStringObjects(x->level[i].forward->obj, obj) < 0))) {
  13.             x = x->level[i].forward;
  14.         }
  15.         update[i] = x;
  16.     }
  17.    
  18.     level = zslRandomLevel(); // 随机生成节点层数
  19.     if (level > zsl->level) {
  20.         for (i = zsl->level; i < level; i++) {
  21.             update[i] = zsl->header;
  22.         }
  23.         zsl->level = level;
  24.     }
  25.     x = zslCreateNode(level, score, obj); // 创建新节点
  26.     for (i = 0; i < level; i++) {
  27.         x->level[i].forward = update[i]->level[i].forward;
  28.         update[i]->level[i].forward = x;
  29.     }
  30.     x->backward = (update[0] == zsl->header ? NULL : update[0]);
  31.     if (x->level[0].forward)
  32.         x->level[0].forward->backward = x;
  33.     else
  34.         zsl->tail = x;
  35.     zsl->length++;
  36.     return x;
  37. }
复制代码
删除操作

  1. int zslDelete(zskiplist *zsl, double score, robj *obj) {
  2.     zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
  3.     int i;
  4.    
  5.     x = zsl->header;
  6.     for (i = zsl->level-1; i >= 0; i--) {
  7.         while (x->level[i].forward &&
  8.                (x->level[i].forward->score < score ||
  9.                (x->level[i].forward->score == score &&
  10.                 compareStringObjects(x->level[i].forward->obj, obj) < 0))) {
  11.             x = x->level[i].forward;
  12.         }
  13.         update[i] = x;
  14.     }
  15.     x = x->level[0].forward;
  16.     if (x && score == x->score && compareStringObjects(x->obj, obj) == 0) {
  17.         for (i = 0; i < zsl->level; i++) {
  18.             if (update[i]->level[i].forward == x) {
  19.                 update[i]->level[i].forward = x->level[i].forward;
  20.             }
  21.         }
  22.         if (x->level[0].forward) {
  23.             x->level[0].forward->backward = x->backward;
  24.         } else {
  25.             zsl->tail = x->backward;
  26.         }
  27.         while (zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL) {
  28.             zsl->level--;
  29.         }
  30.         zsl->length--;
  31.         zslFreeNode(x);
  32.         return 1;
  33.     }
  34.     return 0;
  35. }
复制代码
查找操作

  1. zskiplistNode *zslFind(zskiplist *zsl, double score, robj *obj) {
  2.     zskiplistNode *x = zsl->header;
  3.     int i;
  4.     for (i = zsl->level-1; i >= 0; i--) {
  5.         while (x->level[i].forward &&
  6.                (x->level[i].forward->score < score ||
  7.                (x->level[i].forward->score == score &&
  8.                 compareStringObjects(x->level[i].forward->obj, obj) < 0))) {
  9.             x = x->level[i].forward;
  10.         }
  11.     }
  12.     x = x->level[0].forward;
  13.     if (x && score == x->score && compareStringObjects(x->obj, obj) == 0) {
  14.         return x;
  15.     }
  16.     return NULL;
  17. }
复制代码
跳表的长处

在 Redis 中的应用

跳表主要用于 Redis 的有序集合 (Sorted Set) 数据类型。通过跳表,可以高效实现按分数排序的多种操作,如范围查询、排名查询等。它与哈希表结合使用,实现了元素的快速访问和分数范围内的高效查询。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4