美文网首页程序员代码改变世界
如何使用Redis存储图的联通关系

如何使用Redis存储图的联通关系

作者: dugangabc | 来源:发表于2016-02-05 22:49 被阅读520次

问题描述

一张图由若干节点和连接节点的组成。本文考虑如何利用Redis保存所有节点和边的信息,并且支持给定任意节点,查询出与其联通的所有节点。所谓联通,指的是两个节点之间一定有一条若干条边组成的路径。

如上需求可以使用专门的图数据库(如neo4j)进行实现。但实际上,只要进行恰当的设计,并结合一些设定,使用Redis也可以实现如上需求。

节点信息存储方法

首先,我们需要用到Redis的散列类型来存储节点的信息。key为节点的唯一标识id(以下称为节点id),value为散列表,保存节点的各种信息。

如下是一个简单的例子,假设有三个节点,id为1,2,3,4,5。代表五个人,每个人都有姓名,年龄,职业三个属性,则每个人的数据的存储方式如下(以id为1的人为例):

hset person:1 name mike
hset person:1 age 23
hset person:1 profession engineer

节点联通关系存储方法

使用Redis的集合类型表示节点之间的联通关系。注意,这里抛弃掉了边的信息,仅保留联通关系。即抛弃掉了两个节点联通的具体路径

假设存在如下的五个人有如下的朋友(联通)关系:

1 -- 2, 2 -- 3, 4 -- 5, 5 -- 2

我们希望通过特定的算法,当这四个联通关系依次入库后,redis数据库中有如下的存储结构:

friend:1 [1, 2, 3, 4, 5]
friend:2 [1]
friend:3 [1]
friend:4 [1]
friend:5 [1]

从上面四个联通关系可以看出,实际上1,2,3,4,5是彼此联通的,那么就构成了一个联通图。每一个联通图,需要按照一定原则选举出一个代表节点。该节点的关系键保存完成的联通成员信息。而其余成员节点的关系键均指向该代表性节点。以上面的例子为例,节点的选举原则是选取联通图中编号最小的节点。则1为代表性节点,则friend:1中保存了完整的联通成员信息。节点2-5则仅保存与1联通。这样保存的目的是防止数据的冗余存储。联通图假设有n个成员,则需要的存储空间为2*n-1。

为了达到上面的存储效果,需要在入库时对关系键进行一些调整操作,具体步骤如下:

步骤1 获取关联关系的左右两个节点,检查是否存在节点的关系键,若没有则创建关系键并将自身加入关系集合

sadd friend:1 1 
sadd friend:2 2 

步骤2 针对每个节点的关系键,获取其关联的所有节点中id最小的节点(注意,若使用有序集合,最小节点将更方便求解,此处以普通集合为例)。如下为伪代码,混合了redis命令和java的赋值命令

set1 =  smems friend:1 //将friend:1中的集合保存在set1
set2 =  smems friend:2
std1 = min(set1)//取set1中最小的元素
std2 = min(set2)

步骤3 找出std1和std2的较小值和较大值

stdMin = std1>std2? std2:std1
stdMax = std1>std2? std1:std2

步骤4 若stdMin==stdMax则无需进行任何操作,否则将stdMax中的所有元素都加入到stdMin中,且stdMax中的元素对应的关系键全部设置成stdMin

members = smems friend:stdMax
//stdMax中的元素对应的关系键全部设置成stdMin
for(String member:members){
    del friend:member
    sadd friend:member stdMin
}
//将friend:stdMin和friend:stdMax中的元素合并,并将结果放入stdMin
smerge friend:stdMin friend:stdMin friend:stdMax

每一个关联关系的入库都需要如上四步。如果存在多线程入库的情况,需要将其形成一个事务处理。下面以1 -- 2, 2 -- 3, 4 -- 5, 5 -- 2的入库顺序逐步进行推演:

入库1 -- 2

步骤1后为:

friend:1 [1]
friend:2 [2]

