API 交互

API 交互

UrnaDB 内部提供了多种数据结构抽象,例如 Table 、Record 、 Variant 、Lock 类型,这些数据类型对应着常见的业务代码所需使用的数据结构。后续的文档统一称之为 NameSpace 命名空间,同一命名空间中存储都是同一类型的数据模型的数据,了解不同命名空间的数据模型及其特性,有助于开发者根据业务需求选择最合适的数据模型,从而高效实现上层业务功能。

Important

客户端与 UrnaDB 服务端之间的数据交互通过 PQL 协议,PQL(Patch Query Language)是一种基于 HTTP + JSON 的数据操作与查询协议,为客户端与服务器之间的结构化数据交换提供了统一、简单且可扩展的接口,任何能够发起 HTTP 请求的软件都可以作为 PQL 客户端;以下内容将介绍如何使用 PQL API 与服务器进行数据交换示例。

PQL 协议统一返回的数据格式为 JSON 格式,HTTP 请求统一响应格式如下:

{
    "status": "success",
    "message": "server output text message",
    "data": ...
}

JSON Body 包括 3 个字段 statusmessagedata 字段,相关字段作用说明如下表格:

字段 类型 描述
status 枚举 返回状态,值为 successerror ,可根据此判断操作是否成功
message 字符串 给人类阅读的消息,方便客户端显示错误提示
data JSON 对象或数组 返回的具体数据,可以是 JSON 对象也可以是数组类型

📈 Table 命名空间

Tip

Table 命名空间的结构类似关系数据库 SQL 中的一张表结构,一旦创建完成之后 Table 会为每条记录分配一个全局唯一递增的 t_id 索引,Table 的每列可以存储任何有映射关系的半结构化数据,例如编程语言中的 struct 和 class 字段都可以使用 Table 进行存储。相比于关系数据库 SQL 中表有更高的灵活性,不需要提前定义表 schema 结构,可以在使用的过程中灵活的动态添加、删除和修改这些记录字段。

创建一张名为 users 的 Table 命名空间,也可以称之为数据表。类似于关系数据库 SQL 中的 CREATE TABLE ... { ... } 语句,但区别于不需要提前定义表 schema 结构,代码如下:

curl -X PUT http://192.168.31.221:2668/tables/users \
  -H "Auth-Token: 73suyb7iNeZsmqhvaxgEn7Ug2"

如果上面操作执行成功,会创建一个 users 的命名空间,这个 users 命名空间后续可以存放更多数据记录,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "table created successfully"
}

Important

在创建命名空间的时候可以指定该命名空间的生命周期,生命周期是指该命名空间创建之后能存活多久,以秒为单位,当到达期限后 UrnaDB 的垃圾回收器会主动删除该命名空间释放其占用的存储空间。

例如在创建 users 时通过 ttl 字段来指定它的生命周期为 1800 秒,在 1800 秒之后会被垃圾回收器主动删除,代码如下:

curl -X PUT "http://192.168.31.221:2668/tables/users" \
  -H "Auth-Token: 73suyb7iNeZsmqhvaxgEn7Ug2" \
  -H "Content-Type: application/json" \
  -d '{
    "ttl": 1800
  }'

下面是 Table 命名空间中的 Rows 结构的完整 JSON 抽象,Table 命名空间中的每条数据记录的 schema 布局是不同的,其中的 t_id 为数据库主动分配的自增主键:

{
    "table": {
        "1": {
            "active": true,
            "age": 25,
            "name": "Leon Ding",
            "score": 95.5,
            "tags": [
                "admin",
                "user"
            ]
        },
        "2": {
            "active": false,
            "age": 30,
            "name": "Alice Wang",
            "config": {
                "font": 14,
                "theme": "dark"
            },
            "email": "example@xxx.com"
        },
        "3": {}
    },
    "t_id": 4
}

users 命名空间中插入一条数据记录,这条操作类似于关系数据库 SQL 中的 INSERT ... INTO ... VALUES ... 语句,代码如下:

curl -X POST "http://192.168.101.252:2668/tables/users/rows" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI" \
  -H "Content-Type: application/json" \
  -d '{
    "rows": {
      "name": "Leon Ding",
      "age": 25,
      "active": true,
      "score": 95.5,
      "tags": ["admin", "user"],
      "meta": {
        "editor": "system"
      }
    }
  }'

