
简介数据库访问组件是桌面与业务系统开发中连接关系型数据库的关键桥梁尤其在Delphi生态中原生高效的数据链路往往决定项目长期稳定性。MyDAC作为一套直接与MySQL协议交互的组件库通过TMyConnection、TMyQuery等控件实现了握手认证、参数化查询与事务控制。解析v5.10源码核心价值在于理解连接字符串解析、认证插件匹配、客户端库libmysql.dll动态加载等底层机制避免因版本或位数不匹配导致的闪退与Lost connection问题。在实际工程中合理设置字符集、开启协议日志、管理连接池能显著提升老项目维护效率。本文围绕源文件阅读、最小化连接代码、常见坑位梳理帮助开发者快速将MyDAC应用至Delphi与MySQL的集成场景。1. 从绕过官方驱动说起MyDAC 到底是什么为什么要读 v5.10 源文件做 Delphi 连 MySQL 的人迟早会撞上一堵墙官方驱动版本跟数据库版本经常对不上换了 MySQL 小版本连接组件就翻车DBExpress 的 MySQL 驱动遇到新协议直接黑匣子用 ODBC 桥接又慢又难调。MyDACMySQL Data Access Components就是为这个场景存在的一套原生组件它绕过中间层直接用客户端协议跟 MySQL 通信把 TMyConnection、TMyQuery、TMyScript 这些控件拖进 IDE 就能干活。读 v5.10 源文件这件事跟用编译好的组件包完全不同你能看到连接握手、命令编码、结果集解析的具体实现遇到问题能自己打断点查原因而不是对着黑匣子猜。适合谁手上有 Delphi 7 到 XE 时代老项目、需要长期维护 MySQL 连接的开发者以及想把数据访问层彻底搞懂、不想被驱动绑架的人。2. 组件骨架TMyConnection、TMyQuery、TMyScript 的事务边界与连接模型2.1 连接链路与协议握手MyDAC 的整套设计都围绕 TMyConnection 展开。这个组件管理的不只是 socket 连接还包含了握手阶段的状态机、字符集协商、压缩协议开关、SSL 选项这些底层细节。v5.10 的源码里连接建立过程大致是解析 Server 属性里的主机名和端口建立 socket发送握手包读取服务器返回的协议版本与能力标志位再根据 Options 里的配置决定是否启用压缩、是否使用 SSL最后做认证。这套流程跟 mysql 命令行客户端的差别在于MyDAC 在每一步之间留了很多回调点所以你能在 BeforeConnect、AfterConnect 事件里做自定义处理也可以在源码层直接修改握手行为。常见做法是直接在 TMyConnection 的配置面板里填 Server、Port、Username、Database但读源码你会发现连接串解析逻辑远比面板上的字段丰富。Options 里有几个关键开关直接决定握手能否成功Compress 控制压缩协议Protocol 决定走 TCP 还是管道AuthPlugin 决定认证插件。v5.10 的年代 MySQL 5.0/5.1 还是 mysql_old_password 和 mysql_native_password 混用所以 AuthPlugin 如果不匹配握手就会在认证阶段被服务器拒绝报错往往又含糊只给一个 Access denied。读源码的好处就是你能在 DoAuth 那个方法上打断点直接看客户端把什么哈希算法发给了服务器。// 最小连接配置Delphi 代码 MyConnection1.Server : 192.168.1.10; MyConnection1.Port : 3306; MyConnection1.Username : app_user; MyConnection1.Password : secret; MyConnection1.Options.Compress : False; MyConnection1.Options.Protocol : TCP/IP; MyConnection1.Options.AuthPlugin : mysql_native_password; MyConnection1.Open;这段代码里的 AuthPlugin 是 v5.10 里最容易被忽略的选项。如果你连接的 MySQL 是 5.5 以上并启用了新的认证插件而 MyDAC 这边还按旧协议握手服务器会直接断开连接错误信息只看得到 Lost connection。我一般会先把 Protocol 固定为 TCP/IP确认连接正常后再研究其他传输方式因为管道和共享内存在不同 Windows 版本上的行为差异很大容易跟权限问题混在一起。参数方面Server 支持主机名或 IP也可以用冒号带端口比如 db01:3307但显式写 Port 属性更稳妥。Options.Compress 如果打开传输层会做压缩对文本类 SQL 效果好但会额外消耗 CPU低带宽环境可以开局域网内建议关掉不然压出来的延迟反而更高。Charset 参数在 v5.10 里默认是空但实际连接建立后必须设置否则中文乱码问题会从握手一路蔓延到结果集编码后面避坑章节还会专门讲。2.2 TMyQuery 与 TMyScript 的分工边界TMyQuery 是最常用的数据集组件它封装了准备语句、参数绑定、结果集读取三件事。v5.10 的源码里TMyQuery 对参数的处理不是简单拼接字符串而是走 PreparedStatement 的协议分支先给服务器发送 COM_STMT_PREPARE拿到 statement ID再用二进制协议绑定参数。这条路径的好处是参数类型可以精确传递坏处是如果参数类型跟表字段类型不匹配服务器端会报转换错误。源码里 TMyParam 这个类的 DataType 枚举控制的就是二进制协议里每个参数的字段类型描述。TMyScript 是另一套逻辑它处理的是多条 SQL 的批量执行。常见场景是把建表语句导进来跑用 TMyScript 可以一次执行整段 DDL而不需要像 TMyQuery 那样一条条执行。v5.10 的实现里TMyScript 会按分隔符切分 SQL 语句逐条发送遇到错误可以配置是跳过还是中断。这里有个容易踩的坑TMyScript 默认把分号当作语句分隔符但如果你的 SQL 里有存储过程定义过程体内的分号会被误切解决方案是把存储过程的 DELIMITER 命令在脚本头部声明或者在源码里改掉语句解析规则。// 用 TMyScript 批量执行 DDL MyScript1.Connection : MyConnection1; MyScript1.SQL.Text : CREATE TABLE IF NOT EXISTS t_user ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL ); CREATE TABLE IF NOT EXISTS t_order ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL );; MyScript1.Execute;TMyScript .Execute 会遍历整个 SQL 文本逐段提交给服务器。注意它默认不会把多条语句包在一个事务里如果中途某条失败前面执行成功的语句不会自动回滚。我一般会在执行前先开一个显式事务配合源码里的事务控制方法保证批量 DDL 的原子性。另外TMyScript 在解析语句时对注释的处理也比较粗行注释跟语句分割混在一起容易出现解析错位所以脚本里尽量少写注释或者统一用 /* */ 块注释。2.3 连接池与事务边界v5.10 的连接池在设计上比新版简单得多它本质上是一个连接对象的复用列表。源码里 TMyConnection 持有一个连接池管理器按连接串的哈希值做分组相同连接串的请求复用同一个池。但这个池是进程内的不是跨进程的所以多进程应用里每个进程都会建自己的池这不算缺陷但你要有预期。池的大小由 Pooling 和 PoolSize 控制PoolSize 到了上限后新请求会排队等待释放而不是无限新建连接。连接池还有一个隐蔽行为空闲连接会被服务器端断开MySQL 的 wait_timeout 默认 8 小时但如果你的服务器设置了更短的超时池里的连接大概率已经死了。MyDAC 的源码在取连接时会发一个 ping 包检测存活检测失败就丢弃重连。这个检测逻辑在 TMyConnection.Validate 相关方法里你可以通过 BeforeConnect 事件观察重连频率。事务边界方面v5.10 的事务是基于连接的同一个连接上所有数据集共享一个事务所以如果你开了多个 TMyQuery它们默认都在同一个事务上下文里。// 显式事务控制范式 MyConnection1.StartTransaction; try MyQuery1.SQL.Text : UPDATE t_account SET balance balance - 100 WHERE id :id; MyQuery1.Params.ParamByName(id).AsInteger : 1001; MyQuery1.Execute; MyQuery2.SQL.Text : UPDATE t_account SET balance balance 100 WHERE id :id; MyQuery2.Params.ParamByName(id).AsInteger : 1002; MyQuery2.Execute; MyConnection1.Commit; except MyConnection1.Rollback; raise; end;事务代码的关键点在于StartTransaction 调用的是 TMyConnection 的方法而不是 TMyQuery。v5.10 的 TMyQuery 没有独立事务能力所有数据集共用连接的事务状态。如果你在不同数据集上交错读写注意保持事务内语句的顺序一致避免死锁。还有一个细节TMyQuery 默认的 FetchAll 是 True也就是 Execute 之后一次性把结果集全部拉回本地改成 False 走流式读取可以在大结果集场景里降低内存占用但连接会被占住直到你把结果集读完或关闭数据集。3. 让 v5.10 在现代环境跑起来的安装与配置3.1 从源文件到 IDE 安装包编译与路径拿到 v5.10 源文件第一件事不是新建项目而是把组件包编译进 IDE。源文件里一般带的是 .dpk 工程文件Delphi Package你需要根据自己用的 IDE 版本选择对应的包。v5.10 那个年代的源码官方发布时可能只支持到 Delphi 2007 或 2010拿到新版 IDE 里编译往往会遇到编译错误最常见的报错是某个 Winapi 头文件路径找不到或者 UnicodeString 类型不兼容。我一般是先把所有 .dpk 都打开逐个编译过去记录下哪个包报错再对症处理。编译顺序有讲究先编译运行期包dcl 前缀之外的那个再编译设计期包dcl 开头的。运行期包会被你的项目引用设计期包负责把组件注册到 IDE 的工具面板上。顺序反了IDE 会提示找不到运行期包。编译后需要把生成的 .bpl 和 .dcp 文件路径加到 IDE 的 Library path 里这样每次新建项目就能直接从组件面板拖出 TMyConnection 等控件。这里的坑在于Library path 有全局和项目两种级别全局配置对所有项目生效项目配置只对当前项目生效如果你多个项目用不同版本的 MyDAC建议用项目级别的配置避免版本冲突。3.2 运行期分发包libmysql.dll 与客户端库的配合MyDAC 不是纯 Delphi 实现它的连接层还需要 MySQL 客户端库的支持。v5.10 源码里有个关键单元处理动态加载 libmysql.dll程序启动时会按 DllName 属性指定的名字查找这个库。默认是 libmysql.dll但你要根据 MySQL 服务端版本选择匹配的客户端库版本。MySQL 5.1 之前和 5.5 的客户端库在认证协议上有差异用错版本会出现握手失败或者方法找不到的报错。运行时部署的常见做法是把 libmysql.dll 放在可执行文件同目录或者在系统 PATH 环境变量指向的目录下。放在同目录最稳因为 MyDAC 的加载逻辑优先找应用程序目录。注意 32 位和 64 位的区分如果你的 IDE 和项目编译目标是 32 位就用 32 位客户端库如果项目是 64 位就用 64 位库。混用会直接报 Bad image 的错误这个现象在 Windows 上非常典型很多人误以为是 MyDAC 组件坏了其实是 DLL 位数不匹配。3.3 环境变量与 IDE 库路径如果你的源文件里有源码级别的调试需求比如想在 TMyConnection 的握手方法里打断点那就要把源文件目录也加进 IDE 的 Debug path。这样 Delphi 在调试时会自动定位到对应的 .pas 文件单步跟踪时能看到 MyDAC 内部实现。这个配置跟 Library path 是独立的不加也能编译但加了之后调试体验完全不同。我一般会新建一个专门的目录把源文件复制进去而不是直接用解压出来的原始目录因为后面改源码、替换文件时不会污染原始备份。环境变量方面要注意的是 Windows 的 PATH。有些机器上装了多个 MySQL 工具或客户端PATH 里的 libmysql.dll 版本可能是被某个应用改写过的。排查问题时先检查 PATH 里所有可能命中的 DLL 路径再用进程监视工具确认程序启动时实际加载了哪一个。这个问题极其隐蔽因为程序不报错的时候你根本不会去查加载路径。可以用一个简单的方式验证在程序启动代码里调用 MyDAC 提供的版本查询接口打印出实际加载的客户端库版本号如果跟你预期不一致再逐步排查加载路径。// 验证实际加载的客户端库版本 procedure TForm1.Button1Click(Sender: TObject); var LibVer: string; begin // 返回当前动态加载的 libmysql.dll 版本字符串 LibVer : MyConnection1.ClientVersion; ShowMessage(LibVer); end;ClientVersion 属性是 MyDAC 直接问 DLL 导出的版本接口拿到的跟服务端版本无关。如果这里显示的值跟你放的 DLL 对不上说明加载路径被 PATH 或系统目录抢先了。解决方法是把目标 DLL 放到 exe 目录并在代码里用 SetDllDirectory 强制优先搜索该目录或者把 DllName 属性写成绝对路径。还有一点v5.10 的 DllName 可以是完整文件名如果留空MyDAC 会按一组默认名字逐个尝试加载这组名字在源码里能看到。4. 用 TMyConnection TMyQuery 跑通最小查询代码与参数4.1 最小连接代码跑通 MyDAC 的最小步骤不是往界面上拖控件而是先写一段不依赖设计时连接配置的代码。这样你能确定问题出在代码还是环境。连接串相关属性如 Server、Username、Password、Database直接在代码里赋值即可。打开连接后判断 Connected 属性是否为 True。这一步能验证客户端库加载、网络连通、认证三个环节。program MyDAC_Minimal; uses System.SysUtils, DAcConnection, MyConnection, MyQuery; var Conn: TMyConnection; Q: TMyQuery; begin Conn : TMyConnection.Create(nil); try Conn.Server : 127.0.0.1; Conn.Port : 3306; Conn.Username : root; Conn.Password : 123456; Conn.Database : testdb; Conn.Options.Charset : utf8mb4; Conn.Open; WriteLn(Connected: , Conn.Connected); Q : TMyQuery.Create(nil); try Q.Connection : Conn; Q.SQL.Text : SELECT 1 AS val; Q.Open; WriteLn(Query result: , Q.FieldByName(val).AsInteger); finally Q.Free; end; finally Conn.Free; end; end.注意这段代码里 Conn.Free 在 Q.Free 之后因为 Q 还引用着连接对象顺序反了会访问空指针。这个顺序问题在界面开发里不常见的问题是因为组件之间有明确的依赖关系。Options.Charset 使用 utf8mb4 而不是老旧的 utf8可以避免后来的 4 字节 emoji 乱码问题。如果服务端字符集是 latin1这里设置 utf8mb4 会导致连接后的字符转换错乱所以 Charset 要跟服务端一致或者服务端统一改成 utf8mb4。Open 失败时异常消息里会带出阶段信息如果是 Cant connect说明网络或端口有问题如果是 Access denied说明用户名密码或认证插件有问题。4.2 参数化查询与类型映射参数化查询是使用 MyDAC 的核心技能。v5.10 的 TMyQuery 用:ParamName语法标识参数通过 Params 属性赋值。参数化有两个好处避免 SQL 注入以及让 MySQL 服务端复用执行计划。后者在高频调用场景下性能差异明显尤其当 SQL 文本完全一致、只换参数值时服务端不需要重新解析 SQL。// 参数化查询示例 MyQuery.SQL.Text : SELECT id, name, balance FROM t_account WHERE balance :min_balance AND status :st; MyQuery.Params.ParamByName(min_balance).AsFloat : 1000.0; MyQuery.Params.ParamByName(st).AsString : active; MyQuery.Open;这里有两个容易出错的地方。一是 ParamByName 的参数名必须跟 SQL 里的完全一致大小写敏感程度取决于源码里的实现v5.10 一般是严格匹配写错了会抛 Parameter not found 的异常。二是类型映射如果字段是 DECIMAL 类型服务端会返回字符串形式的数值用 AsFloat 可能会导致精度丢失正确做法是用 AsBCD 或 AsCurrency 读取。v5.10 源码里对 DECIMAL 的处理是默认映射成字符串字段类型所以你在 Delphi 里读到的 FieldDefs 类型是 ftString而不是 ftFloat。不了解这个映射规则的人常常在读取阶段莫名丢精度。参数类型还有一个隐蔽坑当你给参数赋了字符串值但 SQL 里对应字段是 INT服务端在二进制协议下会把字符串转成数字转换失败会报 1366 错误。为了避免这个问题赋值前要确认参数类型可以直接指定 MyQuery.Params[0].DataType : ftInteger或者用 AsInteger 赋值让组件自动推导。I 在源码里见过不少人在这一块吃了亏以为参数化就不会有类型问题其实参数类型跟字段类型不匹配一样报错。4.3 批量写入与事务提交批量写入是 MyDAC 在数据导入场景里的强项。v5.10 支持两种方案一种是用 TMyQuery 循环执行 INSERT 语句另一种是用 TMyScript 拼接多条语句批量执行。两种方案的性能差异很大循环执行每条语句都在客户端和服务端之间做一次完整往返网络开销是瓶颈TMyScript 把多条语句一次性发送能显著降低延迟但语句太长会触发 MySQL 的 max_allowed_packet 限制超过限制就被直接断开连接。// 批量导入范式带事务包裹 MyConnection1.StartTransaction; try MyQuery.SQL.Text : INSERT INTO t_log (user_id, action, created_at) VALUES (:uid, :act, NOW()); for i : 0 to 999 do begin MyQuery.Params.ParamByName(uid).AsInteger : userIds[i]; MyQuery.Params.ParamByName(act).AsString : actions[i]; MyQuery.Execute; end; MyConnection1.Commit; except MyConnection1.Rollback; raise; end;这条路径的关键是事务包裹的位置。逐条 Execute 默认是自动提交模式如果不包事务每执行一条就提交一次插入 1000 条就要做 1000 次磁盘刷写事务把多次写入合并成一次提交整体耗时能降低 50% 以上。v5.10 的 TMyConnection 在 StartTransaction 之后会把自动提交关掉Commit 或 Rollback 之后恢复。还有一个容易被忽略的点事务期间不要执行 DDL因为 MySQL 会自动隐式提交当前事务导致前面写的业务数据被提前提交一旦后面出错回滚也回滚不掉整个事务的保护就失效了。MySQL 的 max_allowed_packet 默认值在 v5.1 之前是 1MB5.5 之后默认 4MB。如果你用 TMyScript 批量导入要估算单次发送的 SQL 总字节数。有一个简单经验每条 INSERT 按 200 字节估算1000 条就是 200KB远够用但如果你的行里有 JSON 或 BLOB 字段单行可能就超过 1MB那最好改成循环单条执行或者分批提交。v5.10 源码里 TMyScript 在发送前不会自动拆分超长包所以在这个问题上没有后悔药只能调到匹配的值。5. 避坑v5.10 的 5 个高发问题5.1 握手失败显示 Connected但一执行 SQL 就报 Lost connection现象TMyConnection.Open 成功返回Connected 为 True但紧接着 TMyQuery.Open 抛异常信息是 Lost connection to MySQL server during query。原因握手阶段协商成功只代表认证通过但 SQL 执行时客户端库和服务端的协议版本不匹配。最常见的情况是服务端是 MySQL 8.0默认认证插件是 caching_sha2_password而 MyDAC v5.10 自带的客户端库是老版本 libmysql只支持 mysql_native_password。握手时服务器允许你连上但执行查询时客户端库发的能力标志位触发了服务器的不兼容路径连接就被断掉。解决要么把 MySQL 用户认证插件改回 mysql_native_password执行ALTER USER app_user% IDENTIFIED WITH mysql_native_password BY password;要么找一个支持 caching_sha2_password 的客户端库替换默认的 libmysql.dll。但 v5.10 的年代根本没有这样的库所以最常见做法是改用户认证插件。如果你不能改服务器配置那就得考虑升级到新版 MyDAC 或者换官方驱动v5.10 在 MySQL 8.0 面前确实力不从心。5.2 libmysql.dll 位数不对程序启动直接闪退现象程序编译成功双击运行没有任何窗口弹出Windows 事件日志里记录 0xc000007b 错误。原因项目编译目标是 32 位但你放的 libmysql.dll 是 64 位的或者反过来。Windows 在加载 DLL 时的位数检查非常严格一旦不匹配直接拒绝加载不给你任何异常处理机会。这个报错信息很容易被误判成系统组件损坏拿去修 VC 运行库浪费时间。解决打开项目的目标平台配置确认是 Win32 还是 Win64然后下载对应位数的客户端库。放置目录要在 exe 同目录或 PATH 环境下不要同时放两个版本的 DLL 在搜索路径里。验证方法是用 dumpbin 或任务管理器查看已加载模块确认进程实际加载的 DLL 路径。5.3 utf8 乱码写入正常读出来变成问号现象通过 MyDAC 写入的中文在 mysql 客户端里看到正常但用 MyDAC 读出来全是问号或者反过来客户端写入的 UTF-8 中文在 Delphi 界面上显示乱码。原因连接字符集没有设置或者设置为 utf8 不够全面。MySQL 的 utf8 实际上最多支持 3 字节真正完整的 UTF-8 是 utf8mb4。此外 v5.10 还需要在 TMyConnection 的 Options.Charset 里指定字符集这个设置会发送 SET NAMES 语句如果漏了连接就用服务端的默认字符集通常是 latin1 或 utf8跟客户端的 UnicodeString 编码对不上。解决显式设置 Options.Charset : utf8mb4同时确认服务端表结构的字符集也是 utf8mb4。还有一个隐藏点v5.10 在打开连接时如果没有执行 SET NAMES后续写入的字符串会按连接建立时的默认字符集编码导致同样的数据在不同连接上表现不一致。在源码里找到执行 SET NAMES 的位置确认它是在每次连接握手之后自动调用的如果没找到就得自己封装一个打开连接后立即执行的初始化 SQL。5.4 连接池里的死连接运行几小时后突然报错现象程序运行初期一切正常跑了几个小时或者过了一夜某个查询突然报错 Server has gone away重启程序后恢复正常。原因MySQL 服务器的 wait_timeout 或 interactive_timeout 把空闲连接断开了。MyDAC 连接池里的连接停留在池中没有活动服务器超时关闭后池里的连接对象还是认为自己是有效的直到下次取用时才发现 socket 已经不可读。v5.10 源码里虽然有 ping 验证但它只验证 socket 是否能写而服务端关闭连接后TCP 层可能不会马上通知客户端所以 ping 通过但实际执行查询失败。解决把池里的连接回收逻辑调短一点。可以在 TMyConnection 的 BeforeConnect 事件里记录连接建立时间再用定时器定期检查池里的连接是否空闲超过某个阈值超过就直接关闭并从池里移除。另外把 MySQL 服务端的 wait_timeout 调大到 86400 只能缓解不能根治关键还是客户端要有重连保护。最直接的做法是执行 SQL 前用 TMyConnection 的 Validate 或 Ping 方法做一次检查发现连接已死就用新的连接串重新打开。5.5 源码编译时路径硬编码导致换机器失败现象在一台机器上编译好的源码包拷贝到另一台机器后重新编译报找不到某个 .dcu 或 .pas 文件但文件明明就在源码目录里。原因v5.10 源码的 .dpk 文件里可能带绝对路径或者 IDE 的 Search path 配置里引用了原机器的 C 盘路径。Delphi 的包编译在查找单元时按 Library path、Search path、项目文件目录顺序搜索如果源码相对路径不完整就会去原机器的绝对路径里找找不到自然报错。解决在 IDE 的 Tools-Options-Library 里把用不到的旧路径全部清掉只留当前源码目录的相对路径。把所有 .dpk 用文本编辑器打开检查里面有没有 C:\ 之类的绝对路径指向有就改成相对路径或者直接删掉。在源码根目录新建一个 LibraryPath.inc 之类的公共配置文件统一维护路径这样换机器时只需要改一个文件。6. 把源文件变成调试工具协议日志与慢 SQL 定位MyDAC v5.10 源码里最有价值的部分不是组件本身的业务逻辑而是那个能开启协议日志的底层单元。TMyConnection 有一个 LogFile 相关的属性设置后会把客户端和服务端之间的所有协议数据包写入文件。这个功能在生产环境排查问题时极其好用当你觉得 SQL 正确但结果不对打开协议日志能直接看到客户端发出的原始 SQL 字节流和服务端返回的每一段数据。日志里能清晰看到 SQL 是否被正确编码、字符集是否一致、返回结果集的行数是否符合预期。// 开启协议日志用于排查一次诡异查询 MyConnection1.LogFile : c:\temp\mydac_protocol.log; MyConnection1.LogEnabled : True; try MyQuery.SQL.Text : SELECT * FROM t_order WHERE order_no :no; MyQuery.Params.ParamByName(no).AsString : ORD-2024-001; MyQuery.Open; finally MyConnection1.LogEnabled : False; end;定位慢 SQL 是另一个常见用法。MySQL 本身有 slow query log但它是服务端视角MyDAC 的日志能看到客户端视角从发出 SQL 到收到结果集的完整耗时。这个时间包含网络往返、服务端执行、结果集传输三个部分。如果服务端日志显示的慢查询时间短但我这边总耗时长问题就在网络或结果集传输上如果服务端也慢那就去优化 SQL 本身。这种两边的日志对照是判断性能瓶颈在哪一段的可靠办法。日志文件会增长很快生产环境不要常开只在排查问题时开启并及时关闭。我个人习惯是在追查一个具体问题时先把服务端通用日志打开然后在我的代码里开启 MyDAC 客户端日志两边时间戳对齐着看通常半小时内就能定位问题。这个习惯帮我在一个老项目里找出过一次诡异的数据错乱协议日志显示 MyDAC 发的 SQL 是对的但结果集的某一行跟数据库里的对不上最后发现是连接池复用了不同库的会话。这种问题不借助协议日志几乎没法查。希望这篇整理对你有点用至少能让你拿到 v5.10 源文件时知道从哪里下手不至于被旧组件的各种边界问题劝退。看完觉得有用的话找台测试机把 MyDAC 装上跑一遍第 4 章那个最小查询再对比第 5 章的坑应该比直接上手老项目省心得多。本文还有配套的精品资源点击获取