Skip to content
在线演示下载安装包

绿色安装包换成自己的数据库:MySQL、PostgreSQL 与国产库怎么配 ​

绿色安装包默认带一个 SQLite 库,文件就是 data/uvp.db。装完就能用,备份就是拷一个文件,单机小规模部署没有比这更省事的选择。

适用版本:换成 MySQL / PostgreSQL / SQL Server 的做法适用于 1.1.17 及以上;金仓、PolarDB、达梦这三种国产库是 1.1.18 起才支持的,1.1.17 及更早版本不认识这些类型,照第五节配会启动即退。另外,独立包里只带 SQLite 的建库脚本,外部库的全量基线 SQL 要另外取,见第三节。

但下面几条里中了一条,就该换成自己的数据库:

  • 平台要跑两个以上实例(SQLite 是单文件,多进程写会锁)
  • 单位有 DBA 和备份体系,数据必须落在受管的库里
  • 审计或信创要求数据库在国产化清单内
  • 库已经很大,单文件备份和恢复开始难做

换库的操作本身没什么门槛。麻烦在于改错之后提示很含糊:后端启动直接退出,日志里可能只有一行 third-party log message omitted(连原因都被吞了),也可能只有 phase=database 或 phase=migration。三种日志对应三种完全不同的错法,第六节有对照表。

下面按「先做后懂」的顺序写:想直接动手,从第三节开始,每一步都写了怎么算成功;想先弄明白为什么,就看第一节。

config.yml 修改示意图:箭头指向 gormv2 段里要改的四行,分别是 usedbtype 写 mysql、MySQL 开关改成 1、write 段换成自己的连接参数、SQLite 开关改成 0
图 1 箭头指到的就是全部要改的地方:① 选 MySQL ② 打开 MySQL 开关 ③ 填连接参数 ④ 关掉 SQLite 开关。少改一个,都可能启动即退。

一、先记住两句话 ​

gormv2 段里有两个名字很像、作用完全不同的东西:

  • isinitglobalgormmysql 是电源开关:启动时去不去连 MySQL;
  • usedbtype 是选择开关:业务读写走哪个库。

电源没开,选择开关拧到 MySQL 也没用——后端拿不到连接,直接退出。

配置在包根的 config/config.yml,gormv2 段下,绿色包默认长这样:

yaml
gormv2:
  usedbtype: "sqlite"        # 业务读写用哪个库
  mysql:
    isinitglobalgormmysql: 0 # 启动时是否去连 MySQL
  postgresql:
    isinitglobalgormpostgresql: 0
  sqlserver:
    isinitglobalgormsqlserver: 0
  sqlite:
    isinitglobalgormsqlite: 1

规则只有两条:

  1. 两个值必须指向同一个库。 三种组合,结果完全不同:
    • 开关开了、usedbtype 也指着它:正常启动。
    • 开关开了、但地址或账号错:启动退出,日志里有 phase=database。
    • usedbtype 指着 MySQL、开关却是 0:连接根本没建,取到空值就退出,日志只剩 third-party log message omitted。最常见,也最容易被误判成「MySQL 连不上」。
  2. 开关最好只开一个。 升级预检要求「有且仅有一个 1,且与 usedbtype 一致」;普通 start 不校验。忘关 SQLite 开关不会导致启动失败,但两个库都会被连接、被迁移,容易留下数据分叉。

出包脚本把这些开关写死了。deploy/standalone/make-config.py 要求「除目标库外全部置 0」,注释里把踩过的坑记了几处,其中就包括「开关没开时后端启动即退、日志里看不出原因」。

二、支持哪些库 ​

usedbtype说明全量基线脚本默认端口
sqlite绿色包默认,单文件baseline.sql(随包)—
mysql也认 mariadb / tidbuvp-gb28181.sql3306
postgresql也认 postgres / postgrepostgresql_converted.sql5432
kingbase人大金仓,协议兼容 PG复用 postgresql_converted.sql54321
polardb阿里 PolarDB MySQL 版复用 uvp-gb28181.sql3306
dm达梦 DM8,独立方言dameng_converted.sql5236
sqlserver能启动,但当前版本不自动做它的备份和在线升级sqlserver_converted.sql1433

