{"id":20986619,"url":"https://github.com/dpwgc/fraisedb","last_synced_at":"2026-03-04T07:01:35.851Z","repository":{"id":180772344,"uuid":"665560411","full_name":"dpwgc/fraisedb","owner":"dpwgc","description":"基于Hashicorp Raft + LevelDB的分布式键值型数据库。包含键值读写、范围查询、过期清理、空间隔离、事件订阅发布等功能。","archived":false,"fork":false,"pushed_at":"2024-05-17T18:09:01.000Z","size":325,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-14T17:41:25.226Z","etag":null,"topics":["cluster","db","go","kv","leveldb","metadata","nosql","raft"],"latest_commit_sha":null,"homepage":"https://gitee.com/dpwgc/fraisedb","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dpwgc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-07-12T13:26:26.000Z","updated_at":"2024-05-18T14:35:42.000Z","dependencies_parsed_at":"2024-05-17T14:37:35.622Z","dependency_job_id":"cb3a3c91-b9e3-470f-b406-9a9fb5fbd247","html_url":"https://github.com/dpwgc/fraisedb","commit_stats":null,"previous_names":["dpwgc/fraisedb"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dpwgc/fraisedb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpwgc%2Ffraisedb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpwgc%2Ffraisedb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpwgc%2Ffraisedb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpwgc%2Ffraisedb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dpwgc","download_url":"https://codeload.github.com/dpwgc/fraisedb/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpwgc%2Ffraisedb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30075425,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T05:31:57.858Z","status":"ssl_error","status_checked_at":"2026-03-04T05:31:38.462Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cluster","db","go","kv","leveldb","metadata","nosql","raft"],"created_at":"2024-11-19T06:14:21.811Z","updated_at":"2026-03-04T07:01:35.821Z","avatar_url":"https://github.com/dpwgc.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FraiseDB\n## 基于Hashicorp Raft + LevelDB的分布式键值型数据库\n\n***\n\n## 功能\n* 键值写入/读取/范围查找\n* 名称空间隔离数据\n* 支持设置键值过期时间\n* 节点间数据保持强一致性\n* 直接通过HTTP接口操作数据\n* WS订阅监听某个/某批前缀相同的键值更新事件\n\n***\n\n## 设计\n\n### 节点上的数据读写流程\n* 数据读取：直接访问指定名称空间的LevelDB\n* 数据写入：先将数据写入到Raft集群的日志中，再通过日志消费将数据同步进LevelDB中\n* 订阅发布服务-事件广播：节点从Raft日志中同步数据时，会顺带将数据投放到通道中，然后广播推送给目前连接到该节点的特定订阅者客户端\n* 过期键值清理：对过期键值有两种处理方式，第一种是在查询时检测并删除，第二种是后台任务定时批量扫描删除\n\n![store](img/1.png)\n\n### 集群上的数据读写流程\n* 数据读取：可在任意节点进行\n* 数据写入：只能在领导者节点进行，如果在非领导者节点执行写入操作，会自动将该请求转发至当前集群的领导者节点执行\n\n![store](img/2.png)\n\n***\n\n## 使用方式\n* `1` 启动集群中的第一个节点时（或者单节点部署时），配置文件config.yaml中的node.first设为true，第一个节点默认为领导者节点\n* `2` 启动集群中的第二、第三...乃至后续节点时，配置文件config.yaml中的node.first设为false\n* `3` 在主节点（第一个启动的节点）上调用`新建节点`接口，将后续启动的节点依此加入到第一个节点所在的集群中\n#### `注` 程序入口：main.go，配置文件：config.yaml\n\n***\n\n## 打包方式\n```\ngo build main.go\n```\n\n***\n\n## HTTP接口文档\n\n***\n\n### node 集群节点相关\n\n***\n\n\u003e `POST` http://127.0.0.1:8000/v2/node 新建节点（必须在领导者节点上执行）\n#### 请求\nBody\n```json\n{\n  \"addr\": \"127.0.0.1\",\n  \"httpPort\": 7000,\n  \"tcpPort\": 6999\n}\n```\nhttpPort：节点的http端口号，集群外部对节点的操作都通过http接口进行；tcpPort：节点的tcp端口号，供内部Raft集群调度使用\n\n#### 成功响应\n```json\n{\n  \"code\": 1000,\n  \"data\": \"127.0.0.1:7000\"\n}\n```\ndata：节点的外部操作地址（http地址）\n\n***\n\n\u003e `DELETE` http://127.0.0.1:8000/v2/node/{endpoint} 删除节点（必须在领导者节点上执行）\n#### 请求\nURI\n```\nendpoint = 127.0.0.1:7000\n```\nendpoint：节点的外部操作地址（http地址）\n\n#### 成功响应\n```json\n{\n  \"code\": 1000\n}\n``` \n\n***\n\n\u003e `GET` http://127.0.0.1:8000/v2/nodes 获取节点列表\n#### 成功响应\n```json\n{\n  \"code\": 1000,\n  \"data\": [\n    {\n      \"endpoint\": \"127.0.0.1:8000\",\n      \"health\": true,\n      \"leader\": true\n    },\n    {\n      \"endpoint\": \"127.0.0.1:7000\",\n      \"health\": true,\n      \"leader\": false\n    }\n  ]\n}\n```\nhealth：是否健康；leader：是否是领导者节点\n\n***\n\n\u003e `GET` http://127.0.0.1:8000/v2/leader 获取领导者节点\n#### 成功响应\n```json\n{\n  \"code\": 1000,\n  \"data\": \"127.0.0.1:8000\"\n}\n```\n\n***\n\n\u003e `GET` http://127.0.0.1:8000/v2/config 获取当前节点配置（内部调用）\n#### 成功响应\n```json\n{\n  \"node\": {\n    \"first\": true,\n    \"addr\": \"127.0.0.1\",\n    \"tcpPort\": 7999,\n    \"httpPort\": 8000\n  },\n  \"store\": {\n    \"data\": \"./data\",\n    \"log\": \"./log\"\n  }\n}\n```\n\n***\n\n\u003e `GET` http://127.0.0.1:8000/v2/health 获取当前节点健康状态（内部调用）\n#### 成功响应\n```\n1\n```\n\n***\n\n### namespace 名称空间相关\n\n***\n\n\u003e `POST` http://127.0.0.1:8000/v2/namespace/{namespace} 新建名称空间\n#### 请求\nURI\n```\nnamespace = dev\n```\n#### 成功响应\n```json\n{\n  \"code\": 1000\n}\n``` \nnamespace：名称空间\n\n***\n\n\u003e `DELETE` http://127.0.0.1:8000/v2/namespace/{namespace} 删除名称空间\n#### 请求\nURI\n```\nnamespace = dev\n```\n#### 成功响应\n```json\n{\n  \"code\": 1000\n}\n``` \n\n***\n\n\u003e `GET` http://127.0.0.1:8000/v2/namespaces 获取名称空间列表\n#### 成功响应\n```json\n{\n  \"code\": 1000,\n  \"data\": [\n    \"default\",\n    \"dev\"\n  ]\n}\n``` \ndata：名称空间列表\n### kv 键值相关\n\n***\n\n\u003e `PUT` http://127.0.0.1:8000/v2/kv/{namespace}/{key} 更新键值\n#### 请求\nURI\n```\nnamespace = dev\nkey = test\n```\nBody\n```json\n{\n    \"value\": \"hello world\",\n    \"ttl\": 300\n}\n```\nkey：键；value：值；ttl：有效时间（秒）（当ttl=0时，键值不会过期）\n#### 成功响应\n```json\n{\n  \"code\": 1000,\n  \"data\": 1690475805\n}\n``` \ndata：键值的过期日期（秒级时间戳）\n***\n\n\u003e `DELETE` http://127.0.0.1:8000/v2/kv/{namespace}/{key} 删除键值\n#### 请求\nURI\n```\nnamespace = dev\nkey = test\n```\n#### 成功响应\n```json\n{\n  \"code\": 1000\n}\n``` \n\n***\n\n\u003e `GET` http://127.0.0.1:8000/v2/kv/{namespace}/{key} 根据键获取键值\n#### 请求\nURI\n```\nnamespace = dev\nkey = test\n```\n#### 成功响应\n\n```json\n{\n  \"code\": 1000,\n  \"data\": {\n    \"key\": \"test\",\n    \"value\": \"hello world\",\n    \"ddl\": 1690475805\n  }\n}\n``` \nddl：键值的过期日期（秒级时间戳）（ddl=0时表示该键值不会过期）\n***\n\n\u003e `GET` http://127.0.0.1:8000/v2/kvs/{namespace}/{key_prefix}?offset=0\u0026count=10 根据键前缀获取键值列表\n#### 请求\nURI\n```\nnamespace = dev\nkey_prefix = te\n```\nQuery\n```\noffset = 0\ncount = 10\n```\n#### 成功响应\n```json\n{\n  \"code\": 1000,\n  \"data\": [\n    {\n      \"key\": \"test\",\n      \"value\": \"hello world\",\n      \"ddl\": 1690475805\n    },\n    {\n      \"key\": \"test1\",\n      \"value\": \"hi world\",\n      \"ddl\": 0\n    }\n  ]\n}\n```\nkey_prefix：键的前缀；offset：分页游标；count：分页大小（count=0时返回当前游标后的所有数据）\n***\n\n### subscribe 订阅监听相关\n\n***\n\n\u003e `WS` ws://127.0.0.1:8000/subscribe/{namespace}/{key_prefix}/{client_id} 订阅监听某个/某批前缀相同的键值更新事件\n#### 请求\nURI\n```\nnamespace = dev\nkey_prefix = te\nclientId = 12he6fj48dhe36fu398rhf3hf392423g\n```\n#### 推送消息样式\n```json\n{\n  \"method\": 1,\n  \"key\": \"test\",\n  \"value\": \"hello world\",\n  \"ddl\": 1690475805\n}\n```\nclient_id：客户端id（自定义传入，必须唯一）；method：0-键值删除事件；1-键值写入事件\n***\n\n### HTTP接口失败响应格式\n```json\n{\n  \"code\": 1002,\n  \"error\": \"len(namespace) == 0\"\n}\n```\n失败响应码\n* 1001 接口入参异常响应码\n* 1002 服务处理异常响应码\n* 1003 集群调用异常响应码","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdpwgc%2Ffraisedb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdpwgc%2Ffraisedb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdpwgc%2Ffraisedb/lists"}