前言

想象一个场景,如果CMDB使用关系型数据库(MySQL/Oracle)存储整个机房的网络拓扑信息

  • 服务器
  • 交换机
  • 路由器
  • 防火墙
  • 端口
  • 链路
  • 机架
  • 机房

这个关系型数据库的表字段得建多少外键?查询一条链路要join多少张表?怎么实时更新维护?

图数据库

Neo4j是一个原生图数据库(Native Graph Database),常被用于构建知识图谱(Knowledge Graph)应用。

图数据库是一种以图结构(节点、关系、属性)为核心存储模型的新型数据库。

它不再使用传统的表、行、列结构,而是直接存储实体与实体之间的关系,非常适合表达网络、拓扑、组织、社交、依赖等天然具有复杂关系连接特性的数据。

图数据库最大的特点是:关系是第一公民,查询时无需JOIN、无需外键,速度极快。

使用图数据库

Cypher是Neo4j图数据库专用的查询语言,专门用来查询和操作图数据,是图数据库版的SQL,但比SQL更简单、更直观、更擅长处理关系网。 

from neo4j import GraphDatabase

# 连接你的 Neo4j 数据库
URI = "neo4j://127.0.0.1:7687"
USER = "neo4j"
PASSWORD = ""  # 这里改成你的密码!



#空白图纸
def trunk_graph(driver):
    with driver.session() as session:
        session.run("MATCH (n) DETACH DELETE n")
    print("✅图谱数据已清空")


#构建图
def build_graph(driver):
    with driver.session() as session:
        session.run("""
               CREATE
               (router:Router {name:'核心路由器', ip:'10.0.0.1'}),
               (fw:Firewall {name:'防火墙', ip:'10.0.0.2'}),
               (switch:Switch {name:'汇聚交换机', ip:'10.0.0.3'}),
               (server1:Server {name:'应用服务器-1', ip:'192.168.1.10'}),
               (server2:Server {name:'应用服务器-2', ip:'192.168.1.11'}),
               (rack:Rack {name:'机柜A'}),
               (room:Room {name:'机房一区'})

               CREATE
               (router)-[:CONNECT_TO]->(fw),
               (fw)-[:CONNECT_TO]->(switch),
               (switch)-[:CONNECT_TO]->(server1),
               (switch)-[:CONNECT_TO]->(server2),
               (server1)-[:MOUNT_IN]->(rack),
               (rack)-[:LOCATED_IN]->(room)
           """)
    print("✅机房拓扑创建完成!")

#图查询
def query_server_path(driver):
    with driver.session() as session:
        result = session.run("""
            MATCH path = (server1:Server {name:'应用服务器-1'})<-[:CONNECT_TO*]-()
            RETURN path
        """)
        for record in result:
            print("服务器1的上游链路:", record["path"])

if __name__ == '__main__':
    driver = GraphDatabase.driver(URI, auth=(USER, PASSWORD))
    trunk_graph(driver)
    build_graph(driver)
    query_server_path(driver)
    driver.close()
test_neo4j

所以未来的趋势不是用Neo4j取代外键,而是关系型数据库继续当主数据源,Neo4j 做关系加速层,复杂查询走图,事务和报表还留在SQL里。

关系性数据库管数据,图数据库管关系,关系性数据库写,图库查,CDC 同步,各司其职。

旁路小模型根据自然语言生成Cypher查询

import os,ollama,json

from llama_index.core import (
    SimpleDirectoryReader,
    VectorStoreIndex,
    StorageContext,
    load_index_from_storage,
    Settings
)
from llama_index.core.node_parser import SentenceSplitter
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from neo4j import GraphDatabase

#长期记忆-Rage
class LocalRAG(object):
    def __init__(
        self,
        knowledge_dir="knowledge",
        index_dir="vector_index",
        chunk_size=30,
        chunk_overlap=4
    ):
        self.knowledge_dir = knowledge_dir
        self.index_dir = index_dir

        # 配置分段 & 向量模型
        Settings.node_parser = SentenceSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
        Settings.embed_model = HuggingFaceEmbedding(model_name="all-MiniLM-L6-v2")

        # 自动加载已有索引,没有则重建
        if os.path.exists(index_dir):
            self.index = self._load_index()
        else:
            self.index = self._build_index()

        self.retriever = self.index.as_retriever(similarity_top_k=3)

    def _build_index(self):
        documents = SimpleDirectoryReader(self.knowledge_dir).load_data()
        index = VectorStoreIndex.from_documents(documents)
        # 保存到本地
        index.storage_context.persist(persist_dir=self.index_dir)
        return index

    def _load_index(self):
        storage_context = StorageContext.from_defaults(persist_dir=self.index_dir)
        return load_index_from_storage(storage_context)

    def search(self, query):
        nodes = self.retriever.retrieve(query)
        return [node.text for node in nodes]

#长期记忆关系图谱
class LocalGraph(object):
    #初始化知识图谱连接
    def __init__(self,url,username,password):
        self.url = url
        self.username = username
        self.password = password
        self.driver=GraphDatabase.driver(uri=self.url, auth=(self.username, self.password))

    def loacal_side_llm(self,user_prompt):
        prompt = f"""
        你是一个只会输出JSON的Cypher生成器。
        任务:
        从用户问题中,提取【应用名称】。
        生成固定格式的JSON,不要解释,不要多余内容。
        节点:App(oamname)
        关系:call
        输出格式(严格遵守):
        {{
            "match": "(a:App)-[r:call*1..20]->(b:App)",
            "where": "a.oamname = '这里填提取到的应用名称'",
            "return": "a.oamname AS 入口应用, collect(b.oamname) AS 全链路依赖"
        }}
        用户问题:{user_prompt}
        """
        response = ollama.generate(
            model="llama3.1:8b",
            prompt=prompt,
            format="json",
            options={"temperature": 0.01}
        )
        return response.get("response")

    def search(self, query,parameters=None):
        with self.driver.session() as session:
            result = session.run(query, parameters)
            return result.data()  # 返回Python能看懂的格式

    def init_data(self):
        with self.driver.session() as s:
            s.run("""
                CREATE (a1:App {oamname:'nginx-gateway'}),
                       (a2:App {oamname:'ops-mgmt'}),
                       (a3:App {oamname:'rds-mysql-5678abc9-singapore-prod'})
                            
                CREATE (a1)-[:call]->(a2),
                       (a2)-[:call]->(a3)
            """)
        print("✅ 测试数据插入完成!")

    def trunk_graph(self):
        with self.driver.session() as session:
            session.run("MATCH (n) DETACH DELETE n")
        print("✅图谱数据已清空")


if __name__ == '__main__':
    graph = LocalGraph(url="neo4j://localhost:7687",username="neo4j",password="Dell2026")


    # graph.trunk_graph()
    # graph.init_data()


    # graph.loacal_side_llm(user_prompt="111")
    # query = """
    #     MATCH (a:App {oamname: 'Nginx'})-[:call*1..20]->(b:App)
    #     RETURN a.oamname AS 入口应用, collect(b.oamname) AS 全链路依赖
    # """
    #


    result_json= graph.loacal_side_llm("nginx-gateway应用的链路信息")
    result = json.loads(result_json)
    cypher = f"MATCH {result['match']} WHERE {result['where']} RETURN {result['return']}"
    print(cypher)
    print(graph.search(query=cypher))
View Code

 

 

 

 

 

参考

posted on 2026-03-24 10:46  运维体系建设之路  阅读(46)  评论(0)    收藏  举报