表里有四处跟直觉不太一样:

  • 这张表按 1.1.18 写。1.1.17 只认 sqlite / mysql / postgresql / sqlserver 四个规范名:国产库三种,以及 mariadb / tidb / postgres / postgre / mssql 等别名都不认识,配上去会启动失败。用旧版本一律写规范名。
  • 金仓和 PolarDB 不需要单独的 SQL 脚本。它们的方言分别落到 PG 和 MySQL,复用同一套。真正独立的只有达梦。
  • PolarDB 要看版本。配 polardb 只适用 MySQL 版;PolarDB for PostgreSQL 得配 postgresql,配成 polardb 在连接阶段就会失败。
  • 脚本里带发布基线数据(用户、菜单、权限、字典等),一共 117 张表,但不含 CREATE DATABASE。库要你先建。

三、照着做:MySQL 换库 7 步 ​

以 MySQL 8.0、库名 uvp_gb28181 为例。每一步都写了怎么算成功,没看到成功标志就先别往下走。

换库主干五步流程图:建库、导入全量基线、改配置、停服重启、登录验证,右侧列出每一步做对之后应该看到的结果
图 2 主干就这 5 步,右边是每步的「怎么算过」;正文按操作顺序拆成 7 步,多了第 0 步准备和第 7 步回退。卡住了就带着日志去第六节对照。

⚠️ 全量基线脚本不在独立包里。 包内 resource/baseline/baseline.sql 是 SQLite 用的,不能拿它导 MySQL。外部库的基线要另外取与平台版本对应的那一份:仓库里是 server/resource/database/uvp-gb28181.sql,没有仓库就找交付方要对应版本的脚本,别从别的版本凑。PG / SQL Server / 达梦分别是 postgresql_converted.sql / sqlserver_converted.sql / dameng_converted.sql,同样要另取。

第 0 步:确认版本,拿到脚本 ​

两样东西先备齐,后面才不会白做:

  • 平台版本:看包目录名,例如 uvp-gb28181-linux-amd64-1.1.17。
  • 与这个版本配套的 uvp-gb28181.sql(117 张表 + 初始数据)。

✅ 算成功:版本号能从目录名读出来,手里的 SQL 也是同一版本。

第 1 步:建库 ​

bash
mysql -uroot -p -e "CREATE DATABASE uvp_gb28181 \
  DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"

✅ 算成功:执行 SHOW DATABASES;,列表里有 uvp_gb28181。

⚠️ 字符集必须是 utf8mb4。库建好之后再改字符集,旧数据要重建,代价很大。

第 2 步:导入全量基线 ​

bash
mysql -uroot -p --default-character-set=utf8mb4 uvp_gb28181 < uvp-gb28181.sql

✅ 算成功:下面这句输出 117。

bash
mysql -uroot -p -N -e "SELECT COUNT(*) FROM information_schema.tables \
  WHERE table_schema='uvp_gb28181';"

⚠️ 导入命令最后那个 uvp_gb28181 是库名,别写成 uvp_gb28181_test 之类的另一个库——数据导错库,平台连的那个库还是空的。

第 3 步:停服 ​

bash
./uvp-gb28181-ctl.sh stop

✅ 算成功:命令返回后,页面打不开,包内各组件端口都不再监听。

第 4 步:改 config.yml 这 4 行 ​

打开包根的 config/config.yml,找到 gormv2: 段,按图 1 改:

