首页 游戏 软件 资讯 排行榜 专题
首页
业界动态
Go 127 RowsColumnScanner 让数据库驱动自定义数据扫描方式

Go 127 RowsColumnScanner 让数据库驱动自定义数据扫描方式

热心网友
77
转载
2026-05-16

在Go的数据库生态里,database/sql 包无疑是最成功的抽象之一。它统一了接口,让开发者几乎不用关心底层驱动。但如果你深入用过像pgx这样功能丰富的驱动,可能会隐约感觉到一种“不对等”——尤其是在处理复杂数据类型时。这种别扭的感觉,根源在于一个存在了十几年的结构性问题:驱动对查询参数的写入有话语权,但对结果读取的控制力却近乎为零。

Go 1.27 引入的 driver.RowsColumnScanner 接口,正是为了解决这个问题。它不是一个你会天天手动调用的新API,但它正在悄然改变驱动与标准库之间的权力格局。

一个所有 Go 数据库开发者都遇到过的问题

先看一段最常见的Go数据库操作代码:

var name string
var createdAt time.Time
err := db.QueryRow("SELECT name, created_at FROM users WHERE id = $1", id).
    Scan(&name, &createdAt)

表面上看,代码干净利落。但如果你用的驱动支持更丰富的类型(比如PostgreSQL的UUID、数组),就会察觉到一种微妙的不公。

当你传入查询参数时,驱动可以通过实现 driver.NamedValueChecker 接口来检查、转换甚至拒绝参数,主动权在握。然而,当你调用 rows.Scan 读取结果时,流程就变了味:

  1. rows.Next() 让驱动把当前行的数据填充到一个 []driver.Value(也就是 []any)切片里。
  2. rows.Scan(dest...) 遍历这个切片,使用 database/sql 内置的 convertAssign 函数,将 any 转换成你提供的目标类型。

问题就出在第二步。驱动被完全架空了。无论它对当前数据库连接、字段的二进制格式有多了解,标准库都只会按照自己那套固定的规则进行类型转换。

在只有 stringint64time.Time 的简单世界里,这没问题。但现实中的数据库,类型系统要复杂得多。

数组、UUID、自定义类型——那些被逼着绕路走的数据

以PostgreSQL为例。假设有一张用户表,包含了UUID和标签数组:

CREATE TABLE users (
    id   UUID PRIMARY KEY,
    tags TEXT[]
);

如果使用pgx的原生接口,映射到Go类型非常自然:

var id uuid.UUID
var tags []string
err := conn.QueryRow(ctx, "SELECT id, tags FROM users WHERE id = $1", id).
    Scan(&id, &tags)

驱动知道UUID的二进制格式,也知道如何将 TEXT[] 的二进制表示还原成Go的字符串切片。一切都在底层高效完成。

但一旦换到 database/sql 的标准接口,这些便利就消失了。因为驱动返回的 []any 切片,到了标准库那里,它不认识 pgtype.UUID,也不认识 []string

于是,驱动只剩下两个都不怎么好的选择:

  1. 放弃二进制格式:把数据转成文本字符串塞进 []any,让标准库去解析。这等于把已经解析好的数据重新序列化,再让客户端解析一遍,纯属性能浪费。
  2. 让开发者自己处理:要求用户为每个复杂类型实现 sql.Scanner 接口。这带来了大量样板代码,而且这些 Scanner 实现是“盲”的,它们拿不到连接上下文、字段类型等关键信息,只能在黑暗中摸索转换。

这正是pgx作者jackc在提出相关提案时描述的核心困境:驱动拥有一套灵活强大的类型系统,但在读取结果这一侧,标准库完全不给它施展的机会。

RowsColumnScanner:驱动手握方向盘的那一刻

Go 1.27 在 database/sql/driver

type RowsColumnScanner interface {
    Rows
    ScanColumn(dest any, n int) error
}

当驱动实现了这个接口后,database/sql 在调用 rows.Scan 时,就不再走自己那套 convertAssign 逻辑了。它会将每一次扫描操作,直接委托给驱动的 ScanColumn(dest, index) 方法。

更妙的是,如果驱动对某一列不想处理(或者暂时没处理),它可以返回 driver.ErrSkip 错误。这时,标准库就会退回到原来的转换路径。这意味着驱动可以采取渐进策略,先接管自己有绝对把握的类型,其他的保持原样。

这看似只是增加了一个方法,实则翻转了权力关系。以前是标准库从驱动那里拿到一包“原材料”([]any)后自己加工;现在是每一步加工都询问驱动:“这个零件,你想怎么装?”

二进制格式:一个直接的性能收益

新接口最直接的性能提升,来自于驱动可以放心使用二进制格式传输数据。

以PostgreSQL的libpq协议为例,字段数据可以用文本格式传输,也可以用二进制格式传输。二进制格式的解析开销远低于文本格式——它省去了词法分析、转义处理和字符串转换等步骤。

