查询状态数据库

本主题包含的信息可帮助您了解如何查询存储区块链账本当前状态数据的状态数据库。

什么是状态数据库?

区块链账本的当前状态数据存储在状态数据库中。

开发 Oracle Blockchain Platform 链代码时,您可以通过执行富查询从状态数据库中提取数据。Oracle Blockchain Platform 通过使用 SQL 丰富的查询语法和 CouchDB 查找表达式来支持富查询。请参见 SQL Rich Query SyntaxCouchDB Rich Query Syntax

Hyperledger Fabric 不支持 SQL 丰富的查询。如果您的 Oracle Blockchain Platform 网络包含 Hyperledger Fabric 参与者,则需要确保执行以下操作:

  • 如果链代码包含 SQL 丰富的查询语法,则这些链代码仅安装在使用 Oracle Blockchain Platform 的成员对等节点上。

  • 如果需要在 Oracle Blockchain Platform 和 Hyperledger Fabric 对等节点上安装链代码,请在链代码中使用 CouchDB 语法,并确认 Hyperledger Fabric 对等节点已设置为使用 CouchDB 作为其状态数据库资料档案库。Oracle Blockchain Platform 可以处理 CouchDB。

Oracle Blockchain Platform 如何与 Berkeley DB 配合使用?

Oracle Blockchain Platform 使用 Oracle Berkeley DB 作为状态数据库。Oracle Blockchain Platform 基于 SQLite 扩展在 Berkeley DB 中创建关系表。此体系结构提供了一种可靠的高性能方法来验证 SQL 丰富的查询。

对于每个渠道链代码,Oracle Blockchain Platform 都会创建一个 Berkeley DB 表。此表存储状态信息数据,并至少包含一个名为 key 的键列和一个名为 valuevalueJson 的值列,具体取决于您是否使用 JSON 格式数据。

Column Name(列名) 类型 说明
key TEXT 状态表的关键字列。
value TEXT 状态表的值列。
valueJson TEXT 状态表的 JSON 格式值列。

请注意,valueJsonvalue 列是互斥的。因此,如果链代码为键分配 JSON 值,则 valueJson 列将存储该值,且值列将设置为 null。如果链代码为键分配非 JSON 值,则 valueJson 列将设置为 null,并且值列将保存该值。

状态数据库示例

以下是汽车经销商示例状态数据库中的键及其值的示例:

