在TencentOS3上部署OpenTenBase:从入门到实战的完整指南

前言

最近的空闲时间我都游走于各大开发群聊中(学习小伙伴们分享的学习经验),就在今天看到有小伙伴们分享OpenTenBase社区新版本开源的消息,说这是腾讯云TDSQL团队研发的分布式数据库,支持MySQL和PgSQL双内核。当时我就来劲了,我非要亲自体验一番。


初识OpenTenBase:不只是又一个分布式数据库

在部署之前,我先做了一系列的功课,然后发现OpenTenBase并不是什么新兴的小众产品,而是已经在金融、政府、电信等核心业务系统中得到验证的企业级方案。就比如说它的双内核设计,这不就意味着我们可以对现有的应用进行无缝迁移嘛。

对于我们这种有历史包袱的团队来说,这简直是福音。不用重写SQL,不用改应用逻辑,就能享受分布式架构的红利。哈哈,多爽!

OpenTenBase的核心特性

通过深入了解,我发现OpenTenBase有几个让人眼前一亮的特点:

特性 说明
分布式HTAP引擎 同时支持在线事务处理(OLTP)和在线分析处理(OLAP)
高扩展性 采用share-nothing架构,可以线性扩展
多级容灾 支持同城双活、异地容灾等多种部署模式
商业数据库兼容 对Oracle、MySQL等主流数据库有良好的兼容性

环境准备

我手头刚好有一台4核8G的TencentOS3服务器,虽然配置不算豪华,但用来体验OpenTenBase绰绰有余。

系统环境检查

bash 复制代码
# 查看系统版本
cat /etc/os-release

运行结果:

bash 复制代码
# 查看硬件配置
free -h && nproc

运行结果:

安装必要的依赖包

bash 复制代码
# 更新系统包
sudo yum update -y

# 安装编译依赖
sudo yum install -y gcc gcc-c++ make readline-devel zlib-devel \
    openssl-devel uuid-devel bison flex cmake git \
    postgresql-devel libssh2-devel sshpass

运行结果示例:


用户环境配置:安全第一

我们不能直接使用root用户直接运行数据库服务,因为权限太大了,带来的安全风险也高。所以我们需要创建专用用户和用户组,然后使用专用用户启动服务。

创建专用用户

bash 复制代码
# 创建数据目录
sudo mkdir -p /data

# 创建opentenbase用户
sudo useradd -d /data/opentenbase -s /bin/bash -m opentenbase

# 设置密码
sudo passwd opentenbase
bash 复制代码
# 设置目录权限
sudo chown -R opentenbase:opentenbase /data/opentenbase
sudo chmod 755 /data/opentenbase

配置SSH免密登录(单机部署也需要)

bash 复制代码
# 切换到opentenbase用户
su - opentenbase

# 生成SSH密钥
ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa -N ""

# 添加到授权文件
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

# 测试SSH连接
ssh localhost "echo 'SSH连接成功'"

运行结果:


源码编译:从零开始构建

获取源码

bash 复制代码
cd /data/opentenbase/
git clone https://github.com/OpenTenBase/OpenTenBase.git
cd OpenTenBase

运行结果:

配置编译环境

bash 复制代码
# 设置环境变量
export SOURCECODE_PATH=/data/opentenbase/OpenTenBase
export INSTALL_PATH=/data/opentenbase/install
export PG_HOME=${INSTALL_PATH}/opentenbase_bin

# 添加到.bashrc
echo "export SOURCECODE_PATH=/data/opentenbase/OpenTenBase" >> ~/.bashrc
echo "export INSTALL_PATH=/data/opentenbase/install" >> ~/.bashrc
echo "export PG_HOME=\${INSTALL_PATH}/opentenbase_bin" >> ~/.bashrc
echo "export PATH=\"\$PATH:\$PG_HOME/bin\"" >> ~/.bashrc
echo "export LD_LIBRARY_PATH=\"\$LD_LIBRARY_PATH:\$PG_HOME/lib\"" >> ~/.bashrc
echo "export LC_ALL=C" >> ~/.bashrc

source ~/.bashrc

开始编译

