UDB-FX安装使用文档

简介

UDB-FX是一种 NoSQL 数据库,将半结构化数据作为 JSON 或 BSON 文档存储。它适合存放嵌套结构的数据,如用户、产品、日志或订单信息等 ,与关系型数据库不同,文档数据库不需要预定义架构(schema)。相同集合(collection)中的文档可以拥有不同字段或结构,实现了灵活性和可扩展性,并通过UDB-FX-Proxy支持 MongoDB 5.0+协议,几乎可以完全替代MongoDB。

架构

alt text

UDB-FX-Proxy 高可用架构

alt text

  • UDB-FX-Proxy 和 UDB-TX 一台机器部署:适合开发或测试

  • 生产部署:推荐分离部署 + 多副本

  • 多实例部署(UDB-FX-Proxy + UDB-TX 主,UDB-FX-Proxy-read + UDB-TX 从)

  • 多个UDB-FX-Proxy java mongodb驱动不支持 standalone 多 host 模式,节点需要外部负载均衡 例如HAProxy、Nginx TCP 负载模式

安装

linux二进制包安装UDB-FX

安装数据库

准备

创建普通用户,比如 useradd udb

上传并解压安装包,比如 tar zxf soft.tar.gz -C /data/soft/ 解压到/data/soft/目录

修改安装包用户属性,比如 chown -R udb.udb /data/soft

确保普通用户对数据目录有可写权限

注意 使用winscp或者其他上传工具,尽量不要使用lszrz 此插件有不明问题,数据包一但超过一定大小就有几率会在上传时产生损坏,导致文件出错不可用。

启动安装程序
[root@localhost soft]# su udb #切换到普通用户
[udb@localhost soft]$ ./setup.sh
===============================================================================
Welcome
-------
This installer will guide you through the installation of UnvDB.
Version:  22.4
Web:      http://www.unvdb.com
Tel:      0755-84185890
You may quit the installer by typing "Exit",
or press <ENTER> key to continue:       #此段是说明回车就行
===============================================================================
License Agreement
-----------------
Installation and Use of UnvDB Requires Acceptance of the Following 
License Agreement:
UniversalDB (Shenzhen) Technology Co., Ltd.
"SOFTWARE END-USER LICENSE AGREEMENT"
The "software product" includes computer software, and may include 
associated media, documentation.
This "software product" includes any upgrade and supplemental 
materials to the original "software product" provided by UnvDB.
SOFTWARE PRODUCT LICENSE
The "software product" is under the protection of Copyrights 
and International Conventions.
You may disagree the license by typing "Exit",
or press <ENTER> key to continue:        #此段是用户协议同意回车就行不同意"Exit"
===============================================================================
Choose Data Folder
------------------
Please choose a data folder for this installation.
The data folder must be empty and writable.
Default Data Folder: /data/unvdb-data
Enter the absolute path, 
or press <ENTER> key to accept the default, 
    : /data/unvdb-data #此处输入数据存放目录,如果输入错误,请按 Ctrl+Backspace 删除
===============================================================================
Port
----
Please enter UnvDB listen port.
Port (Default: 5678): 
===============================================================================
Username
--------
Please enter UnvDB username.
Username (Default: unvdb): #管理员用户名
===============================================================================
Password
--------
Please enter password: #管理员密码
Password again
--------------
Please enter password again :  #再输入一次
===============================================================================
Ready To Install
----------------
It will start to install UnvDB.
Product Name: UnvDB v22.4
Data Folder: /data/unvdb-data
Port: 5678
Username: unvdb
You may quit the installer by typing "Exit",
or press <ENTER> key to continue: 
The files belonging to this database system will be owned by user "unvdb".
This user must also own the server process.
The database cluster will be initialized with locale "en_US.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".
Data page checksums are disabled.
fixing permissions on existing directory /data/unvdb-data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Asia/Shanghai
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
===============================================================================
Installation Complete
---------------------
Congratulations. UnvDB has been successfully installed to:
    /data/unvdb-data
It is a demo version now, free use for 90 days.
To run as official version,
Contact UniversalDB(Shenzhen) Technology Co.,Ltd.
And copy license file to the installed path.
If you want to register UnvDB as service, please run
    sudo /data/unvdb-data/service.sh
or start UnvDB without service, please run
    /data/unvdb-data/start.sh
载入变量并验证
[udb@localhost soft]# exit #返回root用户
[root@localhost soft]# /data/unvdb-data/service.sh #注册服务
[root@localhost soft]# systemctl start unvdb #启动服务
[root@localhost soft]# source /data/unvdb-data/env.sh #载入环境变量
[root@localhost soft]# ud_sql  #进入udb
如果不是默认用户请输入# ud_sql -U 用户名 unvdb  
由于ud_sql是登录默认用户所以不是默认用户使用的话会显示以下错误
[root@localhost soft]# ud_sql
ud_sql: error: connection to server at "localhost" (::1), port 5678 failed: FATAL:  role "unvdb" does not exist

[root@localhost ~]# ud_sql
ud_sql (22.4)
Type "help" for help.

unvdb=# \l
                              List of databases
   Name    | Owner | Encoding |   Collate   |    Ctype    | Access privileges 
-----------+-------+----------+-------------+-------------+-------------------
 template0 | unvdb | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 | =c/unvdb         +
           |       |          |             |             | unvdb=CTc/unvdb
 template1 | unvdb | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 | =c/unvdb         +
           |       |          |             |             | unvdb=CTc/unvdb
 unvdb     | unvdb | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 | 