上面命令会执行成功之后会返回对应记录的主键 t_id 字段,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "table rows insert successfully",
    "data": {
        "t_id": 1
    }
}

users 的命名空间中查询所有的数据记录,类似于关系数据库 SQL 中的 SELECT * FROM ... 语句,也类似于其他文档型数据库 MongoDB 中的 findAll 操作,代码如下:

curl -X GET "http://192.168.31.221:2668/tables/users" \
  -H "Auth-Token: 73suyb7iNeZsmqhvaxgEn7Ug2"

如果上面操作执行成功会返回该命名空间中所有的数据记录,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "table queried successfully",
    "data": {
        "1": {
            "active": true,
            "age": 25,
            "meta": {
                "editor": "system"
            },
            "name": "Leon Ding",
            "score": 95.5,
            "tags": [
                "admin",
                "user"
            ]
        }
    }
}

从名为 users 的命名空间中查询一条记录,类似于关系数据库 SQL 中的 SELECT * FROM ... WHERE ... 的语句,查询条件是表中 name 字段的值为 Leon Ding 的记录,代码如下:

curl -X GET "http://192.168.31.221:2668/tables/users/rows" \
  -H "Auth-Token: ex224JWgK5H6sKhpNk5ZbWKzM" \
  -H "Content-Type: application/json" \
  -d '{
    "wheres": {
      "name": "Leon Ding"
    }
  }'

如果上面操作执行成功会返回该命名空间中匹配到的数据记录,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "table queried rows successfully",
    "data": [
        {
            "active": true,
            "age": 25,
            "meta": {
                "editor": "system"
            },
            "name": "Leon Ding",
            "score": 95.5,
            "tags": [
                "admin",
                "user"
            ]
        }
    ]
}

根据指定的筛选条件,对 users 命名空间中的数据记录进行部分字段的内容更新操作,这个操作类似于关系数据库 SQL 中的 UPDATE ... SET ... WHERE ... 语句,代码示例如下:

curl -X PATCH "http://192.168.31.221:2668/tables/users" \
  -H "Auth-Token: ex224JWgK5H6sKhpNk5ZbWKzM" \
  -H "Content-Type: application/json" \
  -d '{
    "wheres": {
      "name": "Leon Ding",
      "active": true
    },
    "sets": {
      "age": 26,
      "name": "Leon",
      "meta": {
        "editor": "system"
      }
    }
  }'

如果上面操作执行成功,会修改该命名空间中所匹配到数据记录中的值,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "table rows patched successfully"
}

users 命名空间中已有的数据记录执行条件删除操作,类似于关系数据库 SQL 中的 DELETE FROM ... WHERE ... 的语句,代码如下:

curl -X DELETE "http://192.168.31.221:2668/tables/users/rows" \
  -H "Auth-Token: NjqwUvtNP6XQGeABhgwXIDcNw" \
  -H "Content-Type: application/json" \
  -d '{
    "wheres": {
      "name": "Leon Ding"
    }
  }'

如果上面操作执行成功,会删除该命名空间中所匹配到数据记录,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "table rows remove successfully"
}

下面操作会删除 users 命名空间,这意味着 users 命名空间中存储的数据记录都会被删除,类似于关系数据库 SQL 中的 DROP TABLE ... 语句,代码如下:

curl -X DELETE "http://192.168.31.221:2668/tables/users" \
  -H "Auth-Token: 43CmqMY6r7jndrjUC7cxyJ1SV"

如果上面操作执行成功会删除该命名空间所有数据,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "table deleted successfully"
}

Warning

Table 是一组多个对象记录组成集合,同一个 Table 命名空间随着不断插入新的数据,会导致 Table 越来越大并且有锁的开销,多条记录共享一把 Table 锁有性能损失,所有不建议在单个 Table 命名空间中存储数据较多数据记录。

📂 Record 命名空间

Tip

Record 命名空间的结构类似 MongoDB 中的 Document 结构,Record 通常直接映射编程语言中的 class 的一条记录,OOP 面向对象编程中的对象可以直接映射为 Record 记录。在高并发场景下 Record 一条记录对应一把锁,有着事物处理性能高的优势。Record 一段创建就不能改了,适应场景就是更新不频繁的数据,例如社交论坛系统中帖子功能,一条帖子可以对应一条 Record 记录,如果更新了直接设置一条新的 Record 映射即可。

创建一条名为 142857 的 Record 命名空间,并设置所需存储的数据记录值。例如一张帖子的抽象,操作示例如下:

curl -X PUT "http://192.168.101.252:2668/records/142857" \
  -H "Content-Type: application/json" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI" \
  -d '{
    "record": {
      "title": "Hello UrnaDB: My First Post",
      "author": "Leon Ding",
      "content": "This is a forum post stored inside UrnaDB.\n\n",
      "category": "tech",
      "tags": ["nosql", "database"],
      "views": 123,
      "published": true
    },
    "ttl": 3600
  }'

上面命令执行成功之后 HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "record created successfully"
}

Important

这个 142857 可以看作是一篇文章的 POST ID 编号,业务层只需要提供这个 ID 就可以查询到这条帖子数据,类似于关系数据库中一条记录的唯一主键;同样在创建 Record 记录数据时可以指定 ttl 字段,ttl 的值单位是秒用来设置 Record 的生命周期。

在 Record 命名空间中查询一条名为 142857 的数据记录,操作示例如下:

curl -X GET http://192.168.31.221:2668/records/142857 \
  -H "Auth-Token: dw8PDCPRQcrIVIekL4UheS9ra"

如果执行成功会返回 142857 中存储的完整数据记录:

{
    "status": "success",
    "message": "record queried successfully",
    "data": {
        "author": "Leon Ding",
        "category": "tech",
        "content": "This is a forum post stored inside UrnaDB.\n\n",
        "published": true,
        "tags": [
            "nosql",
            "database"
        ],
        "title": "Hello UrnaDB: My First Post",
        "views": 123
    }
}

对已存在的 142857 数据记录查询某个 column 操作,类似于关系数据库 SQL 中的 SELECT column FROM ... 语句,例如搜索查询操作示例:

curl -X POST "http://192.168.101.252:2668/records/142857" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI" \
  -H "Content-Type: application/json" \
  -d '{
    "column": "tags"
  }'

如果上面请求执行成功,会返回 142857tags 字段所存储的数据记录,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "search completed successfully",
    "data": [
        [
            "nosql",
            "database"
        ]
    ]
}

对已经存在的 142857 记录执行删除操作,这个对应着关系数据库 SQL 中的 DROP TABLE ... 语句,代码如下:

curl -X DELETE "http://192.168.101.252:2668/records/142857" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI"

如果上面请求执行成功会删除对应的 142857 中所存储的数据记录,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "record deleted successfully"
}

📦 Variant 命名空间

Tip

Variant 命名空间是一个通用的动态数据容器,可用于存储多种基础数据类型,例如 String、Boolean、Integer 和 Double 等,它为不同类型的数据提供了统一的 API,使业务层能够以一致且灵活的方式进行操作。

在 Variant 命名空间中,具体的数据类型由首次写入的值决定,并在后续操作中保持一致,例如创建一个名为 views 的 Variant 命名空间,它的值是整数 Integer 基础数据类型,示例如下:

curl -X PUT "http://192.168.101.252:2668/variants/views" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI" \
  -H "Content-Type: application/json" \
  -d '{
    "variant": 0
  }'

如果上面命令执行成功会返回 views 命名空间初始值,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "variant created successfully",
    "data": {
        "variant": 0
    }
}

Important

同样在创建 Variant 命名空间时可以指定 ttl 来设置生命周期,其值单位为秒。

views 创建并初始化成功后,即可对其数值执行类似 Redis 中的 INCR 自增操作,示例如下:

curl -X POST "http://192.168.101.252:2668/variants/views" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI" \
  -H "Content-Type: application/json" \
  -d '{
    "delta": 1
  }'

如果 INCR 自增操作命令执行成功,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "variant incremented successfully",
    "data": {
        "variant": 1
    }
}

Important

请求体中的 delta 值可以是 -1 负数,这样就可以实现原子自减操作。如果 Variant 中存储的是 Boolen 和 String 这些操作就会失效,并返回类型错误信息。

获取名为 views 的命名空间中的值,示例如下:

curl -X GET "http://192.168.101.252:2668/variants/views" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI"

HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "variant queried successfully",
    "data": {
        "variant": 1
    }
}

🔐 Lock 命名空间

Tip

Lock 命名空间是一个分布式租期锁服务,用于在分布式环境中协调多个客户端对共享资源的访问,能实现类似于基于 Redis 和 ETCD 分布式锁服务的功能。Lock 命名空间特别适用于需要确保数据一致性的场景,如分布式任务调度、资源分配、配置更新等操作,为分布式应用提供了可靠的同步机制。