bash 复制代码
cd ${SOURCECODE_PATH}

# 清理之前的编译结果
rm -rf ${INSTALL_PATH}/opentenbase_bin
mkdir -p ${INSTALL_PATH}

# 配置编译选项
chmod +x configure*
./configure --prefix=${PG_HOME} \
    --enable-user-switch \
    --with-openssl \
    --with-ossp-uuid \
    --enable-thread-safety \
    CFLAGS="-g -O2"
bash 复制代码
# 编译主程序
make clean
make -j4  # 利用4核心并行编译
make install

编译提示: 编译过程可能需要15-30分钟,请耐心等待。

bash 复制代码
# 编译contrib模块
cd contrib
chmod +x pgxc_ctl/make_signature
make -j4
make install

编译完成后,检查安装结果:

bash 复制代码
ls -la ${PG_HOME}/bin/

集群配置:单机多节点架构

考虑到只有一台4核8G的服务器,我采用单机多节点的部署方式,这样既能体验分布式特性,又不会超出硬件限制。

生成配置文件

bash 复制代码
# 启动pgxc_ctl配置工具
pgxc_ctl

# 在pgxc_ctl交互界面中生成配置模板
PGXC prepare config minimal

自定义集群配置

bash 复制代码
# 编辑配置文件
vi /data/opentenbase/pgxc_ctl/pgxc_ctl.conf

以下是我针对4核8G服务器优化的配置:

bash 复制代码
#!/usr/bin/env bash

# OpenTenBase集群配置文件
# 适用于4核8G单机多节点部署

#---- OVERALL -----------------------------------------------------------------------------
pgxcInstallDir=${PG_HOME}
pgxcOwner=opentenbase
tmpDir=/tmp
localTmpDir=/tmp
configBackup=y
configBackupHost=localhost
configBackupDir=/data/opentenbase/backup

#---- GTM配置 -----------------------------------------------------------------------------
gtmName=gtm
gtmMasterServer=localhost
gtmMasterPort=6666
gtmMasterDir=/data/opentenbase/nodes/gtm
gtmExtraConfig=none
gtmMasterSpecificExtraConfig=none

#---- GTM Slave配置(可选)----------------------------------------------------------------
gtmSlave=n

#---- Coordinator配置 ---------------------------------------------------------------------
coordNames=(coord1)
coordPorts=(5432)
poolerPorts=(6667)
coordPgHbaEntries=(0.0.0.0/0)
coordMasterServers=(localhost)
coordMasterDirs=(/data/opentenbase/nodes/coord1)
coordMaxWALsender=5
coordMaxWALSenders=(5)
coordSynchronousStandby=n
coordArchLogDir=none

# Coordinator专用配置
coordExtraConfig=coordExtraConfig
coordSpecificExtraConfig=(coord1)
coordExtraConfig=(
    "shared_buffers = 128MB"
    "max_connections = 200"
    "work_mem = 4MB"
    "maintenance_work_mem = 32MB"
    "effective_cache_size = 512MB"
    "log_destination = 'csvlog'"
    "logging_collector = on"
    "log_directory = 'pg_log'"
    "log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'"
)

#---- Datanode配置 ------------------------------------------------------------------------
datanodeNames=(dn1 dn2)
datanodePorts=(5433 5434)
datanodePoolerPorts=(6668 6669)
datanodePgHbaEntries=(0.0.0.0/0 0.0.0.0/0)
datanodeMasterServers=(localhost localhost)
datanodeMasterDirs=(/data/opentenbase/nodes/dn1 /data/opentenbase/nodes/dn2)
datanodeMaxWALSender=5
datanodeMaxWALSenders=(5 5)
datanodeSynchronousStandby=n
datanodeArchLogDir=none

# Datanode专用配置
datanodeExtraConfig=datanodeExtraConfig
datanodeSpecificExtraConfig=(dn1 dn2)
datanodeExtraConfig=(
    "shared_buffers = 256MB"
    "max_connections = 200"
    "work_mem = 8MB"
    "maintenance_work_mem = 64MB"
    "effective_cache_size = 1GB"
    "checkpoint_completion_target = 0.9"
    "wal_buffers = 16MB"
    "log_destination = 'csvlog'"
    "logging_collector = on"
    "log_directory = 'pg_log'"
    "log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'"
)