(3 rows)
启动数据库
./start.sh
启动UDB_FX_PROXY

注:UDB_FX_PROXY 需要一个固有用户来与unvdb连接,一般使用setup时的用户密码和数据库。更改删除需要重新启动UDB_FX_PROXY

默认用户和数据库

./UDB-FX-Proxy --mode="normal" --listen-addr="0.0.0.0:27017" --postgresql-url='postgres://unvdb:<password>@127.0.0.1:5678/<database>'

Setup时手动设置用户密码和数据库需要

./UDB-FX-Proxy --mode="normal" --listen-addr="0.0.0.0:27017" --postgresql-url='postgres://<user>:<password>@127.0.0.1:<port>/<database>'
卸载数据库
  • 注意 请备份好数据再执行以下操作

systemctl stop {unvdb,udb_exporter,node_exporter,filebeat} #停止 数据库,数据库监控,服务器监控,日志文件收集 服务
rm -fr /lib/systemd/system/{unvdb.service,udb_exporter.service,filebeat.service,node_exporter.service} #删除服务文件
mv /data/unvdb-data/ /data/unvdb-data-del #转移数据目录,如确定不再需要也可 rm 删除
userdel -r udb #删除用户和目录,请提前备份用户目录内的数据
rm -fr /tmp/ud_autoctl #删除临时文件

UDB-FX数据库操作

  • 使用ud_sql连接数据库后

创建集合

ud_fxdb 提供 ud_fxdb_api.create_collection 函数可以在不创建数据库的情况下创建集合,实现BSON文档高效管理。

SELECT ud_fxdb_api.create_collection('ud_fxdb','patient');

CURD操作

插入

ud_fxdb_api.insert_one向ud_fxdb集合插入一条数据

select ud_fxdb_api.insert_one('ud_fxdb','patient', '{ "patient_id": "P001", "name": "Alice Smith", "age": 30, "phone_number": "555-0123", "registration_year": "2023","conditions": ["Diabetes", "Hypertension"]}');
select ud_fxdb_api.insert_one('ud_fxdb','patient', '{ "patient_id": "P002", "name": "Bob Johnson", "age": 45, "phone_number": "555-0456", "registration_year": "2023", "conditions": ["Asthma"]}');
select ud_fxdb_api.insert_one('ud_fxdb','patient', '{ "patient_id": "P003", "name": "Charlie Brown", "age": 29, "phone_number": "555-0789", "registration_year": "2024", "conditions": ["Allergy", "Anemia"]}');
select ud_fxdb_api.insert_one('ud_fxdb','patient', '{ "patient_id": "P004", "name": "Diana Prince", "age": 40, "phone_number": "555-0987", "registration_year": "2024", "conditions": ["Migraine"]}');
select ud_fxdb_api.insert_one('ud_fxdb','patient', '{ "patient_id": "P005", "name": "Edward Norton", "age": 55, "phone_number": "555-1111", "registration_year": "2025", "conditions": ["Hypertension", "Heart Disease"]}');

读取

使用 ud_fxdb_api.collection 在集合里检索文档

SELECT document FROM ud_fxdb_api.collection('ud_fxdb','patient');

在查询里使用过滤器

SET search_path TO ud_fxdb_api, ud_fxdb_core;
SET ud_fxdb_core.bsonUseEJson TO true;

SELECT cursorPage FROM ud_fxdb_api.find_cursor_first_page('ud_fxdb', '{ "find" : "patient", "filter" : {"patient_id":"P005"}}');

范围查询

SELECT cursorPage FROM ud_fxdb_api.find_cursor_first_page('ud_fxdb', '{ "find" : "patient", "filter" : { "$and": [{ "age": { "$gte": 10 } },{ "age": { "$lte": 35 } }] }}');

更新

ud_fxdb_api.update 更新age对应的patient值为P004.

select ud_fxdb_api.update('ud_fxdb', '{"update":"patient", "updates":[{"q":{"patient_id":"P004"},"u":{"$set":{"age":14}}}]}');

更新多个属性

SELECT ud_fxdb_api.update('ud_fxdb', '{"update":"patient", "updates":[{"q":{},"u":{"$set":{"age":24}},"multi":true}]}');

删除

使用ud_fxdb_api.delete

SELECT ud_fxdb_api.delete('ud_fxdb', '{"delete": "patient", "deletes": [{"q": {"patient_id": "P002"}, "limit": 1}]}');

集合管理

查询集合 ud_fxdb_api.list_collections_cursor_first_page

SELECT * FROM ud_fxdb_api.list_collections_cursor_first_page('ud_fxdb', '{ "listCollections": 1 }');

