G-Set(增长集合,Grow-Only Set)

一、概念

G-Set(增长集合,Grow-Only Set)是一种冲突自由复制数据类型(Conflict-Free Replicated Data Type, CRDT),用于在分布式系统中同步和合并数据,而不需要中央协调器。G-Set 支持两种操作:添加(add)和查询(query)。一旦元素被添加到 G-Set 中,它就不能被删除,这就是为什么它被称为“增长集合”。

1.1 G-Set 的特点

  • 不可变性:一旦元素被添加到集合中,它就永远存在于集合中,不能被删除。
  • 幂等性:多次添加同一个元素的效果和添加一次该元素的效果相同。
  • 交换性:元素的添加顺序不影响最终的集合状态。
  • 冲突无关:在不同节点上并行添加元素不会导致冲突,所有的更改最终都会被合并到每个节点的副本中。

1.2 G-Set 的应用场景

G-Set 非常适合于需要合并来自不同节点的数据,而这些数据不需要删除操作的场景。例如:

  • 分布式计数器:计数器的每次增加可以视为向 G-Set 中添加一个元素。
  • 曾在线用户集合:记录哪些用户曾经在线,即使他们后来下线了。
  • 标签系统:在一个分布式系统中为对象添加标签,不需要删除标签的功能。

1.3 G-Set 的局限性

由于 G-Set 是一个只增不减的集合,它的主要局限性在于无法从集合中删除元素。这可能导致随着时间的推移,集合的大小不断增长,占用更多的存储空间。为了解决这个问题,CRDT 研究中引入了其他类型的集合,如 OR-Set(可观察移除集合),它允许元素被添加和删除,同时仍然保持冲突无关的特性。

1.4 实现示例

在 Java 中,一个简单的 G-Set 实现可以使用 HashSet 来完成:

import java.util.HashSet;
import java.util.Set;public class GSet<E> {private final Set<E> set = new HashSet<>();public void add(E element) {set.add(element);}public boolean contains(E element) {return set.contains(element);}public Set<E> getElements() {return new HashSet<>(set);}
}

这个实现提供了添加元素和查询元素是否存在的基本操作。由于使用了 HashSet,这个 G-Set 实现自然就具有了幂等性和交换性的特点。

二、示例

2.1 G-Set 的应用场景:分布式计数器

在分布式系统中,使用 G-Set 实现计数器的一个常见方法是将每次计数增加视为向集合中添加一个唯一标识符(例如,时间戳、UUID等)。这样,计数器的值就等于集合中元素的数量。下面是一个具体的示例:

2.1.1 场景描述

假设有一个在线文章阅读平台,需要统计一篇文章的阅读次数。由于平台是分布式的,文章可以同时被多个节点上的用户阅读。为了确保阅读次数的准确性,平台决定使用 G-Set 来实现分布式计数器。

2.1.2 实现步骤
  1. 初始化:对于每篇文章,初始化一个空的 G-Set。
  2. 阅读操作:当一个用户阅读文章时,系统生成一个唯一标识符(例如,用户ID+时间戳),并将其添加到文章对应的 G-Set 中。
  3. 计数查询:要获取文章的阅读次数,只需计算 G-Set 中元素的数量。