步骤2: 得到1中元素最小为1,2中元素最小为2
步骤3: stdMin=1,stdMax=2
步骤4:将2中元素归到1,并设置2中元素的关系键内容为1

friend:1 [1,2]
friend:2 1

入库2 -- 3

步骤1后为(创建了3的关系键):

friend:1 [1,2]
friend:2 [1]
friend:3 [3]

步骤2: 得到2中元素最小为1,3中元素最小为3
步骤3: stdMin=1,stdMax=3
步骤4:将3中元素归到1,并设置3中元素的关系键内容为1

friend:1 [1,2,3]
friend:2 [1]
friend:3 [1]

入库4 -- 5

步骤1后为(创建了4和5的关系键):

friend:1 [1,2,3]
friend:2 [1]
friend:3 [1]
friend:4 [4]
friend:5 [5]

步骤2: 得到4中元素最小为4,5中元素最小为5
步骤3: stdMin=4,stdMax=5
步骤4:将4中元素归到5,并设置4中元素的关系键内容为5

friend:1 [1,2,3]
friend:2 [1]
friend:3 [1]
friend:4 [4,5]
friend:5 [4]

入库5 -- 2

步骤1后为(无变化):

friend:1 [1,2,3]
friend:2 [1]
friend:3 [1]
friend:4 [4,5]
friend:5 [4]

步骤2: 得到5中元素最小为4,2中元素最小为1
步骤3: stdMin=1,stdMax=4
步骤4:将4中元素归到1,并设置4中元素的关系键内容为1

friend:1 [1,2,3,4,5]
friend:2 [1]
friend:3 [1]
friend:4 [1]
friend:5 [1]

联通关系的获取方法

基于如上构造的redis数据存储,可以方便的给定任意一个节点,找出与其联通的所有节点。仅需要如下两个步骤

  1. 获取该节点的关系键,若不存在则返回空值
  2. 若关系键存在,则获取其值。若值中包含键id本身(如friend:1对应的集合中包含1),则直接返回值。否则返回值中id对应的关系键中的内容(如friend:4的集合中没有4,仅有1,则返回friend:1对应的集合)。

相关文章

  • 如何使用Redis存储图的联通关系

    问题描述 一张图由若干节点和连接节点的边组成。本文考虑如何利用Redis保存所有节点和边的信息,并且支持给定任意节...

  • 几个概念:有向图、无向图、加权图、简单图、联通、联通分量、生成树、强连通分量、强联通图图的存储:邻接矩阵(二维、一...

  • 2019-01-07

    Redis的使用(结合自己的项目) Redis是非关系型数据库,和Memcached类似,其存储格式为Key-Va...

  • PHP 配置使用 Redis 储存 Session 数据

        PHP 默认使用文件存储 Session 数据,Redis 拥有极快的数据读写速度,使用 Redis 存储...

  • Redis

    一、redis是什么? 是一个key-value数据库,用来存储数据 二、如何使用redis? 本地启动redis...

  • redis

    redis 是一个用c语言编写的高性能k/v存储数据库,支持持久存储,是非关系型数据库。redis默认使用TCP的...

  • Redis与其他数据库和软件的对比

    名称 类型 数据存储选项 查询类型 附加功能 Redis 使用内存存储(in-memory)的非关系数据库 字符串...

  • 【Redis缓存】- Redis数据结构、基本命令操作、持久化

    一、Redis 数据结构 要想使用 Redis 进行数据存储,首先需要了解 Redis 的数据结构,redis存储...

  • Spring Data Redis - Redis Reposi

    使用Redis Repositories可以方便的在redis中用Redis Hash的方式存储各种对象类型。使用...

  • sentinel限流二开(2)—可插拔的分布式存储

    目前使用的是consul作为存储中心,但是如何“无痛”切换到mongodb、nacos、redis、mysql等存...

网友评论

    本文标题:如何使用Redis存储图的联通关系

    本文链接:https://www.haomeiwen.com/subject/lcoekttx.html