查询索引 ud_fxdb_api.list_indexes_cursor_first_pageud_fxdb_api.list_collections_cursor_first_page`.

列出 patient 集合上的所有索引

SELECT ud_fxdb_api.list_indexes_cursor_first_page('ud_fxdb','{"listIndexes": "patient"}');

ttl 索引默认通过pg_cron调度,可以在cron.job表上查询ttl索引信息

select * from cron.job;

索引

创建索引

ud_fxdb 使用 ud_fxdb_api.create_indexes_background 函数,可以在不中断数据操作的情况下创建索引

在ud_fxdb 的病人集合中创建 age 的单字段索引。

SELECT * FROM ud_fxdb_api.create_indexes_background('ud_fxdb', '{ "createIndexes": "patient", "indexes": [{ "key": {"age": 1},"name": "idx_age"}]}');

在 ud_fxdb 的病人集合中为 age 和 registration_year 字段创建复合索引。

SELECT * FROM ud_fxdb_api.create_indexes_background('ud_fxdb', '{ "createIndexes": "patient", "indexes": [{ "key": {"registration_year": 1, "age": 1},"name": "idx_regyr_age"}]}');

删除索引

使用ud_fxdb_api.drop_indexes 从集合中删除索引 The SQL command demonstrates how to drop the index named id_ab_1 from the first_collection collection of the ud_fxdb.

CALL ud_fxdb_api.drop_indexes('ud_fxdb', '{"dropIndexes": "patient", "index":"idx_age"}');

聚合Group by

ud_fxdb provides the ud_fxdb_api.aggregate_cursor_first_pagefunction, for performing aggregations over the document store.

统计各个注册年份下的病人数量

SELECT cursorpage FROM ud_fxdb_api.aggregate_cursor_first_page('ud_fxdb', '{ "aggregate": "patient", "pipeline": [ { "$group": { "_id": "$registration_year", "count_patients": { "$count": {} } } } ] , "cursor": { "batchSize": 3 } }');

按年份分桶

SELECT cursorpage FROM ud_fxdb_api.aggregate_cursor_first_page('ud_fxdb', '{ "aggregate": "patient", "pipeline": [ { "$bucket": { "groupBy": "$registration_year", "boundaries": ["2022","2023","2024"], "default": "unknown" } } ], "cursor": { "batchSize": 3 } }');

分组并收集每年所有病人的 unique conditions

SELECT cursorpage FROM ud_fxdb_api.aggregate_cursor_first_page('ud_fxdb', '{ "aggregate": "patient", "pipeline": [ { "$group": { "_id": "$registration_year", "conditions": { "$addToSet": { "conditions" : "$conditions" } } } } ], "cursor": { "batchSize": 3 } }');

连接多个集合的数据

先创建一个 appointment 集合

select ud_fxdb_api.insert_one('ud_fxdb','appointment', '{"appointment_id": "A001", "patient_id": "P001", "doctor_name": "Dr. Milind", "appointment_date": "2023-01-20", "reason": "Routine checkup" }');
select ud_fxdb_api.insert_one('ud_fxdb','appointment', '{"appointment_id": "A002", "patient_id": "P001", "doctor_name": "Dr. Moore", "appointment_date": "2023-02-10", "reason": "Follow-up"}');
select ud_fxdb_api.insert_one('ud_fxdb','appointment', '{"appointment_id": "A004", "patient_id": "P003", "doctor_name": "Dr. Smith", "appointment_date": "2024-03-12", "reason": "Allergy consultation"}');
select ud_fxdb_api.insert_one('ud_fxdb','appointment', '{"appointment_id": "A005", "patient_id": "P004", "doctor_name": "Dr. Moore", "appointment_date": "2024-04-15", "reason": "Migraine treatment"}');
select ud_fxdb_api.insert_one('ud_fxdb','appointment', '{"appointment_id": "A007","patient_id": "P001", "doctor_name": "Dr. Milind", "appointment_date": "2024-06-05", "reason": "Blood test"}');
select ud_fxdb_api.insert_one('ud_fxdb','appointment', '{ "appointment_id": "A009", "patient_id": "P003", "doctor_name": "Dr. Smith","appointment_date": "2025-01-20", "reason": "Follow-up visit"}');

查询病人看过的医生和日期

SELECT cursorpage FROM ud_fxdb_api.aggregate_cursor_first_page('ud_fxdb', '{ "aggregate": "patient", "pipeline": [ { "$lookup": { "from": "appointment","localField": "patient_id", "foreignField": "patient_id", "as": "appointment" } },{"$unwind":"$appointment"},{"$project":{"_id":0,"name":1,"appointment.doctor_name":1,"appointment.appointment_date":1}} ], "cursor": { "batchSize": 3 } }');

Mongo协议操作数据库

在 Linux 上安装 MongoDB Shell

在大多数 Linux 发行版中,可以通过包管理器来安装 MongoDB Shell。

centos7

yum install -y mongodb-mongosh

安装完成后,MongoDB Shell 将可以在命令行中通过 mongosh 命令启动。 (默认会使用setup时的用户密码和数据库)

mongosh 'mongodb://<user>:<password>@<ip>:<port>/<database>'

默认用户和数据库

mongosh 'mongodb://unvdb:<password>@127.0.0.1:27017/unvdb'

创建新用户

认证功能暂未支持,roles选项需要置空

db.createUser({
  user: 'newuser',
  pwd: 'newpassword',
  roles: []
})
mongosh 'mongodb://newuser:newpassword@127.0.0.1:27017/<database>'

用法

显示数据库列表:

test> show dbs;
ud_fxdb  0 B

切换数据库

test> use ud_fxdb;
switched to db ud_fxdb
ud_fxdb>

显示集合

ud_fxdb> show collections;
patient

插入文档

ud_fxdb> db.patient.insertOne({ name: "Alice", age: 30 });
{
  acknowledged: true,
  insertedId: ObjectId('684a377d0c02e056b069e328')
}

查询文档

ud_fxdb> db.patient.find();
[
  { _id: ObjectId('684a377d0c02e056b069e328'), name: 'Alice', age: 30 }
]

更新文档

ud_fxdb> db.patient.updateOne({ name: "Alice" }, { $set: { age: 31 } });
{
  acknowledged: true,
  insertedId: null,
  matchedCount: Long('1'),
  modifiedCount: Long('1'),
  upsertedCount: 0
}

删除文档

ud_fxdb> db.patient.deleteOne({ name: "Alice" });
{ acknowledged: true, deletedCount: 1 }

UDB-FX-Proxy

java-MongoDB drivers 连接数据库

package org.example;
import com.mongodb.client.*;
import com.mongodb.client.MongoClients;
import org.bson.Document;


public class Main {
    public static void main(String[] args) {
        MongoClient client = MongoClients.create("mongodb://unvdb:unvdb@192.168.4.134:27017");
        MongoDatabase db = client.getDatabase("ud_fxdb");
        MongoCollection<Document> coll = db.getCollection("patient");
        // 3. 查询所有文档
        System.out.println("=== 查询所有文档 ===");
        FindIterable<Document> allDocs = coll.find();
        for (Document doc : allDocs) {
            System.out.println(doc.toJson());
        }
        client.close();
    }
}

输出

=== 查询所有文档 ===
{"_id": {"$oid": "6855325a6098b0eab869e328"}, "name": "Alice", "age": 30}

安装nginx 配置多个 UDB-FX-Proxy 实例

下载 nginx

wget https://nginx.org/download/nginx-1.28.0.tar.gz

安装 nginx

./configure
make && make install

配置nginx

worker_processes auto;
events { worker_connections 1024; }

stream {

    log_format stream '$remote_addr [$time_local] '
                       '$protocol $status $bytes_sent $bytes_received '
                       '$session_time';

    access_log /var/log/nginx/stream-access.log stream;

    # 上游服务器池
    upstream tcp_backend {
        # 轮询(默认)
        server 192.168.4.134:27017 weight=1 max_fails=3 fail_timeout=30s;
        server 192.168.4.134:27018 weight=1 max_fails=3 fail_timeout=30s;

        # 可选其它算法:
        # least_conn;
        # ip_hash;
        # hash $remote_addr consistent;
    }

    # 日志配置(可选)
    access_log /var/log/nginx/stream-access.log stream;
    error_log  /var/log/nginx/stream-error.log info;

    # 监听 TCP 端口
    server {
        listen       8081;               # 对外监听端口
        proxy_pass   tcp_backend;        # 转发到 upstream

        # 超时设置
        proxy_connect_timeout 1s;
        proxy_timeout         3s;

        # 缓冲区优化
        proxy_buffer_size     16k;

        # 可选:开启 proxy_protocol(如果前端使用了 proxy_protocol)
        # proxy_protocol on;
    }

}

启动nginx

./nginx -c /usr/local/nginx/conf/nginx.conf

配置nginx 地址

package org.example;

import com.mongodb.client.*;
import com.mongodb.client.MongoClients;
import org.bson.Document;


public class Main {
    public static void main(String[] args) {
        MongoClient client = MongoClients.create("mongodb://unvdb:unvdb@172.26.73.248:8081");
        MongoDatabase db = client.getDatabase("ud_fxdb");
        MongoCollection<Document> coll = db.getCollection("patient");
        // 3. 查询所有文档
        System.out.println("=== 查询所有文档 ===");
        FindIterable<Document> allDocs = coll.find();
        for (Document doc : allDocs) {
            System.out.println(doc.toJson());
        }
        client.close();
    }
}

输出

=== 查询所有文档 ===
{"_id": {"$oid": "6855325a6098b0eab869e328"}, "name": "Alice", "age": 30}

成功

MongoDB java drivers读写分离设置

UDB-FX-Proxy 不支持“自动主从发现” 没有 MongoDB replicaSet UDB-FX-Proxy 使用 UDB-TX 流复制但不实现 MongoDB 的 replica set 协议,所以驱动无法通过 isMaster 自动识别主与副本。它只能当作一组静态节点处理,无法像 MongoDB 那样自动选主 UDB-FX-Proxy 不识别 replicaSet 名称,建议不要加 replicaSet=。 启动多个 MongoClient 实例,每个连接对应一个 UDB-FX-Proxy 节点

%%{init: {"theme":"base","themeVariables":{"edgeLabelBackground":"#f9f9f9"}}}%%
graph LR
  subgraph 应用
    A[客户端]
  end

  subgraph "UDB‑FX‑Proxy"
    B["UDB‑FX-Proxy 只读"]
    C["UDB‑FX‑Proxy 主库"]
  end

  subgraph UDB-FX
    D["UDB‑FX 主数据库"]  
    E["UDB‑FX 副本"]
  end

  A -->|连接到| B
  A -->|连接到| C
  B -->|只读| E
  C -->|写入/读取| D
  D -->|复制 - WAL Streaming| E

  %% 节点分类样式
  classDef client fill:#a8dadc,stroke:#457b9d,color:#000,stroke-width:2px;
  classDef proxyRead fill:#ffeb99,stroke:#f4a261,color:#000,stroke-width:2px;
  classDef proxyWrite fill:#ffd6a5,stroke:#e76f51,color:#000,stroke-width:2px;
  classDef primary fill:#90be6d,stroke:#2a9d8f,color:#000,stroke-width:2px;
  classDef replica fill:#8ecae6,stroke:#219ebc,color:#000,stroke-width:2px;

  class A client;
  class B proxyRead;
  class C proxyWrite;
  class D primary;
  class E replica;

  %% 连线样式
  linkStyle 2 stroke:#219ebc,stroke-width:2px,stroke-dasharray:5 2;

java示例代码

package org.example;
import com.mongodb.client.*;
import com.mongodb.client.MongoClients;
import com.mongodb.client.result.InsertOneResult;
import org.bson.Document;

import java.time.LocalDateTime;


public class Main {
    public static void main(String[] args) {


        String writeConnectionString = "mongodb://unvdb:unvdb@192.168.4.134:27017/?directConnection=true";

        MongoClient writeClient = MongoClients.create(writeConnectionString);
        MongoDatabase db2 = writeClient.getDatabase("ud_fxdb");
        MongoCollection<Document> coll2 = db2.getCollection("patient");
        // 构建要插入的新文档
        Document newDoc = new Document()
                .append("name", "张三")
                .append("age", 30)
                .append("createdAt", LocalDateTime.now());
        // 插入操作
        InsertOneResult result = coll2.insertOne(newDoc);
        System.out.println("插入成功,_id = " + result.getInsertedId());
        writeClient.close();


        //读取client
        String readconnectionString = "mongodb://unvdb:unvdb@192.168.4.134:27019/?directConnection=true";
        MongoClient readClient = MongoClients.create(readconnectionString);

        MongoDatabase db = readClient.getDatabase("ud_fxdb");
        ;
        MongoCollection<Document> coll = db.getCollection("patient");
        // 3. 查询所有文档
        System.out.println("=== 查询所有文档 ===");
        FindIterable<Document> allDocs = coll.find();
        for (Document doc : allDocs) {
            System.out.println(doc.toJson());
        }
        readClient.close();
    }
}

国密认证

制作数字证书

在安装UDB-TX完成后,进入bin目录,执行命令

./gen.sh path

自动生成并安装证书, path 是数据库的数据路径。比如/home/soft/unvdb-data/ 执行完上述命令,服务器的证书文件将会自动拷贝到数据目录,客户端的证书文件 自动拷贝到~/.unvdb/目录下。

设置启用国密传输加密方式

a.在 unvdb-data 中修改 unvdbsvr.conf 文件。 修改结果如下:

# - SSL -

ssl = on
ssl_use_tlcp = on
ssl_ca_file = 'cacert.pem'
#ssl_crl_file = ''
#ssl_crl_dir = ''
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_enc_cert_file = 'server_enc.crt'
ssl_enc_key_file = 'server_enc.key'
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
ssl_ciphers = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-SM4-SM3:ECDHE-SM4-GCM-SM3:' # allowed SSL ciphers, support GMSSL
#ssl_prefer_server_ciphers = on
#ssl_ecdh_curve = 'prime256v1'
ssl_ecdh_curve = 'SM2'  #support GMSSL
#ssl_min_protocol_version = 'TLSv1.2'
#ssl_max_protocol_version = ''
#ssl_dh_params_file = ''
#ssl_passphrase_command = ''
#ssl_passphrase_command_supports_reload = off

修改方式:将上图中红框内的参数前的#删除,并将ssl和ssl_use_tlcp的值改为on 即可

在 unvdb-data中修改 ud_ident.conf 添加

# MAPNAME       SYSTEM-USERNAME         UD-USERNAME
certmap  client   unvdb

在 unvdb-data 中修改 ud_hba.conf 文件。 在参数中增加:

hostssl    all       all     0.0.0.0/0        cert map=certmap 

运行

ud_sql "host=127.0.0.1 port=5678 dbname=unvdb user=unvdb ssltlcp=1"

连接成功

[unvdb@x86 ~]$ ud_sql "host=192.168.4.134 port=5678 dbname=unvdb user=unvdb ssltlcp=1"
ud_sql (UDB-TX V2.4(build:24.2.13.1 sha1:35ab28ee))
SSL connection (protocol: GMTLSv1.1, cipher: ECDHE-SM4-SM3, compression: off)
Type "help" for help.

unvdb=#

设置 UDB-FX-Proxy TLCP连接和TLCP监听

bin/UDB-FX-Proxy --mode="normal" \
--listen-addr="0.0.0.0:27017" \
--listen-tlcp="0.0.0.0:27019" \
--listen-tlcp-cert-file="/home/unvdb/unvdb-data/server.crt" \
--listen-tlcp-key-file="/home/unvdb/unvdb-data/server.key" \
--listen-tlcp-ca-file="/home/unvdb/unvdb-data/cacert.pem" \
--listen-tlcp-enc-cert-file="/home/unvdb/unvdb-data/server_enc.crt" \
--listen-tlcp-enc-key-file="/home/unvdb/unvdb-data/server_enc.key" \
--postgresql-url='postgres://unvdb:unvdb@192.168.4.134:5678/?ssltlcp=1&sslrootcert=/home/unvdb/.unvdb/cacert.pem&sslcert=/home/unvdb/.unvdb/client.crt&sslkey=/home/unvdb/.unvdb/client.key&sslenccert=/home/unvdb/.unvdb/client_enc.crt&sslenckey=/home/unvdb/.unvdb/client_enc.key'
  • 国密TLCP暂时无法用mongosh工具进行连接,因为不支持TLCP

用 mongo-java-driver 进行TLCP连接

因为java使用p12格式的证书,所以要提前把客户端证书转成p12格式 kona-pkix-1.0.17.jar;kona-crypto-1.0.17.jar 文件在 https://repo.maven.apache.org/maven2/com/tencent/kona/ 下载

openssl pkcs8 -topk8 -outform PEM -in client.key -out client.key.pem -nocrypt

openssl pkcs8 -topk8 -outform PEM -in client_enc.key -out client_enc.key.pem -nocrypt

#正确的方案:使用 Kona 自带的 KeyTool 工具,直接生成国密证书和 keystore
java -cp "kona-pkix-1.0.17.jar;kona-crypto-1.0.17.jar" com.tencent.kona.pkix.tool.KeyStoreTool -type PKCS12 -store both_clients.p12 -storePasswd 123456 -alias client_sign -keyAlgo EC -key client.key.pem -keyPasswd 123456 -certs client.crt -ca cacert.pem

#正确的方案:使用 Kona 自带的 KeyTool 工具,直接生成国密证书和 keystore
java -cp "kona-pkix-1.0.17.jar;kona-crypto-1.0.17.jar" com.tencent.kona.pkix.tool.KeyStoreTool -type PKCS12 -store both_clients.p12 -storePasswd 123456 -alias client_enc -keyAlgo EC -key client_enc.key.pem -keyPasswd 123456 -certs client_enc.crt -ca cacert.pem

# 生成含根 CA 的 truststore
java -cp "kona-pkix-1.0.17.jar;kona-crypto-1.0.17.jar" com.tencent.kona.pkix.tool.KeyStoreTool -type PKCS12 -alias ca -keyAlgo EC  -certs cacert.pem -store truststore.p12 -storePasswd 123456
  • 添加java 依赖

<dependencies>
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-crypto</artifactId>
            <version>1.0.17</version>
        </dependency>
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-pkix</artifactId>
            <version>1.0.17</version>
        </dependency>
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-ssl</artifactId>
            <version>1.0.17</version>
        </dependency>
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-provider</artifactId>
            <version>1.0.17</version>
        </dependency>

        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver-sync</artifactId>
            <version>5.0.0</version>
        </dependency>

        <!-- SLF4J 日志接口 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.36</version>
        </dependency>

        <!-- 日志实现:simple 版本,控制台输出日志 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.36</version>
        </dependency>
    </dependencies>
  • java代码

package org.example;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.*;
import com.mongodb.client.MongoClients;
import com.tencent.kona.KonaProvider;
import org.bson.Document;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.Security;

public class Main {
    public static void main(String[] args) throws Exception {
        mongoTlCP();
    }

    public static SSLContext createTlcpContext() throws Exception {
        Security.addProvider(new KonaProvider());

        KeyStore keyStore = KeyStore.getInstance("PKCS12",  "Kona");
        try (FileInputStream kis = new FileInputStream("both_clients.p12")) {
            keyStore.load(kis, "123456".toCharArray());
        }
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
        kmf.init(keyStore, "123456".toCharArray());

        KeyStore trustStore = KeyStore.getInstance("PKCS12","Kona");
        try (FileInputStream tis = new FileInputStream("truststore.p12")) {
            trustStore.load(tis, "123456".toCharArray());
        }
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX", "Kona");
        tmf.init(trustStore);

        SSLContext ctx = SSLContext.getInstance("TLCPv1.1");
        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
        return ctx;
    }


    public static void mongoTlCP() throws Exception {
        SSLContext sslContext = createTlcpContext();

        MongoClientSettings settings = MongoClientSettings.builder()
                .applyToSslSettings(b -> b.enabled(true)
                        .context(sslContext)
                        .invalidHostNameAllowed(true))
                .applyConnectionString(new ConnectionString("mongodb://unvdb:unvdb@192.168.4.134:27019"))
                .build();

        try (MongoClient mongoClient = MongoClients.create(settings)) {
            MongoDatabase database = mongoClient.getDatabase("ud_fxdb");
            MongoCollection<Document> collection = database.getCollection("patient");

            FindIterable<Document> documents = collection.find();
            for (Document doc : documents) {
                System.out.println(doc.toJson());
            }
        }
    }
}

用 mongo-go-driver 进行TLCP连接

  • 请联系技术支持获取mongo-go-driver驱动

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"git.unvdb.com/UDB-TX_grp/UDB-TX_drv/udb-mongo-driver-go/v2/bson"
	"git.unvdb.com/UDB-TX_grp/UDB-TX_drv/udb-mongo-driver-go/v2/mongo"
	"git.unvdb.com/UDB-TX_grp/UDB-TX_drv/udb-mongo-driver-go/v2/mongo/options"
	"gitee.com/Trisia/gotlcp/tlcp"
	"github.com/emmansun/gmsm/smx509"
)

func main() {
	// 1. 加载服务端根 CA
	pool := smx509.NewCertPool()
	pemData, err := os.ReadFile("/root/unvdb_cert/cacert.pem")
	if err != nil {
		panic(err)
	}
	ca, err := smx509.ParseCertificatePEM(pemData)
	if err != nil {
		panic(err)
	}
	pool.AddCert(ca)
	// 2. 加载客户端签名证书(用于认证身份)
	authCert, err := tlcp.LoadX509KeyPair("/roo  t/unvdb_cert/client/client.crt", "/root/unvdb_cert/client/client.key")
	if err != nil {
		panic(err)
	}
	// 3. 加载客户端加密证书(用于 ECDHE 密钥交换)
	encCert, err := tlcp.LoadX509KeyPair("/root/unvdb_cert/client/client_enc.crt", "/root/unvdb_cert/client/client_enc.key")
	if err != nil {
		panic(err)
	}
	// 4. 构建 TLCP 配置,启用双向认证并指定密码套件顺序
	config := &tlcp.Config{
		RootCAs:      pool,
		Certificates: []tlcp.Certificate{authCert, encCert},
		CipherSuites: []uint16{
			tlcp.ECDHE_SM4_GCM_SM3,
			tlcp.ECDHE_SM4_CBC_SM3,
		},
	}
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	client, err := mongo.Connect(options.Client().ApplyURI("mongodb://unvdb:unvdb@192.168.4.134:27019").SetTLCPConfig(config)) //设置TLCP配置
	if err != nil {
		log.Fatal(err)
	}
	defer client.Disconnect(ctx)
	coll := client.Database("ud_fxdb").Collection("patient")
	cursor, err := coll.Find(ctx, bson.D{}) // 空过滤器抓取所有文档
	if err != nil {
		log.Fatal(err)
	}
	defer cursor.Close(ctx)
	var docs []bson.M
	if err := cursor.All(ctx, &docs); err != nil {
		log.Fatal(err)
	}
	for _, doc := range docs {
		fmt.Println(doc)
	}
}

MongoDB 功能支持列表

数据库命令

命令 支持情况
change streams
delete
eval 3.0以后已废弃
find
findAndModify
getLastError 是(4.2以后删除,使用WriteConcern 替代)
getMore
getPrevError
insert
parallelCollectionScan 否(4.2移除)
resetError
update

查询和写入命令:支持


事务命令

命令 支持情况
abortTransaction 否(协议层未实现session abortTransaction
commitTransaction 否(协议层未实现session commitTransaction)

管理命令

命令 支持情况
cloneCollectionAsCapped
collMod
connectionStatus
convertToCapped
copydb 废弃
create
createIndexes
currentOp
drop
dropDatabase
dropIndexes
filemd5
killCursors
killOp
listCollections
listDatabases 是(list_databases)
listIndexes 是(list_indexes_cursor_first_page)
reIndex 是)
renameCollection

诊断命令

命令 支持情况
buildInfo
collStats
connPoolStats
connectionStatus
dataSize
dbHash
dbStats
explain
features
hostInfo
listDatabases
listCommands
profiler
serverStatus
top
whatsmyuri

聚合命令

命令支持

命令 支持情况
aggregate
count
distinct
mapReduce

聚合阶段支持

阶段 支持情况
addFields
bucket
bucketAuto
changeStream
collStats
count
currentOp
facet
geoNear
graphLookup
group
indexStats
limit
listLocalSessions
listSessions
lookup 部分
match
merge
out
planCacheStats
project
redact
regexFind
regexFindAll
regexMatch
replaceRoot
replaceWith
sample
set
skip
sort
sortByCount
unset
unwind

表达式

布尔表达式

表达式 支持
and
not
or

转换表达式

表达式 支持
convert
toBool
toDate
toDecimal
toDouble
toInt
toLong
toObjectId
toString

集合表达式

表达式 支持
setEquals
setIntersection
setUnion
setDifference
setIsSubset
anyElementTrue
allElementsTrue

比较表达式

表达式 支持
cmp
eq
gt
gte
lt
lte
ne
in
nin

算术表达式

表达式 支持
abs
add
ceil
divide
exp
floor
ln
log
log10
mod
multiply
pow
round
sqrt
subtract
trunc

三角表达式

表达式 支持
acos, acosh
asin, asinh
atan, atan2, atanh
cos, cosh
degreesToRadians
radiansToDegrees
sin, sinh
tan, tanh

字符串表达式

表达式 支持
concat
indexOfBytes, indexOfCP
ltrim, rtrim, trim
split
strLenBytes, strLenCP
strcasecmp
substr, substrBytes, substrCP
toLower, toUpper

数组表达式

表达式 支持
arrayElemAt
arrayToObject
concatArrays
filter
indexOfArray
isArray
objectToArray
range
reverseArray
reduce
size
slice
zip
in

日期表达式

表达式 支持
dayOfYear
dayOfMonth
dayOfWeek
year, month, week
hour, minute, second, millisecond
dateToString
isoDayOfWeek, isoWeek, isoWeekYear
dateFromParts, dateToParts, dateFromString

条件表达式

表达式 支持
cond
ifNull
switch

累加器表达式

表达式 支持
sum, avg
first, last
max, min
push, addToSet
stdDevPop, stdDevSamp

系统变量

变量 支持
\$\$CLUSTERTIME
\$\$CURRENT
\$\$DESCEND
\$\$KEEP
\$\$PRUNE
\$\$REMOVE
\$\$ROOT

数据类型

类型 支持
Double, String
Object, Array
Binary Data, ObjectId
Boolean, Date, Null
32-bit Int, 64-bit Int
Timestamp, Decimal128
MinKey, MaxKey
Regular Expression
JavaScript(含 Scope)
Undefined

索引和属性

索引类型支持

索引类型 支持
Single Field
Compound
Multikey
Text
2dsphere
2d
Hashed

索引属性支持

属性 支持
TTL
Unique
Partial 仅用于唯一索引
Case Insensitive
Sparse
Background

查询运算符

变量运算符

运算符 支持
map
let

合并运算符

运算符 支持
mergeObjects

文本搜索运算符

运算符 支持
meta

数据类型运算符

运算符 支持
type

文本运算符

运算符 支持
literal

逻辑运算符

运算符 支持
or
and
not
nor

元素运算符

运算符 支持
exists
type

查询评估运算符

运算符 支持
expr
jsonSchema
mod
regex
text 否(使用 $regex 替代)
where

数组运算符

运算符 支持
all
elemMatch
size

注释运算符

运算符 支持
comment

投影运算符

运算符 支持
elemMatch
meta
slice

更新运算符

字段更新运算符

运算符 支持
inc, mul
rename
setOnInsert
set, unset
min, max
currentDate

数组更新运算符

运算符 支持
\$(位置运算符)
[]
addToSet, pop
pull, pullAll
push, pushAll 是(已废弃)

更新修饰符

修饰符 支持
each
slice
sort
position

位运算符

运算符 支持
bit
bitsAllSet
bitsAnySet
bitsAllClear
bitsAnyClear

地理空间运算符

运算符 支持
\$geoWithin
\$geoIntersects
\$near
\$nearSphere
\$geometry
\$minDistance
\$maxDistance
\$center
\$centerSphere
\$box
\$polygon

兼容性测试

UDB-FX-Proxy 命令和Mongodb5 兼容性

管理命令

命令 状态
cloneCollectionAsCapped ❌ 尚未实施
collMod ✅️ 支持
compact ✅️ 支持
convertToCapped ❌ 尚未实施
create ✅️ 支持
createIndexes ✅️ 支持
currentOp ✅️ 支持
drop ✅️ 支持
dropConnections ❌ 尚未实施
dropDatabase ✅️ 支持
dropIndexes ✅️ 支持
getParameter ✅️ 支持
killCursors ✅️ 支持
killOp ❌ 尚未实施
listCollections ✅️ 支持
listDatabases ✅️ 支持
listIndexes ✅️ 支持
logRotate ❌ 尚未实施
reIndex ✅️ 支持
renameCollection ✅️ 支持
setParameter ❌ 尚未实施
shutdown ❌ 尚未实施

聚合命令

命令 状态
aggregate ✅️ 支持
count ✅️ 支持
distinct ✅️ 支持

身份验证命令

命令 状态
authenticate ❌ 尚未实施
logout ✅️ 支持
saslContinue ✅️ 支持
saslStart ✅️ 支持

诊断命令

命令 状态
buildInfo ✅️ 支持
collStats ✅️ 支持
connectionStatus ✅️ 支持
connPoolStats ❌ 尚未实施
dataSize ✅️ 支持
dbStats ✅️ 支持
explain ✅️ 支持
ferretDebugError ✅️ 支持
getCmdLineOpts ✅️ 支持
getLog ✅️ 支持
hostInfo ✅️ 支持
listCommands ✅️ 支持
logApplicationMessage ❌ 尚未实施
ping ✅️ 支持
profile ❌ 尚未实施
serverStatus ✅️ 支持
validate ✅️ 支持
whatsmyuri ✅️ 支持

查询命令

命令 状态
bulkWrite ❌ 尚未实施
delete ✅️ 支持
find ✅️ 支持
findAndModify ✅️ 支持
getMore ✅️ 支持
insert ✅️ 支持
update ✅️ 支持

角色管理命令

命令 状态
createRole ❌ 尚未实施
dropAllRolesFromDatabase ❌ 尚未实施
dropRole ❌ 尚未实施
grantPrivilegesToRole ❌ 尚未实施
grantRolesToRole ❌ 尚未实施
revokePrivilegesFromRole ❌ 尚未实施
revokeRolesFromRole ❌ 尚未实施
rolesInfo ❌ 尚未实施
updateRole ❌ 尚未实施

会话命令

命令 状态
abortTransaction ❌ 尚未实施
commitTransaction ❌ 尚未实施
endSessions ✅️ 支持
killAllSessions ✅️ 支持
killAllSessionsByPattern ⚠️ 尚未完全实施
killSessions ✅️ 支持
refreshSessions ✅️ 支持
startSession ✅️ 支持

用户管理命令

命令 状态
createUser ✅️ 支持
dropAllUsersFromDatabase ✅️ 支持
dropUser ✅️ 支持
grantRolesToUser ❌ 尚未实施
revokeRolesFromUser ❌ 尚未实施
updateUser ✅️ 支持
usersInfo ✅️ 支持

数据 API

与 Atlas Data API 兼容的所有驱动程序和应用程序都应该与 FerretDB 兼容。

路径 状态
/action/aggregate ✅️ 支持
/action/deleteMany ✅️ 支持
/action/deleteOne ✅️ 支持
/action/find ✅️ 支持
/action/findOne ✅️ 支持
/action/insertMany ✅️ 支持
/action/insertOne ✅️ 支持
/action/updateMany ✅️ 支持
/action/updateOne ✅️ 支持

FAQs(常见问题)

Q1: select 出来显示的是BSON十六进制数据

在当前会话设置

SET ud_fxdb_core.bsonUseEJson=true;

永久设置,在unvdbsvr.conf中添加

ud_fxdb_core.bsonUseEJson = true;

使用bson_to_json_string函数

SELECT ud_fxdb_core.bson_to_json_string(your_bson_column)
FROM your_table;