但在过去,如果驱动知道数据最终要经过标准库的 convertAssign 处理,它往往只能选择文本格式。因为 convertAssign 只认识有限的几种基础类型(string, int64, []byte, time.Time 等)。一条用二进制格式传回的UUID列,标准库根本不知道如何将它转换成 string

pgx的基准测试印证了这一点。对于一个查询1000行 text[] 数据的场景,使用二进制格式相比文本格式,在内存分配次数上差了一个数量级。文本路径需要将数组序列化成 {a,b,c} 这样的字符串,客户端再重新解析;而二进制路径可以直接将数组元素按内存布局拷贝,没有中间字符串的转换损耗。

有了 RowsColumnScanner,驱动在 Next() 阶段就能预知自己将处理后续的扫描,从而可以全程采用高效的二进制格式。即使部分列因返回 ErrSkip 而回退到旧路径,主路径上的性能增益依然是实实在在的。

对 Go 工程实践的影响

这一变化,会在几个方面影响我们的日常开发:

第一,自定义类型映射变得更自然。 目前,很多项目会在 database/sql 之上再封装一层,用以处理自增ID、JSONB字段、枚举类型与Go常量之间的映射。这些封装层存在的一个重要原因,就是驱动在扫描端使不上劲。现在,驱动可以在扫描点直接完成这些映射,无需额外的封装层、ORM介入,也不必为每个类型编写独立的 Scanner 适配器。

第二,AI生成的数据库代码会更可靠。 AI助手在编写数据库操作代码时,一个常见难点是它不确定某个数据库字段应该映射到哪种具体的Go类型。created_attime.Time 还是 stringstatusint 还是自定义枚举?config 字段该用 json.RawMessage 还是某个具体的结构体?现在,驱动可以在扫描时直接给出答案,AI生成的代码不必在事后打上一堆类型断言或转换的补丁。

第三,性能敏感的服务需要重新评估扫描路径。 如果你的服务大量使用 database/sql 配合pgx这类高级驱动,升级到Go 1.27后,那些涉及UUID、数组、范围、复合类型等复杂字段的查询,其扫描路径的内存分配次数很可能会显著下降。虽然未必能达到“零分配”,但文本格式作为中间转换站的场景将大幅减少。

不要急着把所有驱动都切过来

在拥抱新特性的同时,也需要冷静看待潜在的兼容性风险。

database/sql 内置的 convertAssign 有一套非常精确的行为定义。例如,将一个 time.Time 值扫描到 *string 时,标准库会将其格式化为 time.RFC3339Nano 字符串。如果驱动的 ScanColumn 方法直接返回了驱动内部的文本表示,格式可能与此不同。

大多数情况下,这种差异并无大碍——只要输出的字符串能被通用的时间解析器识别,最终结果就是等价的。但如果你依赖了某种特定的字符串格式(例如直接进行字符串比较,或者将数据库值作为字符串对外暴露),那么在切换前后,务必进行一轮详细的对比测试。

稳妥的升级策略是:先在测试环境中启用驱动对扫描的接管,运行完整的集成测试,并进行新旧行为的结果diff检查,确认没有意外变化后,再部署到生产环境。

这件事为什么值得写一篇文章

归根结底,RowsColumnScanner 不是一个你会显式调用的日常API。它的深远意义在于,它解开了Go数据库生态中一个长达十几年的结构性症结——参数的写入和结果的读取,本就不应被区别对待。

在过去,这种“不对等”尚可忍受,因为业务类型相对简单。但随着PostgreSQL的数组、JSONB、范围、复合类型,以及SQLite的动态类型在业务中广泛应用,这个症结从“可以忽略”变成了“必须解决”的拦路虎。

Go 1.27 移开了这只拦路虎。对于像pgx、modernc.org/sqlite这样拥有强大原生类型系统的驱动而言,这意味着它们终于在 database/sql 的读写两端,都拿到了本该属于自己的、完整的话语权。这不仅是性能的提升,更是生态走向成熟与完善的关键一步。

来源:https://www.51cto.com/article/842424.html
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

Linux驱动开发中的内核延迟机制详解
业界动态
Linux驱动开发中的内核延迟机制详解

在Linux内核驱动开发领域,开发者们常常专注于硬件寄存器配置、中断处理等核心任务,却容易忽视一个基础但至关重要的能力——内核延时与定时机制。这绝非简单的“等待”功能,而是确保硬件时序精准、系统稳定运行、驱动高效响应的基石。无论是等待设备初始化完成、同步数据传输,还是在中断上下文中协调任务,都离不开

热心网友
05.15
刺客信条艾吉奥战斗技巧同步率反击机制详解
游戏资讯
刺客信条艾吉奥战斗技巧同步率反击机制详解