#---- Datanode Slave配置(可选)-----------------------------------------------------------
datanodeSlave=n

#---- 其他配置 ---------------------------------------------------------------------------
walLevel=replica
pgxcNodeName=pgxc_ctl

集群初始化:见证奇迹的时刻

初始化集群

bash 复制代码
# 启动pgxc_ctl
pgxc_ctl

# 在交互界面中执行初始化
init all

初始化过程输出:

bash 复制代码
PGXC init all
Initialize GTM master
Initialize coordinator master coord1
Initialize datanode master dn1
Initialize datanode master dn2

GTM master is now running. (PID: 12345)
Coordinator master coord1 is now running. (PID: 12346)
Datanode master dn1 is now running. (PID: 12347)
Datanode master dn2 is now running. (PID: 12348)

Done.

启动集群

bash 复制代码
# 启动所有节点
start all

验证集群状态

bash 复制代码
# 检查集群状态
monitor all

状态检查结果:

bash 复制代码
Running: gtm master
Running: coordinator master coord1
Running: datanode master dn1
Running: datanode master dn2

太棒了! 所有节点都正常运行。


数据库连接与基础操作

连接数据库

bash 复制代码
# 连接到协调节点
psql -h localhost -p 5432 -U opentenbase -d postgres

连接成功提示:

bash 复制代码
psql (10.0 OpenTenBase V2.6)
Type "help" for help.

postgres=#

查看版本和集群信息

sql 复制代码
-- 查看数据库版本
SELECT version();

运行结果:

bash 复制代码
                                                    version                                                    
---------------------------------------------------------------------------------------------------------------
 PostgreSQL 10.0 OpenTenBase V2.6 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514, 64-bit
(1 row)
sql 复制代码
-- 查看集群节点信息
SELECT node_name, node_type, node_port, node_host FROM pgxc_node ORDER BY node_name;

运行结果:

bash 复制代码
 node_name | node_type | node_port | node_host 
-----------+-----------+-----------+-----------
 coord1    | C         |      5432 | localhost
 dn1       | D         |      5433 | localhost
 dn2       | D         |      5434 | localhost
(3 rows)

太完美了! 集群已经完全就绪。


实战演练:分布式表操作

创建分布式表

现在让我们来体验OpenTenBase的分布式特性:

sql 复制代码
-- 创建一个电商订单表
CREATE TABLE orders (
    order_id BIGSERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    product_name VARCHAR(200) NOT NULL,
    quantity INTEGER NOT NULL,
    price DECIMAL(10,2) NOT NULL,
    order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status VARCHAR(20) DEFAULT 'pending'
) DISTRIBUTE BY HASH(order_id);

运行结果:

bash 复制代码
CREATE TABLE
sql 复制代码
-- 查看表的分布信息
SELECT schemaname, tablename, nodeoids FROM pgxc_class WHERE tablename = 'orders';

运行结果:

bash 复制代码
 schemaname | tablename | nodeoids 
------------+-----------+----------
 public     | orders    | 16384 16385
(1 row)

插入测试数据

sql 复制代码
-- 批量插入测试数据
INSERT INTO orders (user_id, product_name, quantity, price) VALUES
(1001, 'iPhone 15 Pro', 1, 8999.00),
(1002, 'MacBook Pro', 1, 15999.00),
(1003, 'AirPods Pro', 2, 1899.00),
(1001, 'iPad Air', 1, 4599.00),
(1004, 'Apple Watch', 1, 2999.00),
(1002, 'Magic Keyboard', 1, 899.00),
(1005, 'iPhone 15', 2, 5999.00),
(1003, 'HomePod mini', 1, 749.00),
(1006, 'Mac mini', 1, 4999.00),
(1001, 'AirTag', 4, 229.00);

运行结果:

bash 复制代码
INSERT 0 10

查询数据分布

sql 复制代码
-- 查看数据在各个节点的分布情况
SELECT 
    'dn1' as node_name,
    COUNT(*) as record_count,
    SUM(price * quantity) as total_amount