yaml
gormv2:
  usedbtype: "mysql"            # ① 业务用 MySQL
  mysql:
    isinitglobalgormmysql: 1    # ② 0 → 1,最容易漏
    slowthreshold: 30           # 裸数字,单位是秒;写成 "30s" 会溢出成负数
    loglevel: "warn"
    timezone: "Local"
    write:                      # ③ 换成你自己的库
      host: "192.168.10.220"
      database: "uvp_gb28181"
      port: 3306
      user: "uvp"
      pass: "改成一个真实口令"
      charset: "utf8mb4"
      setmaxidleconns: 10
      setmaxopenconns: 128
      setconnmaxlifetime: 60
      setconnmaxidletime: 30
    isopenreaddb: 0             # 保持 0;开了才会去读 read 段
  postgresql:
    isinitglobalgormpostgresql: 0
  sqlserver:
    isinitglobalgormsqlserver: 0
  sqlite:
    isinitglobalgormsqlite: 0   # ④ 1 → 0,保证只有一个开关是 1

✅ 算成功:执行下面这句,只输出一行(就是 isinitglobalgormmysql: 1)。

bash
grep -nE 'isinitglobalgorm[a-z]+: *1' config/config.yml

⚠️ 缩进别动。usedbtype 和各个方言段都缩在 gormv2: 下面,顶格就跑到段外了,后端读不到。

第 5 步:启动 ​

bash
./uvp-gb28181-ctl.sh start

✅ 算成功:ZLM、nginx、Redis、后端依次启动成功,后端开始监听端口。

⚠️ 启动时还会打印一行「数据库已就绪(.../data/uvp.db),跳过初始化」——那说的是本机 SQLite 文件,跟 MySQL 没关系,别当成 MySQL 也初始化好了。

如果启动时报端口被占用(尤其是「本机一条出站连接正把它当源端口用」):这不是配置错,是默认端口段落在 Linux 的临时端口范围里,处理办法见第六节「启动报端口被占用」。要换端口就改 config/config.env 最上面那段 # [uvp-ports]:

bash
./uvp-gb28181-ctl.sh stop
sed -i 's/^UVP_NGINX_HTTP_PORT=51000$/UVP_NGINX_HTTP_PORT=24500/' config/config.env
./uvp-gb28181-ctl.sh start

⚠️ 版本差异(很重要):1.1.19 起,全新包解压后、第一次启动前改端口就能生效。1.1.18 及更早的包有个洞——全新包没有端口基线文件,改了会被静默忽略、回退默认段(实测改成 53000 段,启动时预检报的仍是 51008,一句提示都没有)。老版本要么先 start 一次再改,要么在 config.env 里同时写一行 UVP_USER_SET_<键名>=1。

第 6 步:登录验证 ​

打开页面登录,看设备、通道、用户列表是否都在。

✅ 算成功:能登录;随便新建一条数据,回 MySQL 里能查到。

⚠️ 新库用的是基线自带的管理员账号,原来 SQLite 里改过的密码不会跟过来。登不上就按部署手册重置管理员密码,别怀疑库没切成功。

⚠️ 首次登录会被要求改初始密码(1.1.18 起)。没改之前,除了「个人信息」以外的接口全部返回「请先修改初始密码」——照着提示改一次就行,别误判成换库把接口弄坏了。用命令行验证时尤其容易撞上:

bash
# 没改密前,这个列表接口会报「请先修改初始密码」,不是 500 也不是连不上库
curl -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:51000/api/users/list?pageNum=1&pageSize=5"

第 7 步:出问题怎么退 ​

停服,把 usedbtype 改回 sqlite、isinitglobalgormsqlite 改回 1、isinitglobalgormmysql 改回 0,再启动。详见第九节。


现场可以打印这份清单逐项勾:

  • □ 版本和脚本对得上
  • □ 库建好,字符集是 utf8mb4
  • □ 表数是 117
  • □ 只有 1 个开关是 1,且与 usedbtype 一致
  • □ 启动日志里没有 startup.failed
  • □ 能登录,设备 / 通道 / 用户都在
  • □ 新建一条数据,新库里能查到

四、PostgreSQL ​

bash
# 1) 建库(PG 的 database 层级相当于 MySQL 的 schema + database)
createdb -h 127.0.0.1 -U postgres uvp_gb28181

# 2) 导入
psql -h 127.0.0.1 -U postgres -d uvp_gb28181 -f postgresql_converted.sql

配置片段(同样是选库 + 开开关 + 填连接参数,三处一起改):