艾吉奥的战斗围绕同步率展开,通过技能与反击快速积累,蓄满后发动强力仪式攻击。二技能可嘲讽并快速积攒同步率,进入反击状态。队友受击时他会高频反击,获取同步率并享有高额伤害加成。嘲讽虽带来风险,但反击时自带减伤,被嘲讽目标攻击时减伤更高,生命危急时嘲讽解除,同时获得。

热心网友
05.15
AMD 26.5.2显卡驱动更新 支持最新游戏性能优化
科技数码
AMD 26.5.2显卡驱动更新 支持最新游戏性能优化

AMD发布AdrenalinEdition26 5 2显卡驱动,主要为《极限竞速:地平线6》和《007初露锋芒》提供官方支持。更新修复了RX9000系列显卡在运行《RoadCraft》和《Satisfactory》时的特定问题,并确认正在与开发商合作解决锐龙AI9HX370平台运行《战地风云6》的崩溃问题。

热心网友
05.15
AMD 26.5.2显卡驱动更新,优化最新游戏性能与兼容性
科技数码
AMD 26.5.2显卡驱动更新,优化最新游戏性能与兼容性

AMD发布26 5 2版显卡驱动,新增对《极限竞速:地平线6》和《007初露锋芒》的首日优化支持,以提升游戏性能。同时修复了RadeonRX9000系列在运行《RoadCraft》和《Satisfactory》时的特定问题。目前已知在特定平台上运行《战地风云6》可能出现崩溃,官方正在协同解决。

热心网友
05.15
AMD显卡驱动优化指南 有效减少游戏帧率波动提升流畅度
科技数码
AMD显卡驱动优化指南 有效减少游戏帧率波动提升流畅度

AMD为Linux内核提交新驱动补丁,前瞻支持ACPI6 7规范的CPPCHighestFreq寄存器。该寄存器使固件能直接报告CPU确切最高频率,取代原有估算机制,为任务调度提供精准依据。此举旨在解决异构架构下线性推算频率偏差大的问题,有望降低游戏帧率波动,提升系统响应与能效表现。

热心网友
05.14

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

iQOO 15T新机预约启动 延续Ultra系列旗舰设计风格
业界动态
iQOO 15T新机预约启动 延续Ultra系列旗舰设计风格

iQOO手机官方今日正式宣布,iQOO 15T已开启全渠道预约。随着预约启动,官方预热海报也首次揭示了新机的侧边轮廓设计。 关于这款新机的更多细节,此前已有数码博主提前剧透。据称,iQOO 15T将延续自家Ultra系列的设计语言,采用标志性的透明风格方形摄像头模组。更引人注目的是其屏幕配置——据爆

热心网友
05.16
美团外卖五折优惠直送寝室无需下楼
业界动态
美团外卖五折优惠直送寝室无需下楼

期末复习在图书馆熬到深夜,突然下起暴雨,裹紧羽绒服还得冒雨下楼拿外卖;军训结束累得只想瘫倒,宿管阿姨却把骑手拦在宿舍区外;想和室友凑单改善伙食,又被复杂的满减、助力规则搞得晕头转向……这大概是许多大学新生的共同经历,差点以为“冲刺取餐”成了宿舍生存的必备技能。其实,只要掌握正确方法,完全能省去这些奔

热心网友
05.16
三星家电退出中国市场 电视显示器等产品停售
业界动态
三星家电退出中国市场 电视显示器等产品停售

一则来自三星(中国)投资有限公司的业务调整通知,在今日引发了广泛关注。通知的核心内容相当明确:为应对急剧变化的市场环境,三星电子决定在中国大陆市场停止销售包括电视、显示器在内的所有家电产品。 这意味着,一个曾经在中国家电市场占据重要地位的品牌,其消费端的产品销售画上了句号。当然,市场更关心的是,存量

热心网友
05.16
一加16全能性能旗舰曝光 搭载骁龙8 Elite Gen6 Pro芯片
业界动态
一加16全能性能旗舰曝光 搭载骁龙8 Elite Gen6 Pro芯片

关于一加下一代旗舰手机一加 16 的最新爆料信息,近期引发了数码圈的广泛关注。知名数码博主 @数码闲聊站 最新透露了一款代号为 SM8975(即骁龙 8 Elite Gen6 Pro 平台)的子品牌新机细节,结合其暗示的表情符号,这款新机极有可能就是备受期待的一加 16。 根据最新的爆料信息,一加

热心网友
05.16
三星家电全面退出中国市场销售
业界动态
三星家电全面退出中国市场销售

三星电子的一则公告,在市场上激起了不小的波澜。根据其官方发布的消息,为应对当前急剧变化的市场环境,公司经过慎重评估,决定在中国大陆市场停止销售包括电视、显示器在内的所有家电产品。 图为三星电子发布的公告截图 这意味着,消费者未来将无法在官方渠道购买到三星品牌的电视、显示器等家用电器。不过,对于已经购

热心网友
05.16