sql server 连接必须用 sqlserver.open 且遵循其 url 格式,国产库需实现自定义 dialector;多库共存应使用 sync.map 管理连接池;跨库无分布式事务,须业务层补偿;各库时区、隔离级别、分页等行为差异需显式适配。

SQL Server 连接必须用 sqlserver.Open,不能套用 MySQL/PostgreSQL 的 DSN 格式
很多人直接把 MySQL 的 DSN 改个 host 和 port 就去连 SQL Server,结果报 invalid connection 或 driver: unknown driver sqlserver。根本原因是 SQL Server 驱动(gorm.io/driver/sqlserver)不识别 user:pass@tcp(...) 这类 MySQL 风格写法。
- 正确写法是使用 Windows 身份验证或 SQL 身份验证的 URL 格式:
sqlserver://sa:yourpassword@localhost:1433?database=master&encrypt=disable - 必须显式加
encrypt=disable(开发环境)或encrypt=true;trustServerCertificate=true(测试环境),否则默认启用加密校验会失败 - 如果用 Windows 认证,需改用
sqlserver://user@domain\instance?database=xxx&...&windows=true,且 Go 进程需运行在对应域账户下 - 注意:SQL Server 的
database参数是必填项,漏掉会连到master,但 GORM 迁移和操作可能因权限或上下文错乱而静默失败
国产数据库(达梦、人大金仓等)必须实现自定义 Dialector,不能只换驱动
官方 GORM 不支持达梦、人大金仓等国产库,光引入第三方驱动(如 github.com/xwb1989/sqlparser 或厂商提供的 dmgo)没用——GORM 无法自动识别其类型系统、分页语法、时间格式、事务行为。
- 核心动作是实现
gorm.Dialector接口,重点覆盖Initialize、Explain、BindVar和BuildStatement - 达梦 V8 需重写分页逻辑:
LIMIT ? OFFSET ?要转成SELECT * FROM (SELECT A.*, ROWNUM RN FROM (...) A) WHERE RN BETWEEN ? AND ? - 人大金仓(Kingbase)虽兼容 PostgreSQL,但默认
search_path是public,而实际 schema 常为kingbase或业务名,必须在Initialize中执行SET search_path TO xxx - 所有国产库都建议关闭
DisableForeignKeyConstraintWhenMigrating: true,否则外键约束在迁移时被忽略,上线后才发现数据一致性崩了
多数据库共存时,sync.Map 管理连接池比全局变量更安全
用一个 var DBs map[string]*gorm.DB 全局变量存多个数据库实例,看似简单,但在并发初始化或热重载场景下极易 panic —— Go map 非线程安全,且无法原子判断 key 是否已存在。
- 推荐用
sync.Map存储:var dbPool sync.Map,配合LoadOrStore做懒加载 - 每个数据库连接必须绑定独立的
*sql.DB连接池配置(SetMaxOpenConns、SetMaxIdleConns),不能复用同一套参数:SQL Server 默认连接超时短,达梦对长连接更敏感,混用会导致空闲连接被误杀 - 务必在
InitDB函数里调用db.Exec("SELECT 1")做连通性验证,否则首次查询才暴露连接失败,错误堆栈难以定位到初始化环节 - 不要在
gorm.Config里设NowFunc统一时区——SQL Server 默认用服务器时区,达梦默认用系统时区,人大金仓可配但常被忽略,统一设反而引发时间字段写入偏移
事务跨库操作不存在“分布式事务”,只能靠业务层补偿
有人试图用 db.Transaction 同时操作 MySQL 和达梦,结果发现事务只在单库生效,另一个库已提交无法回滚。GORM 的事务本质是 *sql.Tx,它只作用于单一 *sql.DB 实例。
- 跨库写入必须拆成独立事务,按“先主后辅”顺序执行,并记录操作日志用于失败后人工或定时任务补偿
- 若需强一致性,得引入外部协调器(如 Seata 的 AT 模式),但 Go 生态成熟度低,目前更现实的是用消息队列 + 本地事务表(比如在 MySQL 写一条待同步记录,由消费者推送到达梦)
- 特别注意:SQL Server 的
READ COMMITTED SNAPSHOT默认关闭,而达梦默认开启 MVCC,混合使用时读已提交语义不一致,容易出现“幻读”类问题,必须在业务逻辑中显式加锁或调整隔离级别
真正麻烦的不是连上数据库,而是让不同方言的 INSERT、UPDATE、SELECT COUNT(*) 在各库返回一致结果——这需要持续适配每种数据库的隐式类型转换规则和函数映射表,别指望一次封装就一劳永逸。











