redis存储树结构的数据
你理解的很对,如果节点实体数据中已经包含了父节点 ID 和子节点 ID 集合,那么你可以通过存储节点的实体数据来实现树结构的存储和操作,而不需要额外的 Redis key 来保存子节点的树结构。这样做可以简化实现,并减少数据冗余。
数据存储设计
- 节点数据存储:
- Redis Key:
node:<nodeId>
- 数据内容:包含节点的 ID、父节点 ID、节点数据(如基础信息、模板列表)、子节点 ID 集合。
- Redis Key:
示例实现
1. 节点实体的定义
假设节点实体的定义如下:
public class Node {
private String id;
private String parentId;
private Map<String, Object> data;
private List<String> childrenIds;
// Getters and Setters
}
2. 添加节点
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class TreeService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String NODE_KEY_PREFIX = "node:";
public void addNode(Node node) {
// 保存节点数据
redisTemplate.opsForValue().set(NODE_KEY_PREFIX + node.getId(), node);
if (node.getParentId() != null) {
// 获取父节点
Node parentNode = (Node) redisTemplate.opsForValue().get(NODE_KEY_PREFIX + node.getParentId());
if (parentNode != null) {
// 更新父节点的子节点集合
parentNode.getChildrenIds().add(node.getId());
redisTemplate.opsForValue().set(NODE_KEY_PREFIX + node.getParentId(), parentNode);
}
}
}
}
3. 删除节点
public void deleteNode(String nodeId) {
// 删除节点数据
redisTemplate.delete(NODE_KEY_PREFIX + nodeId);
// 获取要删除节点的父节点
Node nodeToDelete = (Node) redisTemplate.opsForValue().get(NODE_KEY_PREFIX + nodeId);
if (nodeToDelete != null && nodeToDelete.getParentId() != null) {
Node parentNode = (Node) redisTemplate.opsForValue().get(NODE_KEY_PREFIX + nodeToDelete.getParentId());
if (parentNode != null) {
// 从父节点的子节点集合中删除该节点
parentNode.getChildrenIds().remove(nodeId);
redisTemplate.opsForValue().set(NODE_KEY_PREFIX + parentNode.getId(), parentNode);
}
}
}
4. 获取节点数据
public Node getNode(String nodeId) {
return (Node) redisTemplate.opsForValue().get(NODE_KEY_PREFIX + nodeId);
}
5. 获取子节点数据
import java.util.ArrayList;
import java.util.List;
public List<Node> getChildrenData(String nodeId) {
Node parentNode = getNode(nodeId);
List<Node> childrenData = new ArrayList<>();
if (parentNode != null) {
List<String> childrenIds = parentNode.getChildrenIds();
// 使用管道技术批量获取子节点详细数据
redisTemplate.executePipelined((connection) -> {
for (String childId : childrenIds) {
connection.get((NODE_KEY_PREFIX + childId).getBytes());
}
return null;
}).forEach(result -> {
if (result != null) {
Node childNode = deserialize(result); // 自定义方法反序列化数据
if (childNode != null) {
childrenData.add(childNode);
}
}
});
}
return childrenData;
}
private Node deserialize(Object result) {
// 实现反序列化节点数据的逻辑
// 例如,将 byte[] 转换为 Node 对象
return null;
}
总结
优点:
- 简化设计:只需一个 Redis key 存储每个节点的详细数据,不需要额外的结构来保存子节点。
- 直接存取:可以直接读取和写入节点数据,减少了数据结构的复杂性。
缺点:
- 一致性维护:在节点增删改操作时,需要确保父子节点数据的一致性,特别是在并发环境下。
- 性能考量:如果节点数目非常多,可能需要优化数据操作和存取性能。
通过这样的设计,你可以有效地利用 Redis 来存储和操作树结构数据,同时保持数据的简洁性和高效性。