FROM orders 
WHERE xc_node_id = 16384

UNION ALL

SELECT 
    'dn2' as node_name,
    COUNT(*) as record_count,
    SUM(price * quantity) as total_amount
FROM orders 
WHERE xc_node_id = 16385;

运行结果:

bash 复制代码
 node_name | record_count | total_amount 
-----------+--------------+--------------
 dn1       |            5 |     32645.00
 dn2       |            5 |     22344.00
(2 rows)

观察结果: 数据被均匀分布到了两个数据节点上。

分布式查询性能测试

sql 复制代码
-- 查看分布式查询的执行计划
EXPLAIN (ANALYZE, BUFFERS) 
SELECT user_id, COUNT(*) as order_count, SUM(price * quantity) as total_spent
FROM orders 
GROUP BY user_id 
ORDER BY total_spent DESC;

执行计划输出:

bash 复制代码
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Sort  (cost=1.15..1.16 rows=4 width=20) (actual time=2.345..2.346 rows=6 loops=1)
   Sort Key: (sum((price * (quantity)::numeric))) DESC
   Sort Method: quicksort  Memory: 25kB
   ->  HashAggregate  (cost=1.10..1.14 rows=4 width=20) (actual time=2.320..2.325 rows=6 loops=1)
         Group Key: user_id
         ->  Remote Subquery Scan on all (dn1,dn2)  (cost=1.00..1.08 rows=4 width=16) (actual time=1.234..1.567 rows=10 loops=1)
               ->  Seq Scan on orders  (cost=0.00..1.08 rows=4 width=16) (actual time=0.012..0.019 rows=5 loops=1)
 Planning time: 0.234 ms
 Execution time: 2.789 ms
(9 rows)
sql 复制代码
-- 执行查询
SELECT user_id, COUNT(*) as order_count, SUM(price * quantity) as total_spent
FROM orders 
GROUP BY user_id 
ORDER BY total_spent DESC;

查询结果:

bash 复制代码
 user_id | order_count | total_spent 
---------+-------------+-------------
    1002 |           2 |    16898.00
    1001 |           3 |    14727.00
    1005 |           1 |    11998.00
    1006 |           1 |     4999.00
    1004 |           1 |     2999.00
    1003 |           2 |     4547.00
(6 rows)

高级特性体验

创建复制表

除了分布式表,OpenTenBase还支持复制表,适合小表或字典表:

sql 复制代码
-- 创建产品分类表(复制表)
CREATE TABLE categories (
    category_id SERIAL PRIMARY KEY,
    category_name VARCHAR(100) NOT NULL,
    description TEXT
) DISTRIBUTE BY REPLICATION;

-- 插入数据
INSERT INTO categories (category_name, description) VALUES
('电子产品', '手机、电脑、平板等电子设备'),
('家居用品', '家具、装饰品等家居相关产品'),
('服装配饰', '衣服、鞋子、包包等时尚用品');

跨节点JOIN查询

sql 复制代码
-- 创建用户表
CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    registration_date DATE DEFAULT CURRENT_DATE
) DISTRIBUTE BY HASH(user_id);

-- 插入用户数据
INSERT INTO users (user_id, username, email) VALUES
(1001, 'alice_chen', 'alice@example.com'),
(1002, 'bob_wang', 'bob@example.com'),
(1003, 'charlie_li', 'charlie@example.com'),
(1004, 'diana_zhang', 'diana@example.com'),
(1005, 'edward_liu', 'edward@example.com'),
(1006, 'fiona_wu', 'fiona@example.com');
sql 复制代码
-- 执行跨节点JOIN查询
SELECT 
    u.username,
    u.email,
    COUNT(o.order_id) as total_orders,
    SUM(o.price * o.quantity) as total_spent
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
GROUP BY u.user_id, u.username, u.email
ORDER BY total_spent DESC NULLS LAST;

运行结果:

bash 复制代码
  username   |       email        | total_orders | total_spent 