2.1.3 示例代码
import java.util.HashSet;
import java.util.Set;public class ArticleReadCounter {private final Set<String> readSet = new HashSet<>();// 用户阅读文章时调用此方法public void addRead(String userId) {String uniqueId = userId + "-" + System.currentTimeMillis();readSet.add(uniqueId);}// 获取文章的阅读次数public int getReadCount() {return readSet.size();}
}
2.1.4 示例使用
public class Main {public static void main(String[] args) {ArticleReadCounter counter = new ArticleReadCounter();// 模拟用户阅读文章counter.addRead("user1");counter.addRead("user2");counter.addRead("user3");// 获取并打印阅读次数System.out.println("Article read count: " + counter.getReadCount());}
}
2.1.5 分布式环境下的合并

在分布式环境下,每个节点都可以有自己的 G-Set 实例。当需要合并两个节点的计数器时,可以将两个 G-Set 的元素合并到一个新的 G-Set 中,这个新的 G-Set 包含了所有唯一的阅读事件。由于 G-Set 是冲突无关的,这种合并操作是安全的,不会丢失数据,也不会产生冲突。

跟踪一个在线文章阅读平台的文章阅读次数,其中每次阅读都由一个唯一的事件ID表示,该ID由用户ID和时间戳组合而成。

2.1.5.1 分布式环境设置

假设我们的分布式系统有三个节点展示网页信息:Node A、Node B 和 Node C。每个节点都维护着自己的 G-Set 实例来跟踪文章的阅读事件。

  • Node A 的 G-Set 包含:{"user1-1622547600", "user2-1622547605"}
  • Node B 的 G-Set 包含:{"user3-1622547610", "user4-1622547615"}
  • Node C 的 G-Set 包含:{"user2-1622547605", "user5-1622547620"}

这里,"user2-1622547605" 在 Node A 和 Node C 中都出现了,展示了在分布式系统中,同一个阅读事件可能被多个节点观察到的情况。

2.1.5.2 合并过程

为了得到全局的文章阅读次数,我们需要将这三个节点的 G-Set 合并。合并操作是将所有节点的 G-Set 中的元素合并到一个新的集合中,由于 G-Set 的特性,即使某些阅读事件在多个节点中被记录,它们在合并后的集合中只会出现一次。

合并后的 G-Set 将包含:{"user1-1622547600", "user2-1622547605", "user3-1622547610", "user4-1622547615", "user5-1622547620"}

2.1.5.3 计数结果

文章的总阅读次数等于合并后的 G-Set 中元素的数量,即 5 次。

2.1.5.4 示例代码
import java.util.HashSet;
import java.util.Set;public class DistributedCounter {// 模拟合并过程public static Set<String> mergeSets(Set<String>... sets) {Set<String> mergedSet = new HashSet<>();for (Set<String> set : sets) {mergedSet.addAll(set);}return mergedSet;}public static void main(String[] args) {// 初始化节点的 G-SetsSet<String> nodeASet = new HashSet<>(Set.of("user1-1622547600", "user2-1622547605"));Set<String> nodeBSet = new HashSet<>(Set.of("user3-1622547610", "user4-1622547615"));Set<String> nodeCSet = new HashSet<>(Set.of("user2-1622547605", "user5-1622547620"));// 合并 G-SetsSet<String> mergedSet = mergeSets(nodeASet, nodeBSet, nodeCSet);// 计算并打印总阅读次数System.out.println("Total article reads: " + mergedSet.size());}
}
2.1.6 注意

这种方法的缺点是随着阅读次数的增加,G-Set 的大小也会不断增长,可能会占用大量的存储空间。在实际应用中,需要根据具体情况考虑是否适合使用 G-Set 实现分布式计数器,或者寻找其他更高效的解决方案。

2.2 G-Set 的应用场景:曾在线用户集合

在分布式系统中,使用 G-Set 来跟踪在线用户集合是一个很好的应用场景。在这个场景中,每当用户上线,系统就会将该用户的唯一标识符(如用户ID)添加到 G-Set 中。由于 G-Set 是一个只增不减的集合,这意味着一旦用户ID被添加,它就会永久保留在集合中。这对于跟踪曾经在线的用户非常有用,但请注意,这不适用于实时跟踪当前在线用户,因为用户下线后,其ID仍然保留在集合中。

2.2.1 示例代码
import java.util.HashSet;
import java.util.Set;public class OnlineUserTracker {private final Set<String> onlineUsers = new HashSet<>();// 用户上线时调用此方法public void userOnline(String userId) {onlineUsers.add(userId);}// 检查用户是否曾经上线过public boolean hasUserEverBeenOnline(String userId) {return onlineUsers.contains(userId);}// 获取曾经上线过的用户总数public int getTotalUsersEverOnline() {return onlineUsers.size();}
}
2.2.2 示例使用
public class Main {public static void main(String[] args) {OnlineUserTracker tracker = new OnlineUserTracker();// 模拟用户上线tracker.userOnline("user1");tracker.userOnline("user2");tracker.userOnline("user3");// 检查特定用户是否曾经上线过System.out.println("Has user2 ever been online? " + tracker.hasUserEverBeenOnline("user2"));// 获取并打印曾经上线过的用户总数System.out.println("Total users ever online: " + tracker.getTotalUsersEverOnline());}
}
2.2.3 分布式环境下的合并

在分布式环境下,每个节点都可以维护自己的在线用户 G-Set。当需要同步或合并两个节点的在线用户集合时,可以简单地将两个 G-Set 的元素合并到一个新的 G-Set 中。这个新的 G-Set 包含了所有唯一的用户ID,从而确保了数据的一致性和完整性。

每当用户上线时,系统就会将该用户的唯一标识符(如用户ID)添加到 G-Set 中。由于 G-Set 是一个只增不减的集合,这意味着一旦用户ID被添加,它就会永久保留在集合中,适用于跟踪曾经上线的用户。

2.2.3.1 分布式环境设置

假设我们的分布式系统有三个节点:Node A、Node B 和 Node C。每个节点都维护着自己的 G-Set 实例来跟踪在线用户。