yaml
gormv2:
  usedbtype: "postgresql"                 # ① 选 PG
  postgresql:
    isinitglobalgormpostgresql: 1         # ② 打开 PG 开关
    slowthreshold: 30
    loglevel: "warn"
    timezone: "Asia/Shanghai"
    write:
      host: "127.0.0.1"
      database: "uvp_gb28181"
      port: 5432
      user: "uvp"
      pass: "改成一个真实口令"
      setmaxidleconns: 10
      setmaxopenconns: 128
      setconnmaxlifetime: 60
      setconnmaxidletime: 30

PG 上没有 MySQL 的 charset 键,别硬加。服务端编码在建库的时候就定了。

五、国产化三兄弟 ​

⚠️ 本节三种库 1.1.18 起才支持。1.1.17 及更早版本不认识 kingbase / polardb / dm,照下面配会启动即退,先升级平台。

金仓 KingbaseES ​

协议兼容 PostgreSQL,驱动和迁移脚本都能复用。跟 PG 不一样的就三处:端口默认 54321,管理账号默认 system,导入用 ksql(用法跟 psql 一样)。

bash
ksql -h 127.0.0.1 -p 54321 -U system -d uvp_gb28181 -f postgresql_converted.sql
yaml
gormv2:
  usedbtype: "kingbase"                    # ① 选金仓
  kingbase:
    isinitglobalgormkingbase: 1            # ② 打开金仓开关
    timezone: "Asia/Shanghai"
    write:
      host: "127.0.0.1"
      database: "uvp_gb28181"
      port: 54321
      user: "system"
      pass: "改成一个真实口令"

阿里 PolarDB(MySQL 版) ​

配置名是 polardb,脚本用 MySQL 那份,端口 3306。库类型判断落在 MySQL 方言上,所以 MySQL 那套注意事项原样适用。

yaml
gormv2:
  usedbtype: "polardb"                     # ① 选 PolarDB
  polardb:
    isinitglobalgormpolardb: 1             # ② 打开 PolarDB 开关
    write:
      host: "你的 PolarDB 连接地址"
      database: "uvp_gb28181"
      port: 3306
      user: "uvp"
      pass: "改成一个真实口令"
      charset: "utf8mb4"

PolarDB for PostgreSQL 不适用这段,按上一节配 postgresql。

达梦 DM8 ​

达梦是唯一要单独一套脚本的,坑也最多。有几个参数建实例的时候就得定死,事后改代价很大:

bash
# ⛔ CASE_SENSITIVE=N 是硬要求,不是建议
# ⛔ 两个口令都必须给,只给 SYSDBA 会 fail to init db
dminit PATH=/opt/dm/data DB_NAME=UVP PAGE_SIZE=16 CASE_SENSITIVE=N \
       SYSDBA_PWD='改成口令' SYSAUDITOR_PWD='改成口令'

为什么必须 CASE_SENSITIVE=N:平台里还残留少量不带引号的裸 SQL。达梦默认大小写敏感,GORM 发出去的小写带引号标识符,和裸 SQL 折叠成的大写标识符,会指向两个不同的对象,结果是建表成功、查不到表。设成 N 之后全部归一到大小写不敏感,两边才对得上。

LENGTH_IN_CHAR 不是 dminit 的参数,传了会报 Invalid parameter,事后改 dm.ini 又会让实例起不来。基线生成器把字符串列长度统一乘了 3,GB18030 和 UTF-8 两种汉字编码都够用,现场保持默认就行。

导入用 disql 的 start,或者用 DM 管理工具执行脚本:

text
disql SYSDBA/'口令'@127.0.0.1:5236
SQL> start /opt/uvp/dameng_converted.sql

配置里 schema 必填:

yaml
gormv2:
  usedbtype: "dm"                          # ① 选达梦
  dm:
    isinitglobalgormdm: 1                  # ② 打开达梦开关
    slowthreshold: 30
    loglevel: "warn"
    schema: "SYSDBA"          # 达梦按模式隔离表,不写就落到用户默认模式
    write:
      host: "127.0.0.1"
      port: 5236
      user: "SYSDBA"
      pass: "改成一个真实口令"
      schema: "SYSDBA"