Lock 提供的它提供了以下核心功能:

  1. 互斥锁定:确保同一时间只有一个客户端能够获取特定的锁。
  2. 自动过期:支持设置锁的超时时间,防止死锁情况。
  3. 锁续期:允许持有锁的客户端延长锁的有效期。
  4. 非阻塞获取:支持尝试获取锁而不阻塞当前线程。
  5. 锁状态查询:可以查询锁的当前状态和持有者信息。

在 Lock 命名空间中创建一个名为 orders 的分布式租期锁,锁的名称通常对应需要保护的资源标识符,这样可以通过锁名直接关联到具体的业务资源。通过 orders 这个锁名,可以方便地查询锁的当前状态、持有者信息以及剩余租期时间,实现对订单相关操作的分布式同步控制,示例如下:

curl -X PUT "http://192.168.101.252:2668/locks/orders" -v \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI" \
  -H "Content-Type: application/json" \
  -d '{
    "ttl": 30
  }'

如果上锁成功会返回对应锁的 Token 凭证,这个 Token 是方便于客户端续租和解锁使用的,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "lock created successfully",
    "data": {
        "token": "01KBCNZY9C5XM8YDT835SHW4GN"
    }
}

当上锁成功后客户端可以安全地执行资源相关的业务操作,在有效租期内,客户端对资源的独占访问是受保护的。但在实际业务场景中客户端的操作时间可能超出预期的锁租期,为了防止锁自动过期导致其他客户端意外获取锁,当客户端需要延长持锁时间时,应主动对锁进行续租操作,确保在业务完成前始终保持对资源的独占控制,示例如下:

curl -X PATCH "http://192.168.101.252:2668/locks/orders" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "01KAXPB8D4MNMG731BBZ8MAV28"
  }'

如果对锁续租成功,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "lease acquired successfully",
    "data": {
        "token": "01KBCP1V7GF9CF27PCJDEAT961"
    }
}

客户端对持有的锁进行提前释放和解锁,解锁时同样需要带有锁对应的 Token 凭证,示例如下:

curl -X DELETE "http://192.168.101.252:2668/locks/orders" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "01KBCP1V7GF9CF27PCJDEAT961"
  }'

如果解锁成功,HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "lock deleted successfully"
}

🌡️ 指标监控

Tip

除了基础的命名空间数据操作接口之外,UrnaDB 还提供方便指标监控 HTTP API 接口,客户端可以通过此接口来获取服务端健康状态数据信息。

使用 GET 类型的 HTTP 请求发送至 UrnaDB 的 https://.../health 端点 API,即可返回具体的指标监控 JSON 数据信息:

curl -X GET "http://192.168.101.252:2668/health" \
  -H "Auth-Token: yv2PH82JXfm2UpScAQK37iJVI" \
  -H "Content-Type: application/json"

HTTP 会响应返回 JSON 格式的内容如下:

{
    "status": "success",
    "message": "server is healthy",
    "data": {
        "key_count": 1,
        "gc_state": 0,
        "disk_free": "27.44GB",
        "disk_used": "85.27GB",
        "disk_total": "112.71GB",
        "mem_free": "1.95GB",
        "mem_total": "8.00GB",
        "disk_percent": "75.65%",
        "space_total": "0.00GB"
    }
}

Important

尽量避免频繁调用 health 端点,health 的数据获取涉及到从用户态切换到内核态的系统调用操作,相关的 health 指标信息 JSON 值对应作用说明如下:

字段 作用描述
key_count 1 当前数据库中存储的键数量
gc_state 0 垃圾回收状态:0 表示初始状态,1 表示正在执行 ,2 表示空闲状态
disk_free 27.44GB 磁盘剩余空间
disk_used 85.27GB 已使用磁盘空间
disk_total 112.71GB 磁盘总容量
mem_free 1.95GB 系统可用内存
mem_total 8.00GB 系统总内存
disk_percent 75.65% 磁盘使用百分比
space_total 0.00GB 数据库记录存储总量,逻辑空间不包含 inode 占用

Warning

本篇文档将帮助开发者理解 UrnaDB 支持的数据结构及其对应的 HTTP API 设计,借助这些统一的接口,开发者可以基于任意编程语言实现自己的 SDK 或进一步扩展 UrnaDB 的能力。