  • Node A 的 G-Set 包含在线用户:{"user1", "user2"}
  • Node B 的 G-Set 包含在线用户:{"user3", "user4"}
  • Node C 的 G-Set 包含在线用户:{"user2", "user5"}

这里,"user2" 在 Node A 和 Node C 中都出现了,展示了在分布式系统中,同一个用户可能在多个节点上线的情况。

2.2.3.2 合并过程

为了得到聊天室的全局在线用户集,我们需要将这三个节点的 G-Set 合并。合并操作是将所有节点的 G-Set 中的元素合并到一个新的集合中,由于 G-Set 的特性,即使某些用户ID在多个节点中被记录,它们在合并后的集合中只会出现一次。

合并后的 G-Set 将包含在线用户:{"user1", "user2", "user3", "user4", "user5"}

2.2.3.3 示例代码
import java.util.HashSet;
import java.util.Set;public class OnlineUserTracker {// 模拟合并过程public static Set<String> mergeOnlineUsers(Set<String>... userSets) {Set<String> mergedSet = new HashSet<>();for (Set<String> set : userSets) {mergedSet.addAll(set);}return mergedSet;}public static void main(String[] args) {// 初始化节点的 G-SetsSet<String> nodeAUsers = new HashSet<>(Set.of("user1", "user2"));Set<String> nodeBUsers = new HashSet<>(Set.of("user3", "user4"));Set<String> nodeCUsers = new HashSet<>(Set.of("user2", "user5"));// 合并 G-SetsSet<String> mergedUsers = mergeOnlineUsers(nodeAUsers, nodeBUsers, nodeCUsers);// 打印合并后的在线用户集System.out.println("Merged online users: " + mergedUsers);}
}
2.2.4 注意
  • G-Set 适用于跟踪用户的在线状态,但由于其只增不减的特性,它不适合用于实时监控当前在线用户。
  • 随着时间的推移,G-Set 的大小可能会不断增长,这可能会导致存储空间的问题。在实际应用中,需要考虑这一点,并根据具体需求选择合适的数据结构。

2.3 G-Set 的应用场景:标签系统

在分布式系统中,使用 G-Set 实现标签系统是一个很好的应用场景。在这个场景中,每当需要给一个对象(如文章、图片等)添加标签时,系统就会将该标签的唯一标识符(如标签名)添加到与该对象关联的 G-Set 中。由于 G-Set 是一个只增不减的集合,这意味着一旦标签被添加,它就会永久保留在集合中,适用于标签的累积和历史记录。

2.3.1 示例代码
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;public class TagSystem {// 使用 Map 来存储每个对象及其关联的 G-Setprivate final Map<String, Set<String>> objectTags = new HashMap<>();// 给对象添加标签public void addTagToObject(String objectId, String tag) {// 获取或创建与对象关联的 G-SetSet<String> tags = objectTags.computeIfAbsent(objectId, k -> new HashSet<>());// 将标签添加到 G-Set 中tags.add(tag);}// 获取对象的所有标签public Set<String> getTagsForObject(String objectId) {return objectTags.getOrDefault(objectId, new HashSet<>());}
}
2.3.2 示例使用
public class Main {public static void main(String[] args) {TagSystem tagSystem = new TagSystem();// 给对象添加标签tagSystem.addTagToObject("article1", "Technology");tagSystem.addTagToObject("article1", "Innovation");tagSystem.addTagToObject("article2", "Travel");tagSystem.addTagToObject("article1", "2023");// 获取并打印对象的标签System.out.println("Tags for article1: " + tagSystem.getTagsForObject("article1"));System.out.println("Tags for article2: " + tagSystem.getTagsForObject("article2"));}
}
2.3.3 分布式环境下的合并

在分布式环境下,每个节点都可以维护自己的标签 G-Set。当需要同步或合并两个节点的标签集合时,可以简单地将两个 G-Set 的元素合并到一个新的 G-Set 中。这个新的 G-Set 包含了所有唯一的标签,从而确保了数据的一致性和完整性。

在这个场景中,每当需要给一个对象(如文章、图片等)添加标签时,系统就会将该标签的唯一标识符(如标签名)添加到与该对象关联的 G-Set 中。由于 G-Set 是一个只增不减的集合,这意味着一旦标签被添加,它就会永久保留在集合中。

2.3.3.1 分布式环境设置

假设我们的分布式系统有三个节点:Node A、Node B 和 Node C。每个节点都维护着自己的 G-Set 实例来跟踪对象的标签。

