基于游标的分页时间戳
Cursor based pagination timestamp
我必须实现基于游标的分页,并且我对如何实现这一点感到有点困惑,因为我的实体的主键不是自动递增的,例如 Aerospike。
当比较运算符在我们不使用自动递增的分布式系统中的主键上不可用时,最明显的替代方法是使用时间戳。但这有多可靠?
也就是说,两个用户可能同时上传,这基本上搞砸了基于光标的分页背后的逻辑。
例如,给我从某个时间戳开始的下 10 个项目,该时间戳作为游标发送以获取下一个结果。当此时间戳有两个 post 时,如果 1 post 不适合先前请求的计数范围(例如 10 post,其中重复的 post 将位于位置 11)。
你是如何规避这个问题的?
最明显的方法是当时间戳已经存在时,在时间戳旁边有一个辅助字段和额外的计数器,并在应用程序级别处理额外的逻辑,但所有这些似乎增加了很多膨胀。
非常感谢任何见解!
使用上限列表或上限地图作为数据仓。
Capped Map 代码片段或其变体 - 保留最后 10 次更新:
public class CappedMap {
public static int insert(AerospikeClient client, int i) {
Key key = new Key("test", "testMap", "user1");
MapPolicy mPolicy = new MapPolicy();
int retVal=0;
try {
client.operate(null, key,
MapOperation.removeByIndexRange("myMap",-10,10,MapReturnType.INVERTED),
// INVERTED introduced in server version 3.16.0.1
MapOperation.put(mPolicy, "myMap", Value.get(i),
Value.get("A quick brown fox jumps right over a lazy dog") ));
}
catch (AerospikeException e) {
System.out.println("Error Code: "+e.getResultCode());
}
return i;
}
public static void main(String[] args) {
AerospikeClient client = new AerospikeClient("127.0.0.1", 3000);
int retVal = 0;
for (int i = 0; i < 123; i++) {
System.out.println("Inserting k = "+i);
i = insert(client, i);
}
client.close();
}
}
我怀疑 Twitter 是否为此使用了 RDBMS 自动递增行 ID。有数据库外部的 ZooKeeper 等服务,可用于实现全局序列 ID。不过,您可能不希望拥有一个全局序列 ID,因为如果每个人都必须从同一来源请求一个序列,您就会强制所有内容进行序列化,从而破坏分布式处理的整个概念。
时间是对操作进行排序的自然方式,但您需要就时间是多少达成一致。如果不同的作者与充当 'wall clock' 的服务交谈,他们或多或少可以在时间上达成一致。就像你说的,你不需要纳秒级的精度。具有毫秒时间戳作为其映射键的 Map 将允许您执行以下操作:
- 获取从指定时间戳开始的特定数量的元素,即使没有这样的映射键,使用
get_by_key_rel_index_range()
。
- 获取两个时间戳之间区间内的所有元素,使用
get_by_key_interval()
.
要为用户的推文建模,您可以将他们的 ID 存储在这样的 Map 中,记录的键是用户 ID。
要为用户时间线建模,您可以使用包含 [timestamp, tweet ID, .., ..]
作为元素的 ordered List 用户时间线记录(由用户 ID 键入)。这将允许具有相同时间戳的元素并排存在(其中地图不能有两个具有完全相同键的元素)。
在这种情况下有用的列表操作是:
- 从最接近指定时间戳的元素开始获取特定数量的元素,使用
get_by_value_rel_rank_range()
- 获取两个时间戳之间区间内的所有元素,使用
get_by_value_interavl
。
见Element Ordering and Comparison。
我在此处提供了有关如何使用 List 和 Map 操作对不同事物建模的示例:rbotzer/aerospike-cdt-examples。
我必须实现基于游标的分页,并且我对如何实现这一点感到有点困惑,因为我的实体的主键不是自动递增的,例如 Aerospike。
当比较运算符在我们不使用自动递增的分布式系统中的主键上不可用时,最明显的替代方法是使用时间戳。但这有多可靠?
也就是说,两个用户可能同时上传,这基本上搞砸了基于光标的分页背后的逻辑。
例如,给我从某个时间戳开始的下 10 个项目,该时间戳作为游标发送以获取下一个结果。当此时间戳有两个 post 时,如果 1 post 不适合先前请求的计数范围(例如 10 post,其中重复的 post 将位于位置 11)。
你是如何规避这个问题的?
最明显的方法是当时间戳已经存在时,在时间戳旁边有一个辅助字段和额外的计数器,并在应用程序级别处理额外的逻辑,但所有这些似乎增加了很多膨胀。
非常感谢任何见解!
使用上限列表或上限地图作为数据仓。
Capped Map 代码片段或其变体 - 保留最后 10 次更新:
public class CappedMap {
public static int insert(AerospikeClient client, int i) {
Key key = new Key("test", "testMap", "user1");
MapPolicy mPolicy = new MapPolicy();
int retVal=0;
try {
client.operate(null, key,
MapOperation.removeByIndexRange("myMap",-10,10,MapReturnType.INVERTED),
// INVERTED introduced in server version 3.16.0.1
MapOperation.put(mPolicy, "myMap", Value.get(i),
Value.get("A quick brown fox jumps right over a lazy dog") ));
}
catch (AerospikeException e) {
System.out.println("Error Code: "+e.getResultCode());
}
return i;
}
public static void main(String[] args) {
AerospikeClient client = new AerospikeClient("127.0.0.1", 3000);
int retVal = 0;
for (int i = 0; i < 123; i++) {
System.out.println("Inserting k = "+i);
i = insert(client, i);
}
client.close();
}
}
我怀疑 Twitter 是否为此使用了 RDBMS 自动递增行 ID。有数据库外部的 ZooKeeper 等服务,可用于实现全局序列 ID。不过,您可能不希望拥有一个全局序列 ID,因为如果每个人都必须从同一来源请求一个序列,您就会强制所有内容进行序列化,从而破坏分布式处理的整个概念。
时间是对操作进行排序的自然方式,但您需要就时间是多少达成一致。如果不同的作者与充当 'wall clock' 的服务交谈,他们或多或少可以在时间上达成一致。就像你说的,你不需要纳秒级的精度。具有毫秒时间戳作为其映射键的 Map 将允许您执行以下操作:
- 获取从指定时间戳开始的特定数量的元素,即使没有这样的映射键,使用
get_by_key_rel_index_range()
。 - 获取两个时间戳之间区间内的所有元素,使用
get_by_key_interval()
.
要为用户的推文建模,您可以将他们的 ID 存储在这样的 Map 中,记录的键是用户 ID。
要为用户时间线建模,您可以使用包含 [timestamp, tweet ID, .., ..]
作为元素的 ordered List 用户时间线记录(由用户 ID 键入)。这将允许具有相同时间戳的元素并排存在(其中地图不能有两个具有完全相同键的元素)。
在这种情况下有用的列表操作是:
- 从最接近指定时间戳的元素开始获取特定数量的元素,使用
get_by_value_rel_rank_range()
- 获取两个时间戳之间区间内的所有元素,使用
get_by_value_interavl
。
见Element Ordering and Comparison。
我在此处提供了有关如何使用 List 和 Map 操作对不同事物建模的示例:rbotzer/aerospike-cdt-examples。