达梦是「用户名即模式名」。建库的模式和登录用户对不上,症状同样是建表成功但查不到表。留空时代码会退回用 user 当 schema,别依赖这个兜底。

另一个跟数据库本身无关的现象:达梦驱动会把批量插入改写成逐行 MERGE,跨机房高延迟链路上单条语句能到 200 ms 量级。我们在一台上实测平均 273 ms,一次 OpenAPI 目录发布跑了 2 分 12 秒。别当故障查,链路延迟叠加驱动的改写方式就是这个数;批量操作按这个量级估时。

六、误判怎么排除 ​

先看日志里出现的是哪一种,这三种别混:

日志里看到的真实原因怎么确认
只有 third-party log message omitted(text_omitted=true),没有任何 phaseusedbtype 指向的库没开 isinitglobalgorm*,连接压根没创建,取到空值就退出了;「数据库连接失败」这句原文被日志桥接吞掉数一遍 isinitglobalgorm* 里有几个 1,把目标库那个置 1、其它置 0
phase=database(event=startup.failed)开关开了,但连不上:地址 / 端口 / 账号 / 口令错,或库不存在拿 write 段同一套参数,用 mysql / psql 手工连一次
phase=migration,文案含「检测到空迁移版本表且缺少 XXX 表」库连上了,但全量基线没导、或导进了另一个库 / 另一个 schemaSELECT COUNT(*) 数表(应为 117);确认连的是哪个 host、哪个 database / schema
达梦报「表已存在但查不到」schema 没写,或实例建的时候 CASE_SENSITIVE=YSELECT owner, table_name 看表落在哪个模式
连接串看起来对,却一直认证失败用的是 read 段参数(读写分离没开时这一段完全不生效),或连到了同名的另一个实例确认 isopenreaddb 为 0,只留 write 段;用同一个账号手工连一次
页面能开,登录报 no such table这是 SQLite 场景:data/uvp.db 是空文件或上次建库中断。外部库空库走不到这一步,上面第三行就退出了停服后删掉 data/uvp.db 再 start,让脚本重建

表里「用的是 read 段参数」那一行值得多说一句。./uvp-gb28181-ctl.sh start 每次都会把 config.env 里的几个端口同步进 config.yml,但只改 HTTP、Redis、ZLM 与 media.hookport 这 5 个键,数据库的端口不在同步范围内。所以数据库地址和端口可以放心写在 config.yml,重启不会被改回去。

还有一种:启动报端口被占用(跟数据库无关) ​

尤其是那行写着「本机一条出站连接正把它当源端口用」——停不掉、重试也没用,很容易被当成「数据库连不上导致起不来」。

原因是端口段:Linux 版默认用 51000-51064,这整段落在 Linux 的临时端口范围(默认 32768-60999)里。内核会把这段端口临时分给出站连接做源端口——后端连 Redis、连 MySQL、DNS 查询都算。被占的那一刻 ss -lnt 里看不到监听,但 ZLM / nginx 就是绑不上:偶发、重试无效、过一会儿自己又好。

实测过一台:53000 段上一条「本地 53004 → :6379」的出站连接把 ZLM 的 HTTP 端口顶掉,连试两次都失败。

两种处置,细节在包的 PORTS.md:

bash
# ① 不动端口,把它们从临时池里预留出来(推荐,重启不丢,对所有现场都适用)
sudo sysctl -w net.ipv4.ip_local_reserved_ports="51000-51064"
# 持久化:追加,别覆盖已有的保留段
echo 'net.ipv4.ip_local_reserved_ports = 51000-51064' \
  | sudo tee -a /etc/sysctl.d/99-uvp-ports.conf
bash
# ② 整段换到 32768 以下,比如 24000 段
./uvp-gb28181-ctl.sh stop
vi config/config.env      # 把 51000–51064 整体换成 24000–24064(16 个键都要改)
./uvp-gb28181-ctl.sh start