value valueJson
abg1234 null {"docType":"vehiclePart","serialNumber":"abg1234",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"airbag 2020","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
abg1235 null {"docType":"vehiclePart","serialNumber":"abg1235",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"airbag 4050","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
ser1236 null {"docType":"vehiclePart","serialNumber":"ser1236",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"seatbelt 10020","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
bra1238 null {"docType":"vehiclePart","serialNumber":"bra1238",“汇编程序”:"bobs-bits","assemblyDate":1502688979,"name":"brakepad 4200","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
dtrt10001 null {"docType": "vehicle", "chassisNumber": "dtrt10001", "manufacturer": "Detroit Auto", "model": "a coupe", "assemblyDate": 1502688979, "airbagSerialNumber": "abg1235", "owner": "Sam Dealer", "recall": false, "recallDate": 1502688979

控制台中的丰富查询

管理员可以从控制台运行和分析富查询。

  1. 转到控制台并选择渠道选项卡。
  2. 在通道表中,找到要运行查询的通道,单击通道更多操作按钮,然后单击分析富查询。此时将显示分析富查询对话框。
  3. 要对状态数据库运行富查询,请选择查询执行
    1. 对于链代码,选择部署到要查询的渠道的链代码。
    2. 对于对等节点,选择要查询的对等节点。
      只有当前组织中运行所选链代码的对等节点才可用。
    3. 对于富查询,输入要运行和分析的富查询。
      查询格式必须遵循富查询语法。有关富查询语法的更多信息,请参见 Supported Rich Query Syntax
    4. 对于结果行限制,将滑块移至要提取的最大结果行数。最多可以提取 50 行结果。
  4. 要获取查询的执行计划,请选择查询计划说明。查询执行计划是运行查询所执行的操作序列。
    1. 对于链代码,选择部署到要查询的渠道的链代码。
    2. 对于对等节点,选择要查询的对等节点。
    3. 对于收集,选择状态数据库或专用数据收集。
    4. 对于富查询,输入富查询。
      此查询不需要 explain 关键字。
      例如:select * from <state>
  5. 单击执行结果字段显示查询结果表或执行计划。要将结果表导出为 .csv 文件,请单击导出
    结果表大小限制为 1 MB。您可能需要细化查询,以避免超过此限制。

支持的富查询语法

Oracle Blockchain Platform 支持两种类型的丰富查询语法,可用于查询状态数据库:SQL 丰富的查询和 CouchDB 丰富的查询。

SQL 丰富查询语法

Berkeley DB JSON 扩展采用 SQL 函数的形式。

准备工作

请注意以下信息:

  • 您只能访问正在从中执行查询的渠道链代码 (<STATE>)。
  • 仅支持 SELECT 语句。
  • 无法修改状态数据库表。
  • 一个富查询表达式只能有一个 SELECT 语句。
  • 本主题中的示例只是一些可以编写丰富查询的方法。您可以访问查询 SQL 数据库的通常完整 SQL 语法。
  • 您有权访问 JSON1 扩展(SQLite 扩展)。请参阅 JSON1 扩展SQL As Understand by SQLite

如果您需要有关编写和测试链代码的更多信息,请参阅开发链代码

如何在查询中引用状态数据库

状态数据库表名在内部由 Oracle Blockchain Platform 管理,因此在编写链代码时不需要知道状态数据库的物理名称。

而必须使用 <STATE> 别名来引用表名。例如:select key, value from <STATE>

请注意,<STATE> 别名区分大小写,因此您可以使用 <state><STATE> 或类似 <StAtE> 的名称。

检索所有键

使用以下语法:

SELECT key FROM <STATE>

例如,如果使用此语法查询汽车经销商示例,则会得到以下键列表:

关键字

abg1234

abg1235

ser1236

bra1238

dtrt10001

检索按键按字母顺序排序的所有键和值

使用以下语法:

SELECT key AS serialNumber, valueJson AS details FROM  <state> ORDER BY key

例如,如果使用此语法查询汽车经销商示例,则会得到以下结果:

serialNumber 详细信息
abg1234 {"docType":"vehiclePart","serialNumber":"abg1234",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"airbag 2020","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
abg1235 {"docType":"vehiclePart","serialNumber":"abg1235",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"airbag 4050","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
bra1238 {"docType":"vehiclePart","serialNumber":"bra1238",“汇编程序”:"bobs-bits","assemblyDate":1502688979,"name":"brakepad 4200","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
dtrt10001 {"docType": "vehicle", "chassisNumber": "dtrt10001", "manufacturer": "Detroit Auto", "model": "a coupe", "assemblyDate": 1502688979, "airbagSerialNumber": "abg1235", "owner": "Sam Dealer", "recall": false, "recallDate": 1502688979
ser1236 {"docType":"vehiclePart","serialNumber":"ser1236",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"seatbelt 10020","owner":"Detroit Auto","recall":false,"recallDate":1502688979}

检索以 "abg" 开头的所有键和值

使用以下语法:

SELECT key AS serialNumber, valueJson AS details FROM <state> WHERE key LIKE 'abg%'SELECT key, value FROM <STATE>

例如,如果使用此语法查询汽车经销商示例,则会得到以下结果:

serialNumber 详细信息
abg1234 {"docType":"vehiclePart","serialNumber":"abg1234",“汇编程序”:"panama-parts","assemblyDate":"1502688979","name":"airbag 2020","owner":"Detroit Auto","recall":"false","recallDate":"1502688979"}
abg1235 {"docType":"vehiclePart","serialNumber":"abg1235",“汇编程序”:"panama-parts","assemblyDate":"1502688979","name":"airbag 4050","owner":"Detroit Auto","recall":"false","recallDate":"1502688979"}

检索包含由“底特律汽车”拥有的车辆部件的值的所有关键字

使用以下语法:

SELECT key FROM <state> WHERE json_extract(valueJson, '$.docType') = 'vehiclePart' AND json_extract(valueJson, '$.owner') = 'Detroit Auto'

例如,如果使用此语法查询汽车经销商示例,则会得到以下键列表:

关键字

abg1234

abg1235

ser1236

bra1238

检索“Sam 经销商”拥有的所有汽车的型号和制造商

使用以下语法:

SELECT json_extract(valueJson, '$.model') AS model, json_extract(valueJson, '$.manufacturer') AS manufacturer FROM <state> WHERE json_extract(valueJson, '$.docType') = 'vehicle' AND json_extract(valueJson, '$.owner') = 'Sam Dealer'

例如,如果使用此语法查询汽车经销商示例,则会得到以下结果:

模型 生产商
轿跑车 底特律汽车

如果状态值为 JSON 数组,则可以使用此语法检索“Sam Dealer”拥有的所有汽车的模型和制造商:

SELECT json_extract(j.value, '$.model') AS model, json_extract(j.value, '$.manufacturer') AS manufacturer FROM <state> s, json_each(json_extract(s.valueJson,'$')) j WHERE json_valid(j.value) AND json_extract(j.value, '$.owner') = 'Sam Dealer'

CouchDB 富查询语法

如果要将包含 CouchDB 语法的链代码迁移到 Oracle Blockchain Platform ,或者需要编写链代码以安装在参与 Oracle Blockchain Platform 网络的 Hyperledger Fabric 对等节点上,请使用本主题中的信息。

如果您要编写新的链代码,Oracle 建议您使用 SQL 丰富的查询来利用 Oracle Blockchain Platform 和 Berkeley DB 提供的性能优势。

如果您需要有关编写和测试链代码的更多信息,请参阅开发链代码

不支持的查询参数和选择器语法

Oracle Blockchain Platform 不支持 use_index 参数。如果使用,Oracle Blockchain Platform 将忽略此参数,它将自动选择在相关 StateDB 上定义的索引。

Parameter (参数) 类型 说明
use_index json 指示查询使用特定索引。

检索汽车的所有型号、制造商和车主,并按车主排序

使用此表达式:

{ 
  "fields": ["model", "manufacturer", "owner"], 
  "sort": [   
    "owner" 
   ]
}

检索“Sam 经销商”拥有的所有汽车的型号和制造商

使用此表达式:

{ 
  "fields": ["model", "manufacturer"], 
  "selector": {   
    "docType"  : "vehicle",
     "owner" : "Sam Dealer" 
  }
}

状态数据库索引

状态数据库可以包含大量数据。在这种情况下,Oracle Blockchain Platform 使用索引来改善数据访问。

默认索引

部署链代码后,Oracle Blockchain Platform 将创建两个索引。

  • 键索引—在键列上创建。

  • 值索引 - 在值列上创建。

定制索引

在某些情况下,您可能需要创建定制索引。可以使用可在状态表的上下文中解析的任何表达式定义这些索引。针对 Berkeley DB 创建的定制索引依赖于 SQLite 语法,但是它们遵循 Hyperledger Fabric 提供的相同 CouchDB 实现。

请注意,您可以使用定制索引显著提高 WHERE 和 ORDER BY 语句在大型数据集上的性能。由于使用定制索引会减慢数据插入速度,因此应谨慎使用它们。

每个自定义索引被定义为一个表达式数组,它支持复合索引,在一个文件中表示为 JSON 文档(请注意,每个文件有一个索引)。必须在以下目录结构中名为 "indexes" 的文件夹中将此文件与链代码打包:statedb/relationaldb/indexes。请参见 How to add CouchDB indexes during chaincode installation

定制索引示例

本节中的自定义索引示例使用汽车经销商示例。

示例 1 —此示例为在 WHERE 和 ORDER BY 表达式的上下文中使用 json_extract 表达式编制了索引。

{"indexExpressions": ["json_extract(valueJson, '$.owner')"]}

例如:

SELECT … FROM … ORDER BY json_extract(valueJson, '$.owner')

示例 2 —此示例索引了 WHERE 和 ORDER BY 表达式上下文中两个 json_extract 表达式的复合使用。

{"indexExpressions": ["json_extract(valueJson, '$.docType')", "json_extract(valueJson, '$.owner')"]}

例如:

SELECT … FROM … WHERE json_extract(valueJson, '$.docType') = 'vehiclePart' AND json_extract(valueJson, '$.owner') = 'Detroit Auto'

示例 3 —此示例创建两个索引:示例 1 中所述的索引和示例 2 中所述的索引。请注意,每个 JSON 结构都需要包含在单独的文件中。每个文件都描述一个索引:一个简单的索引(如示例 1),或者一个复合索引(如示例 2)。

索引 1:{"indexExpressions": ["json_extract(valueJson, '$.owner')"]}

索引 2:{"indexExpressions": ["json_extract(valueJson, '$owner')", "json_extract(valueJson, '$.docType')"]}

在以下示例中,索引 2 应用于查询的 WHERE 部分中的 AND 表达式,而索引 1 应用于 ORDER BY 表达式:

SELECT … FROM … WHERE json_extract(valueJson, '$.docType') = 'vehiclePart' AND json_extract(valueJson, '$.owner') = 'Detroit Auto' ORDER BY json_extract(valueJson, '$.owner')

JSON 文档格式

JSON 文档必须采用以下格式:

{"indexExpressions": [expr1, ..., exprN]}

例如:

{"indexExpressions": ["json_extract(valueJson, '$.owner')"]}

富查询验证的差异

在某些情况下,具有 CouchDB 丰富查询的标准 Hyperledger Fabric 和 Oracle Berkeley DB 丰富查询的行为会有所不同。

在带有 CouchDB 的标准超级账本架构中,查询返回的每个键和值对都将添加到事务处理的读取集中,并在验证时进行验证,而不重新执行查询。在 Berkeley DB 中,返回的键和值对不会添加到读取集中,但富查询的结果会散列在 Merkle 树中,并在验证时根据查询的重新执行进行验证。

本机超级账本架构 (Hyperledger Fabric) 不会对丰富的查询提供数据保护。但是,Berkeley DB 包含的功能通过将 Merkle 树散列值添加到读取集中、重新执行富查询以及在验证阶段重新计算 Merkle 树值来保护和验证富查询。请注意,由于在 Oracle Blockchain Platform 中使用 Berkeley DB 进行验证更准确,因此有时会标记链代码调用以进行更频繁的虚拟件读取。