  • Node A 的 G-Set 包含对象 “Article1” 的标签:{"Tech", "Innovation"}
  • Node B 的 G-Set 包含对象 “Article1” 的标签:{"2023", "Tech"}
  • Node C 的 G-Set 包含对象 “Article1” 的标签:{"Innovation", "Environment"}

这里,标签 “Tech” 和 “Innovation” 在多个节点中出现了,展示了在分布式系统中,同一个标签可能被多个节点添加的情况。

2.3.3.2 合并过程

为了得到对象 “Article1” 的全局标签集,我们需要将这三个节点的 G-Set 合并。合并操作是将所有节点的 G-Set 中的元素合并到一个新的集合中,由于 G-Set 的特性,即使某些标签在多个节点中被记录,它们在合并后的集合中只会出现一次。

合并后的 G-Set 将包含对象 “Article1” 的标签:{"Tech", "Innovation", "2023", "Environment"}

2.3.3.3 示例代码
import java.util.HashSet;
import java.util.Set;public class TagSystem {// 模拟合并过程public static Set<String> mergeTags(Set<String>... tagSets) {Set<String> mergedSet = new HashSet<>();for (Set<String> set : tagSets) {mergedSet.addAll(set);}return mergedSet;}public static void main(String[] args) {// 初始化节点的 G-SetsSet<String> nodeATags = new HashSet<>(Set.of("Tech", "Innovation"));Set<String> nodeBTags = new HashSet<>(Set.of("2023", "Tech"));Set<String> nodeCTags = new HashSet<>(Set.of("Innovation", "Environment"));// 合并 G-SetsSet<String> mergedTags = mergeTags(nodeATags, nodeBTags, nodeCTags);// 打印合并后的标签集System.out.println("Merged tags for Article1: " + mergedTags);}
}
2.3.4 注意
  • G-Set 适用于累积对象的标签,但由于其只增不减的特性,它不适合用于需要频繁删除标签的场景。
  • 随着时间的推移,每个对象关联的 G-Set 的大小可能会不断增长,这可能会导致存储空间的问题。在实际应用中,需要考虑这一点,并根据具体需求选择合适的数据结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/55471.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

限流是什么?如何限流?怎么限流?

概述 什么是限流 对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机 为什么要限流 因为互联网系统通常都要面对大并发大流量的请求,在突发情况下(最常见的场景就是秒杀、抢购),瞬时大流量会直接将系统打垮,无法…

html和css实现页面

任务4 html文件 任务5 htm文件 css文件 任务6 html文件 css文件 任务7 html文件 css文件

Java【多线程】synchronized关键字

目录 synchronized的特性 1.互斥 2.可重入 如何自己实现一个可重入锁&#xff1f; 关于死锁 死锁的第三种情况 N个线程M把锁 构成死锁的四个必要条件 java标准库中的线程安全类 线程不安全 线程安全 synchronized关键字-监视器锁monitor locker synchronized的特性 …

Tailscale自建中转服务器derper搭建笔记(基于docker)

自己搭建derper服务器&#xff0c;让Tailscale中转更流畅。 Tailscale是很好的远程组网工具&#xff0c;在两台机器P2P打洞成功的情况下可以实现网络直连&#xff0c;但如果打洞失败就会进行数据中转&#xff0c;我们的数据要跑到国外再跑回来&#xff0c;这样速度就很慢了。 …

STGCN解读(论文+代码)

一、引言 引言部分不是论文的重点&#xff0c;主要讲述了交通预测的重要性以及一些传统方法的不足之处。进而推出了自己的模型——STGCN。 二、交通预测与图卷积 第二部分讲述了交通预测中路图和图卷积的概念。 首先理解道路图&#xff0c;交通预测被定义为典型的时间序列预测…

Axure重要元件一——动态面板

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 本节课&#xff1a;动态面板 课程内容&#xff1a;认识动态面板、动态面板基本操作 应用场景&#xff1a;特定窗口、重要交互、长页面、容器等 一、认识动态面板 动态…

DeBiFormer:带有可变形代理双层路由注意力的视觉Transformer

https://arxiv.org/pdf/2410.08582v1 摘要 带有各种注意力模块的视觉Transformer在视觉任务上已表现出卓越的性能。虽然使用稀疏自适应注意力&#xff08;如在DAT中&#xff09;在图像分类任务中取得了显著成果&#xff0c;但在对语义分割任务进行微调时&#xff0c;由可变形…

bug的定义和测试

一、软件测试的生命周期 软件测试的⽣命周期是指测试流程&#xff0c;这个流程是按照⼀定顺序执⾏的⼀系列特定的步骤&#xff0c;去保证产品 质量符合需求。在软件测试⽣命周期流程中&#xff0c;每个活动都按照计划的系统的执⾏。每个阶段有不同的 ⽬标和交付产物 需求分析…

【python+Redis】hash修改

文章目录 前请详解一、关于Update1. 语法2. 代码示例 二、完整代码 前请详解 Redis库数据 keyvalue1{“id”: 1, “name”: “xxx”, “age”: “18”, “sex”: “\u7537”}2{“id”: 2, “name”: “xxx”, “age”: “18”, “sex”: “\u5973”}3{“id”: 3, “name”: “…

软件测试面试题600多条及答案

这些问题都是软件测试领域常见的面试问题&#xff0c;以下是一些可能的答案&#xff1a; 什么是软件测试&#xff1f; 软件测试是一系列活动&#xff0c;旨在评估软件产品的质量和性能&#xff0c;以确保它符合规定的需求和标准。它包括执行程序或系统以验证其满足规定需求的过…

“探索Adobe Photoshop 2024:订阅方案、成本效益分析及在线替代品“

设计师们对Adobe Photoshop这款业界领先的图像编辑软件肯定不会陌生。如果你正考虑加入Photoshop的用户行列&#xff0c;可能会对其价格感到好奇。Photoshop的价值在于其强大的功能&#xff0c;而它的价格也反映了这一点。下面&#xff0c;我们就来详细了解一下Adobe Photoshop…

数据结构(8.2_1)——插入排序

插入排序 算法思想&#xff1a;每次将一个待排序的记录按其关键字大小插入到前面已排序好的子序列中&#xff0c;直到全部记录插入完成。 代码实现 #include <stdio.h>void InsertSort(int A[], int n) {int i, j.temp;for (i 1; i < n; i) {//将各元素插入已排好…

【每日一题】LeetCode每日一题-无重复字符的最长子串

题目链接&#xff1a;https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/ 题目描述&#xff1a; 给定一个字符串 s&#xff0c;找到其中不包含重复字符的最长子串的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因…

Axure重要元件二——内联框架

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;内联框架 课程内容&#xff1a;认识内联框架、基本嵌入 应用场景&#xff1a;表单、图片、文字嵌入式场景、交互应用 一、认识内联框架 内联框架的…

如何安全擦除 iPhone 上的所有数据,避免隐私泄露?

在当今的数字时代&#xff0c;隐私安全尤为重要。特别是在转让或出售 iPhone 之前&#xff0c;擦除设备上的所有内容是每位用户都应注意的关键步骤。尽管苹果自带了删除数据的功能&#xff0c;但有时这并不足以保证数据完全无法恢复。本文将结合 iPhone 自带的"抹掉所有内…

软考(中级-软件设计师)计算机系统篇(1018)

十、存储系统 10.1 层次结构主存–辅存&#xff1a;实现虚拟存储系统&#xff0c;解决了主存容量不够的问题。 Cache–主存&#xff1a;解决了主存与CPU速度不匹配的问题。 10.2 分类 1、按位置分类&#xff1a;可分为内存和外存。 内存&#xff08;主存&#xff09;&#…

Erlang的吸睛特性:热升级功能的工作和实现原理

Erlang的热升级功能允许开发者在不停止系统的情况下进行代码更新。这一机制是Erlang语言的核心优势之一&#xff0c;特别适用于需要高可用性的分布式系统。下面通过源代码来剖析其工作原理。 1. 模块定义 Erlang中的每个模块通常包含多个功能。在热升级过程中&#xff0c;旧版…

【从零开发Mybatis】引入XNode和XPathParser

引言 在上文&#xff0c;我们发现直接使用 DOM库去解析XML 配置文件&#xff0c;非常复杂&#xff0c;也很不方便&#xff0c;需要编写大量的重复代码来处理 XML 文件的读取和解析&#xff0c;代码可读性以及可维护性相当差&#xff0c;使用起来非常不灵活。 因此&#xff0c…

o1快慢思考的风又吹到了Agent!

智能体&#xff08;Agent&#xff09;通过自然对话与用户互动有两个任务&#xff1a;交谈和规划/推理。对话回应必须基于所有可用信息&#xff0c;行动必须有助于实现目标。与用户交谈和进行多步推理和规划之间的二分法&#xff0c;类似卡尼曼引入的人类快速思考和慢速思考系统…

库卡ForceTorqueControl(二)

1. 基准坐标系RCS 基准坐标系 RCS 是力 / 力矩控制的参考系。基准坐标系的原点始终是当前的TCP。 1.1 BASE 的 RCS 姿态 基准坐标系的姿态与当前基础坐标系&#xff08;基座坐标系&#xff09;的姿态一致。它不取决于刀具的姿态。基准坐标系的原点是当前的 TCP。 示例&#xff…