② 手工改 16 行太容易漏,建议直接照 config.env 顶部那段注释逐行改:主端口是 51000–51014 这些单值,UVP_ZLM_RTP_RANGE 是 51014-51063 这种区间(两处段号都要换)。改完 start,预检会打印新范围,确认是 24000-24064 就对了。

⚠️ 版本差异:1.1.19 起,start 的端口预检会自己把这类占用报出来,并打印本机临时端口范围和处置命令。1.1.18 及更早只查「有没有人监听」,所以会出现「预检通过、ZLM 却绑失败」,当时只能靠 ss -tan | grep <端口> 自己找。

⚠️ 为什么不把默认段直接改掉:改默认值等于所有存量现场升级后端口全变(访问地址、防火墙规则、设备侧媒体配置都要重配),比偶发绑失败破坏大得多。所以默认段保持不变,用上面两种办法处理;新部署可以直接选 32768 以下的段,省掉这一步。

七、换成外部库之后,升级和备份要注意什么 ​

绿色包的在线升级会对外置数据库做备份,但不会把口令写进注册文件。升级前必须在 config/config.env 或 systemd 的 EnvironmentFile 里给出口令:

ini
UVP_UPGRADE_DB_PASSWORD=改成数据库备份账号的口令

也可以只给变量名,由环境注入:

ini
UVP_UPGRADE_DB_PASSWORD_ENV=MY_UVP_DB_PASSWORD

口令缺失时,升级会在停服之前就阻断。宁可升不了,也别停完服才发现备份做不了。外置库备份默认上限 8 GiB,库更大时显式提高:

ini
UVP_UPGRADE_EXTERNAL_BACKUP_MAX_BYTES=21474836480

SQLite 不需要这两个变量。

八、已有的数据怎么办 ​

平台没有内置「库类型互转」的工具。改个配置不会把数据带过去,两条路:

推荐:按新部署处理。 先配好外部库、导基线、启动,然后重新录入设备、通道、用户。设备侧只要把 SIP 参数重新指向平台,工作量取决于设备数量,好处是不留数据一致性的尾巴。

确实要搬已有数据,就用第三方数据库工具(DBeaver 的数据传输、Navicat 的数据同步之类)按表搬。搬之前先做三件事:

  1. 停平台。运行中搬必然丢数据,写入一直在发生。
  2. 按父表先、子表后的顺序搬,或者搬完统一处理外键。设备表、通道表、用户表之间都有引用。
  3. 自增主键、时间列、布尔列这三种类型最容易被工具改语义,搬完必须对账。

对账不用 117 张表全对,四个数对得上就基本能说明问题:

sql
SELECT COUNT(*) FROM sys_users;          -- 用户数
SELECT COUNT(*) FROM gb_device;          -- 设备数
SELECT COUNT(*) FROM gb_channel;         -- 通道数
SELECT COUNT(*) FROM sys_casbin_rule;    -- 权限规则数

四个数都和原库一致,再抽查两台设备点播,才算搬完。

九、回退 ​

data/uvp.db 只要没删,回退就是改三行、重启一次:

  1. usedbtype 改回 "sqlite"
  2. isinitglobalgormsqlite 改回 1
  3. isinitglobalgormmysql 改回 0

切换期间产生的新数据在外部库里,不会自动回来,回退意味着丢掉这段时间的变更,动手前想清楚。

还有个容易误会的地方:切到外部库以后,ctl.sh start 仍然会在本机建/检查 data/uvp.db,并打印「数据库已就绪,跳过初始化」。那是脚本的固定动作,只看本机这个 SQLite 文件,完全不检查外部库有没有表,也不影响业务用的外部库。别把这行当成「MySQL 也初始化好了」。判断切没切成功,看登录页和 logs/app/ 里的数据库连接日志。


数据库换完,接下来常问的是缓存。绿色包自带的 Redis 也能换成自己的,步骤比换库少,但有个「改了不生效」的坑,见《换成自己的 Redis》。