• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 通过向量数据库实现数据持久化 (C/C++)
2<!--Kit: ArkData-->
3<!--Subsystem: DistributedDataManager-->
4<!--Owner: @cuile44; @baijidong-->
5<!--Designer: @houpengtao1-->
6<!--Tester: @logic42-->
7<!--Adviser: @ge-yafang-->
8
9
10## 场景介绍
11
12向量数据库是一种支持存储、管理和检索向量数据的数据库,也支持标量的关系型数据处理。数据类型"floatvector"用来存储数据向量化的结果,从而实现对这些数据的快速检索和相似性搜索‌。</br>
13从API version 18开始,支持通过向量数据库实现数据持久化。
14
15## 基本概念
16
17- **结果集**:指用户查询之后的结果集合,可以对数据进行访问。结果集提供了灵活的数据访问方式,可以更方便地拿到用户想要的数据。
18- **floatvector**:该数据类型表示向量数据,例如[1.0, 3.0, 2.4, 5.1, 6.2, 11.7]。
19
20## 约束限制
21
22- 系统默认日志方式是[WAL](data-terminology.md#wal模式)(Write Ahead Log)模式,系统默认落盘方式是[FULL模式](data-terminology.md#full模式)。
23
24- 数据库中默认有4个读连接和1个写连接,线程获取到空闲读连接时,即可进行读取操作。当没有空闲读连接时,会创建新的读连接。
25
26- 为保证数据的准确性,数据库同一时间只能支持一个写操作,并发的写操作会串行执行。
27
28- 当应用被卸载完成后,设备上的相关数据库文件及临时文件会被自动清除。
29
30- 为保证插入并读取数据成功,建议一条数据不要超过2M。超出该大小,插入成功,读取失败。
31
32## 规格限制
33
34详情见[规格限制](data-persistence-by-vector-store.md#规格限制)。
35
36## 接口说明
37
38详细的接口说明请参考[RDB](../reference/apis-arkdata/capi-rdb.md)。
39
40| 接口名称 | 描述 |
41| -------- | -------- |
42| int OH_Rdb_SetDbType(OH_Rdb_ConfigV2 *config, int dbType) | 设置数据库类型。 |
43| OH_Rdb_Store *OH_Rdb_CreateOrOpen(const OH_Rdb_ConfigV2 *config, int *errCode) | 获得一个相关的OH_Rdb_Store实例(调用OH_Rdb_SetDbType设置dbType为RDB_CAYLEY),操作向量数据库。 |
44| int OH_Rdb_ExecuteV2(OH_Rdb_Store *store, const char *sql, const OH_Data_Values *args, OH_Data_Value **result) | 执行有返回值的SQL语句,用来执行写操作,支持参数绑定,语句中的各种表达式和操作符之间的关系操作符号(例如=、>、<)不超过1000个。|
45| int OH_Rdb_ExecuteByTrxId(OH_Rdb_Store *store, int64_t trxId, const char *sql) | 使用指定的事务ID执行无返回值的SQL语句,事务ID为0时不使用事务。 |
46| OH_Cursor *OH_Rdb_ExecuteQuery(OH_Rdb_Store *store, const char *sql) | 根据指定SQL语句查询数据库中的数据。 |
47| OH_Cursor *OH_Rdb_ExecuteQueryV2(OH_Rdb_Store *store, const char *sql, const OH_Data_Values *args) | 根据指定SQL语句查询数据库中的数据,支持参数绑定,语句中的各种表达式和操作符之间的关系操作符号(例如=、>、<)不超过1000个。 |
48| int OH_Rdb_DeleteStoreV2(const OH_Rdb_ConfigV2 *config) | 删除数据库。 |
49| int OH_Cursor_GetFloatVectorCount(OH_Cursor *cursor, int32_t columnIndex, size_t *length) | 获取当前行中指定列的浮点数数组大小。 |
50| int OH_Cursor_GetFloatVector(OH_Cursor *cursor, int32_t columnIndex, float *val, size_t inLen, size_t *outLen) | 以浮点数数组的形式获取当前行中指定列的值,其中inLen不能小于实际的数组大小。 |
51
52## 开发步骤
53
54**添加动态链接库**
55
56CMakeLists.txt中添加以下lib。
57
58```txt
59libnative_rdb_ndk.z.so
60```
61
62**头文件**
63
64```c++
65#include <database/data/oh_data_values.h>
66#include <database/rdb/oh_cursor.h>
67#include <database/rdb/relational_store.h>
68```
69
701. 判断当前系统是否支持向量数据库,若不支持,则表示当前系统不具备向量数据库能力。示例代码如下:
71
72   ```c
73   int numType = 0;
74   // 如果numType为2则支持向量数据库,为1则不支持向量数据库
75   OH_Rdb_GetSupportedDbType(&numType);
76   ```
77
782. 当前系统支持向量数据库时,获取OH_Rdb_Store实例。示例代码如下:
79
80   ```c
81   // 创建OH_Rdb_Config对象
82   OH_Rdb_ConfigV2 *config = OH_Rdb_CreateConfig();
83   // 该路径为应用沙箱路径
84   OH_Rdb_SetDatabaseDir(config, "xxx");
85   // 数据库文件名
86   OH_Rdb_SetStoreName(config, "rdb_vector_test.db");
87   // 应用包名
88   OH_Rdb_SetBundleName(config, "xxx");
89   // 数据库是否加密
90   OH_Rdb_SetEncrypted(config, false);
91   // 数据库文件安全等级
92   OH_Rdb_SetSecurityLevel(config,   OH_Rdb_SecurityLevel::S1);
93   // 数据库文件存放的安全区域
94   OH_Rdb_SetArea(config, RDB_SECURITY_AREA_EL1);
95   // 数据库类型
96   OH_Rdb_SetDbType(config, RDB_CAYLEY);
97
98   // 获取OH_Rdb_Store实例
99   int errCode = 0;
100   OH_Rdb_Store *store_ = OH_Rdb_CreateOrOpen(config, &errCode);
101   ```
102
1033. 获取到OH_Rdb_Store后,建表并插入数据。
104
105   > **说明:**
106   >
107   > 向量数据库没有显式的flush操作实现持久化,数据插入即保存在持久化文件。
108
109   示例代码如下:
110
111   ```c
112   char createTableSql[] = "CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, data1 floatvector(2));";
113   // 执行建表语句
114   OH_Rdb_ExecuteByTrxId(store_, 0, createTableSql);
115
116   // 不使用参数绑定插入数据
117   OH_Rdb_ExecuteV2(store_, "INSERT INTO test (id, data1) VALUES (0, '[3.4, 4.5]');", nullptr, nullptr);
118   // 使用参数绑定插入数据
119   OH_Data_Values *values = OH_Values_Create();
120   OH_Values_PutInt(values, 1);
121   float test[] = { 1.2, 2.3 };
122   size_t len = sizeof(test) / sizeof(test[0]);
123   OH_Values_PutFloatVector(values, test, len);
124   char insertSql[] = "INSERT INTO test (id, data1)   VALUES (?, ?);";
125   OH_Rdb_ExecuteV2(store_, insertSql, values, nullptr);
126   OH_Values_Destroy(values);
127   ```
128
1294. 获取到OH_Rdb_Store后,修改或删除数据。示例代码如下:
130
131   ```c
132   // 不使用参数绑定修改数据
133   OH_Rdb_ExecuteV2(store_, "update test set data1 = '[5.1, 6.1]' where id = 0;", nullptr, nullptr);
134
135   // 使用参数绑定修改数据
136   float test1[2] = { 5.5, 6.6 };
137   OH_Data_Values *values1 = OH_Values_Create();
138   OH_Values_PutFloatVector(values1, test1, 2);
139   OH_Values_PutInt(values1, 1);
140   OH_Rdb_ExecuteV2(store_, "update test set data1 = ? where id = ?", values1, nullptr);
141   OH_Values_Destroy(values1);
142
143   // 不使用参数绑定删除数据
144   OH_Rdb_ExecuteV2(store_, "delete from test where id = 0", nullptr, nullptr);
145
146   // 使用参数绑定删除数据
147   OH_Data_Values *values2 = OH_Values_Create();
148   OH_Values_PutInt(values2, 1);
149   OH_Rdb_ExecuteV2(store_, "delete from test where id = ?", values2, nullptr);
150   OH_Values_Destroy(values2);
151   ```
152
1535. 获取到OH_Rdb_Store后,查询数据。
154
155   > **说明:**
156   >
157   > 当应用完成查询数据操作,不再使用结果集(OH_Cursor)时,请及时调用destroy方法关闭结果集,释放系统为其分配的内存。
158
159   示例代码如下:
160
161   ```c
162   // 不使用参数绑定查询数据
163   OH_Cursor *cursor = OH_Rdb_ExecuteQueryV2(store_, "select * from test where id = 1;", nullptr);
164   int rowCount = 0;
165   cursor->getRowCount(cursor, &rowCount);
166   cursor->goToNextRow(cursor);
167   size_t count = 0;
168   // floatvector数组是第二列数据
169   OH_Cursor_GetFloatVectorCount(cursor, 1, &count);
170   float test2[count];
171   size_t outLen;
172   OH_Cursor_GetFloatVector(cursor, 1, test2, count, &outLen);
173   cursor->destroy(cursor);
174
175   // 使用参数绑定查询数据
176   char querySql[] = "select * from test where id = ?;";
177   OH_Data_Values *values3 = OH_Values_Create();
178   OH_Values_PutInt(values3, 1);
179   cursor = OH_Rdb_ExecuteQueryV2(store_, querySql, values3);
180   OH_Values_Destroy(values3);
181   cursor->destroy(cursor);
182
183   // 子查询,创建第二张表
184   OH_Rdb_ExecuteV2(store_, "CREATE TABLE IF NOT EXISTS test1(id text PRIMARY KEY);", nullptr, nullptr);
185   cursor = OH_Rdb_ExecuteQueryV2(store_, "select * from test where id in (select id from test1);", nullptr);
186   cursor->destroy(cursor);
187
188   // 聚合查询
189   cursor = OH_Rdb_ExecuteQueryV2(store_, "select * from test where data1 <-> '[1.0, 1.0]' > 0 group by id having max(data1 <=> '[1.0, 1.0]');", nullptr);
190   cursor->destroy(cursor);
191
192   // 多表查询
193   cursor = OH_Rdb_ExecuteQueryV2(store_, "select id, data1 <-> '[1.5, 5.6]' as distance from test union select id, data1 <-> '[1.5, 5.6]' as distance from test order by distance limit 5;", nullptr);
194   cursor->destroy(cursor);
195   ```
196
1976. 创建视图并执行查询。示例代码如下:
198
199   ```c
200   OH_Rdb_ExecuteV2(store_, "CREATE VIEW v1 as select * from test where id > 0;", nullptr, nullptr);
201   OH_Cursor *cursor = OH_Rdb_ExecuteQueryV2(store_, "select * from v1;", nullptr);
202   cursor->destroy(cursor);
203   ```
204
2057. ‌使用向量索引进行查询,提升查询效率。
206
207   向量数据库索引‌是一种以向量作为键的索引机制,旨在提供高效且快速的搜索能力。
208
209   当前支持的向量索引基础语法和扩展语法如下:
210
211   - 基础语法如下:
212
213     ```sql
214     // index_name为索引名称,index_type是索引类型,dist_function是索引距离度量类型
215     CREATE INDEX [IF NOT EXISTS] index_name ON table_name USING index_type (column_name dist_function);
216
217     DROP INDEX table_name.index_name;
218     ```
219   - 扩展语法如下:
220
221     ```sql
222     CREATE INDEX [基础语法] [WITH(parameter = value [, ...])];
223     ```
224
225   **表1** 索引类型(index_type)
226
227   | 类型      | 备注说明                                                     |
228   | --------- | ------------------------------------------------------------ |
229   | gsdiskann | 适用于处理高维稠密向量数据,如文本嵌入、图像特征等。         |
230
231   **表2** 索引距离度量类型(dist_function)
232
233   | 类型   | 计算符号 | 备注说明   |
234   | ------ | -------- | ---------- |
235   | L2     | <->      | 欧式距离。|
236   | COSINE | <=>      | 余弦距离。|
237
238   **表3** 扩展语法参数(parameter)
239
240   | 参数名称   | 取值范围和约束 | 备注说明   |
241   | ------ | -------- | ---------- |
242   | QUEUE_SIZE | 设置范围是[10, 1000],默认值 20。     | 代表创建索引搜索近邻的时候候选队列的长度,queue_size越大,构建速度降低,召回率有略微提升。 |
243   | OUT_DEGREE | 设置范围是[1, 1200] ,默认值 60。      | 邻居节点出度数量。out_degree与pageSize也有关系,out_degree的数量超过pageSize的存储范围将报错GRD_INVALID_ARGS。|
244
245   > **说明:**
246   >
247   > - 删除索引的时候需要指定表名称,即Drop Index table.index_name248   >
249   > - 随表一起创建的索引不能删除,如建表时创建的主键。
250   >
251   > - 向量索引的命中条件。必须是ORDER BY + LIMIT类型的查询,ORDER BY只有一个排序条件,这个条件是向量距离条件;ORDER BY与DESC连用,不会使用向量索引;查询距离度量与创建索引时的度量需要保持一致,例如创建向量索引时使用L2,在查询时使用<->进行度量才可以命中索引。
252
253   示例代码如下:
254
255   ```c
256   // 基础用法,创建的索引名称为diskann_l2_idx,索引列为repr,类型为gsdiskann,距离度量类型为L2
257   OH_Rdb_ExecuteV2(store_, "CREATE INDEX diskann_l2_idx ON test USING GSDISKANN(data1 L2);", nullptr, nullptr);
258
259   // 删除表test中的diskann_l2_idx索引
260   OH_Rdb_ExecuteV2(store_, "DROP INDEX test.diskann_l2_idx;", nullptr, nullptr);
261
262   // 扩展语法,设置QUEUE_SIZE为20,OUT_DEGREE为50
263   OH_Rdb_ExecuteV2(store_, "CREATE INDEX diskann_l2_idx ON test USING GSDISKANN(repr L2) WITH (queue_size=20, out_degree=50);", nullptr, nullptr);
264   ```
265
2668. 配置数据老化功能。当应用的数据需要定期清理时,可以按时间或空间配置数据老化策略,从而实现数据的自动化清理。
267
268   语法如下所示:
269
270   ```sql
271   CREATE TABLE table_name(column_name type [, ...]) [WITH(parameter = value [, ...])];
272   ```
273
274   其中,parameter为可配置的参数,value为对应取值,具体情况见下表。
275
276   **表4** 数据老化策略参数(parameter)
277
278   | 参数名称 | 必填 | 取值范围和使用说明 |
279   | ------ | -------- | ---------- |
280   | time_col | 是 | 列名。类型必须为整数且不为空。 |
281   | interval | 否 | 老化任务线程的执行间隔时间,超过该时间后执行写操作,触发老化任务,删除符合老化条件的数据;若在间隔时间内执行写操作,不会触发老化任务。取值范围是[5 second, 1 year],时间单位支持second、minute、hour、day、month、year,不区分大小写或复数形式(1 hour和1 hours均可),默认是1 day。 |
282   | ttl | 否 | 数据保留时间。取值范围是[1 hour, 1 year],时间单位支持second、minute、hour、day、month、year,不区分大小写或复数形式(1 hour和1 hours均可),默认是3 month。 |
283   | max_num | 否 | 数据量限制。取值范围是[100, 1024],默认是1024。老化任务在执行完过期数据删除后,如剩余表内数据超过max_num行,则会找到距离过期时间最近的时间点,删除该时间点对应的所有数据,直到数据量少于max_num。 |
284
285   时间相关参数会按数值换算为秒作为原子单位,取值规则如下所示:
286
287   | 单位 | 向下换算为秒取值 |
288   | ------ | -------- |
289   | year | 365 * 24 * 60 * 60 |
290   | month | 30 * 24 * 60 * 60 |
291   | day | 24 * 60 * 60 |
292   | hour | 60 * 60 |
293   | minute | 60 |
294
295   例如配置`ttl = '3 months'`,实际ttl会被换算为`3 * (30 * 24 * 60 * 60) = 7776000 seconds`。
296
297   示例代码如下:
298
299   ```c
300   // 每隔五分钟执行写操作后,会触发数据老化任务
301   OH_Rdb_ExecuteV2(store_, "CREATE TABLE test2(rec_time integer not null) WITH (time_col = 'rec_time', interval = '5 minute');", nullptr, nullptr);
302   ```
303
3049. 配置数据压缩功能。该功能在建表时配置,可以压缩数据类型为text的列数据。
305
306   从API version 20开始,支持数据压缩功能。
307
308   语法如下所示:
309
310   ```sql
311   CREATE TABLE table_name(content text [, ...]) [WITH(compress_col = 'content')];
312   ```
313
314   其中,compress_col为必填参数,value是类型为text的数据列名,可以与数据老化功能同时配置。
315
316   示例代码如下:
317
318   ```c
319   // content列配置了数据压缩,并且配置了数据老化。
320   OH_Rdb_ExecuteV2(store_, "CREATE TABLE IF NOT EXISTS test3 (time integer not null, content text) with (time_col = 'time', interval = '5 minute', compress_col = 'content');", nullptr, nullptr);
321   ```
322
32310. 删除数据库。示例代码如下:
324
325    ```c
326    OH_Rdb_CloseStore(store_);
327    OH_Rdb_DeleteStoreV2(config);
328    ```