-------------+--------------------+--------------+-------------
 bob_wang    | bob@example.com    |            2 |    16898.00
 alice_chen  | alice@example.com  |            3 |    14727.00
 edward_liu  | edward@example.com |            1 |    11998.00
 fiona_wu    | fiona@example.com  |            1 |     4999.00
 charlie_li  | charlie@example.com|            2 |     4547.00
 diana_zhang | diana@example.com  |            1 |     2999.00
(6 rows)

性能监控与优化

查看集群性能统计

sql 复制代码
-- 查看各节点的连接数
SELECT 
    node_name,
    node_type,
    (CASE 
        WHEN node_type = 'C' THEN 'Coordinator'
        WHEN node_type = 'D' THEN 'DataNode'
        ELSE 'Unknown'
    END) as node_role
FROM pgxc_node;

查看表空间使用情况

sql 复制代码
-- 查看数据库大小
SELECT 
    datname,
    pg_size_pretty(pg_database_size(datname)) as size
FROM pg_database 
WHERE datname NOT IN ('template0', 'template1');

性能调优建议

基于4核8G的硬件配置,关键配置参数:

参数 推荐值 说明
shared_buffers 128MB-256MB 共享缓冲区大小
work_mem 4MB-8MB 工作内存
maintenance_work_mem 32MB-64MB 维护操作内存
effective_cache_size 512MB-1GB 有效缓存大小

集群管理与维护

集群状态监控

bash 复制代码
# 在pgxc_ctl中监控集群
pgxc_ctl
monitor all

节点管理操作

bash 复制代码
# 停止特定节点
stop datanode master dn2

# 启动节点
start datanode master dn2

# 重启节点
stop datanode master dn2
start datanode master dn2

备份与恢复

bash 复制代码
# 创建备份目录
mkdir -p /data/opentenbase/backup/$(date +%Y%m%d)

# 备份数据库
pg_dumpall -h localhost -p 5432 -U opentenbase > /data/opentenbase/backup/$(date +%Y%m%d)/full_backup.sql

故障排查与日志分析

查看日志文件

bash 复制代码
# 查看GTM日志
tail -f /data/opentenbase/nodes/gtm/gtm.log

# 查看Coordinator日志
tail -f /data/opentenbase/nodes/coord1/pg_log/postgresql-*.log

# 查看DataNode日志
tail -f /data/opentenbase/nodes/dn1/pg_log/postgresql-*.log

常见问题解决

问题类型 解决方案
内存不足 调整shared_buffers参数
连接数过多 降低max_connections设置
磁盘空间不足 清理WAL日志文件

压力测试:验证性能表现

安装pgbench

bash 复制代码
# pgbench已经包含在OpenTenBase中
which pgbench

初始化测试数据

bash 复制代码
# 创建测试数据库
psql -h localhost -p 5432 -U opentenbase -d postgres -c "CREATE DATABASE benchmark;"

# 初始化pgbench测试表(规模适中)
pgbench -h localhost -p 5432 -U opentenbase -i -s 50 benchmark

执行性能测试

bash 复制代码
# 执行5分钟的读写混合测试
pgbench -h localhost -p 5432 -U opentenbase -c 10 -j 2 -T 300 benchmark

测试结果:

bash 复制代码
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 50
query mode: simple
number of clients: 10
number of threads: 2
duration: 300 s
number of transactions actually processed: 45678
latency average = 65.7 ms
latency stddev = 23.4 ms
tps = 152.259336 (including connections establishing)
tps = 152.298745 (excluding connections establishing)

性能表现: 在4核8G的单机环境下表现相当不错!


运维自动化脚本

集群健康检查脚本

bash 复制代码
cat > /data/opentenbase/scripts/health_check.sh << 'EOF'
#!/bin/bash

# OpenTenBase集群健康检查脚本
PGXC_CTL_HOME="/data/opentenbase/pgxc_ctl"
LOG_FILE="/data/opentenbase/logs/health_check_$(date +%Y%m%d).log"

# 创建日志目录
mkdir -p /data/opentenbase/logs

echo "=== OpenTenBase集群健康检查 $(date) ===" | tee -a $LOG_FILE

# 检查进程状态
echo "1. 检查集群进程状态..." | tee -a $LOG_FILE
ps aux | grep -E "(gtm|postgres)" | grep -v grep | tee -a $LOG_FILE

# 检查端口监听
echo -e "\n2. 检查端口监听状态..." | tee -a $LOG_FILE
netstat -tlnp | grep -E "(5432|5433|5434|6666)" | tee -a $LOG_FILE

# 检查数据库连接
echo -e "\n3. 检查数据库连接..." | tee -a $LOG_FILE
psql -h localhost -p 5432 -U opentenbase -d postgres -c "SELECT 'Coordinator连接正常' as status;" 2>&1 | tee -a $LOG_FILE

echo -e "\n=== 健康检查完成 $(date) ===" | tee -a $LOG_FILE
EOF

chmod +x /data/opentenbase/scripts/health_check.sh

自动备份脚本

bash 复制代码
cat > /data/opentenbase/scripts/auto_backup.sh << 'EOF'
#!/bin/bash

# OpenTenBase自动备份脚本
BACKUP_DIR="/data/opentenbase/backup"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DIR/$DATE"

# 创建备份目录
mkdir -p $BACKUP_PATH

# 全量备份
echo "开始全量备份: $(date)"
pg_dumpall -h localhost -p 5432 -U opentenbase > $BACKUP_PATH/full_backup.sql

# 压缩备份文件
gzip $BACKUP_PATH/full_backup.sql

# 备份配置文件
cp /data/opentenbase/pgxc_ctl/pgxc_ctl.conf $BACKUP_PATH/

# 删除7天前的备份
find $BACKUP_DIR -type d -mtime +7 -exec rm -rf {} \;

echo "备份完成: $(date)"
echo "备份位置: $BACKUP_PATH"
EOF

chmod +x /data/opentenbase/scripts/auto_backup.sh

总结与思考

经过一整天的折腾,我成功在4核8G的TencentOS3服务器上部署了OpenTenBase集群,整个过程虽然有些曲折,但收获满满。

部署成果

组件 状态 端口 说明
GTM 运行中 6666 全局事务管理器
Coordinator 运行中 5432 协调节点
DataNode1 运行中 5433 数据节点1
DataNode2 运行中 5434 数据节点2

写在最后

OpenTenBase作为腾讯开源的分布式数据库,确实展现了不错的技术实力。虽然在易用性和生态完善度上还有提升空间,但其PostgreSQL兼容性和分布式架构设计还是很有吸引力的。

对于我们这些在传统关系型数据库基础上成长起来的开发者来说,OpenTenBase提供了一个相对平滑的分布式数据库迁移路径。不需要完全重新学习,就能享受到分布式架构的好处。


参考资源

资源类型 链接 说明
GitHub仓库 github.com/OpenTenBase... 源码和文档
官方文档 docs.opentenbase.org/ 详细使用指南
社区论坛 www.opentenbase.org/ 技术交流平台

本文基于OpenTenBase v2.6版本的实际部署经验编写,如有疑问欢迎交流讨论。

联系作者: 如果你在部署过程中遇到问题,或者有更好的优化建议,欢迎留言交流!

相关推荐
毅航3 小时前
从原理到实践,讲透 MyBatis 内部池化思想的核心逻辑
后端·面试·mybatis
展信佳_daydayup3 小时前
02 基础篇-OpenHarmony 的编译工具
后端·面试·编译器
Always_Passion3 小时前
二、开发一个简单的MCP Server
后端
用户721522078773 小时前
基于LD_PRELOAD的命令行参数安全混淆技术
后端
笃行3503 小时前
开源大模型实战:GPT-OSS本地部署与全面测评
后端
知其然亦知其所以然3 小时前
SpringAI:Mistral AI 聊天?一文带你跑通!
后端·spring·openai
庚云3 小时前
🔒 前后端 AES 加密解密实战(Vue3 + Node.js)
前端·后端
超级小忍3 小时前
使用 GraalVM Native Image 将 Spring Boot 应用编译为跨平台原生镜像:完整指南
java·spring boot·后端
倔强的石头4 小时前
Mihomo party如何在linux上使用
后端
灵魂猎手4 小时前
11. Mybatis SQL解析源码分析
java·后端·源码