SqlSugar连接MySQL 8.0.29报错排查:认证插件与连接驱动全解析
接到这个问题的人十有八九第一眼看的是 SqlSugar 的代码。可折腾一圈后你会发现SqlSugar 通常只是那个把底层异常原样抛出来的“传话人”真正的根子大多出在 MySQL 8.0.29 的认证插件和连接驱动身上。我在好几个 .NET 项目里都撞上过这个坑现象五花八门有的在Open()就崩有的等跑第一条 SQL 才报错还有的换个环境就莫名恢复。这篇把 SqlSugar 连 MySQL 8.0.29 的报错种类、根因和排查链路一次说清。1. 先把报错场景摆清楚SqlSugar 连 MySQL 8 常见错误地图报错这件事最烦人的地方在于同样写着“连不上”不同阶段抛出来的异常信息完全不是一回事。我习惯先把异常分成四类类别判断对了排查方向基本就锁定了一半。第一类是认证阶段报错。这类报错的典型文本是Authentication method caching_sha2_password not supported by any of the available plugins。它的特征是 SqlSugar 还没开始执行任何 SQL只是创建连接就失败说明 TCP 端口通了但客户端和服务器在“怎么验证密码”这个环节谈崩了。第二类是 SSL/TLS 传输阶段报错。典型如MySqlException: Reading from the stream has failed或者The host xxx does not support SSL connections。这类错误有个迷惑性你单独用命令行客户端连 MySQL 是正常的程序里一跑就挂。原因多半是驱动版本、连接字符串里的 SSL 参数和 MySQL 8.0.29 的 TLS 配置不匹配。第三类是程序集加载报错。比如Could not load file or assembly MySql.Data, Version8.0.0.0。这是 .NET 项目里最常见的依赖地狱问题SqlSugarCore 引用的 MySql.Data 版本和你项目里显式安装的版本不一致程序启动时就会炸。这类错误和 MySQL 服务器本身一点关系都没有。第四类是 ORM 元数据层报错。比如 SqlSugar 在执行 CodeFirst 初始化或查询时抛出Given key was not present in the dictionary或者建表时报Unknown collation: utf8mb4_0900_ai_ci。这是 SqlSugar 在读取 MySQL 8 的 information_schema 元数据时遇到老版本驱动或老版本 ORM 不认识的字段所致。先放一张快速对照表后面每一类都展开讲。报错关键词出现时机第一怀疑方向caching_sha2_password not supported打开连接时驱动版本过旧 / 认证插件不兼容Reading from the stream has failed连接或执行 SQL 时SSL 参数配置不当 / Pipelining 异常does not support SSL connections打开连接时SslMode 和服务器 TLS 策略冲突Could not load file or assembly程序集加载时MySql.Data 版本冲突Given key was not present in the dictionarySqlSugar 查询/建表时SqlSugarCore 版本过旧Unknown collation: utf8mb4_0900_ai_ciCodeFirst 建表时驱动不识别 MySQL 8 默认排序规则这张表的作用不是让你背答案而是帮你在看到报错的第一时间知道“该去查哪个文件、哪段配置”。下面的章节就按这几类逐个拆。2. 主犯是 MySQL 8 默认认证插件caching_sha2_password 的兼容账这一节是重头戏。绝大多数 SqlSugar 连 MySQL 8.0.29 报错根因都不在 SqlSugar 本身而在 MySQL 8 默认认证插件从mysql_native_password换成了caching_sha2_password。2.1 认证插件的机制差异MySQL 5.7 时期默认认证插件是mysql_native_password。它的逻辑很简单客户端把密码哈希发给服务器服务器比对哈希值一致就通过。旧版 MySql.Data、MySqlConnector 都按照这个逻辑来实现所以老项目连 MySQL 5.7 从来不纠结认证参数。MySQL 8.0 开始默认插件变成了caching_sha2_password。它的校验方式复杂得多尤其是首次连接或者缓存未命中时为了不把明文密码直接暴露在网络上需要一个“请求服务器 RSA 公钥 用公钥加密密码再传输”的过程。如果连接本身没有走 TLS客户端就必须主动向服务器索要公钥。问题就出在这里旧版驱动根本不认识caching_sha2_password这个协议也不知道去哪拿 RSA 公钥于是直接抛Authentication method caching_sha2_password not supported by any of the available plugins。这年代久远的驱动并不是版本号差了一点点而是整套认证流程都不适配。2.2 方案一把用户认证方式改回老插件这是网上流传最广的解法也确实能最快让程序跑通。登录 MySQL 命令行执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;生产环境我一般不建议直接用 root而是单独建一个应用账号CREATE USER app_user% IDENTIFIED WITH mysql_native_password BY 强密码; GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO app_user%; FLUSH PRIVILEGES;改完可以用这条 SQL 确认SELECT user, host, plugin FROM mysql.user WHERE user app_user;如果你希望整个实例都回到老插件行为也可以在my.cnf里加[mysqld] default-authentication-pluginmysql_native_password然后重启 MySQL 服务。但这里必须泼一盆冷水这个方案只对 MySQL 8.0.29 暂时有效因为mysql_native_password从 8.0.34 开始默认禁用后面版本迟早要清理。你可以在 8.0.29 上拿它止血但不该把它当长期方案。2.3 方案二升级驱动并显式允许公钥获取我更推荐的做法是解决客户端能力问题。如果你项目里用的是 MySql.Data请升级到 8.0.29 或更高版本MySqlConnector 则建议 2.2.6 以上。升级驱动只是第一步连接字符串里还必须加两个参数SslModeNone;AllowPublicKeyRetrievalTrue;AllowPublicKeyRetrievalTrue的作用是允许客户端在首次连接时向服务器请求 RSA 公钥。这个参数默认是关的因为它存在中间人风险——公钥如果在不安全通道里被替换密码就可能被截获。所以需要SslModeNone配合使用时你心里要有数如果网络环境不可信建议反过来用SslModeRequired;AllowPublicKeyRetrievalFalse走 TLS 反而更安全。两个方案放一起对比维度改回 mysql_native_password升级驱动 公钥获取见效速度最快一条 SQL 的事需要改包版本和连接串安全性旧哈希算法偏弱符合 MySQL 8 默认安全策略长期兼容性差后续 MySQL 版本会禁用好可平滑升级现网影响用户认证方式改变审计需留意主要是发布变更我的经验是本地开发图省事可以改认证插件但一旦要上测试环境或生产环境必须走方案二。别把临时止血当成永久修复否则下一个版本升级时你会再被咬一次。3. 连接字符串参数差一个都连不上MySQL 8 的连接行为对连接字符串参数非常敏感。很多时候代码本身没问题包版本也够新但就是连接失败就是因为少了某个参数或者参数取值不对。3.1 高频参数速查表参数名适用驱动推荐值作用AllowPublicKeyRetrievalMySql.Data / MySqlConnector非 TLS 时设为 True允许客户端获取 RSA 公钥SslModeMySql.Data / MySqlConnectorNone 或 Required控制 TLS 策略PipeliningMySqlConnector 专用遇到流读取异常时设为 False关闭管线化复用CharSetMySql.Data 常用utf8mb4指定字符集Character SetMySqlConnector 常用utf8mb4指定字符集Connection Timeout通用15~30建连超时秒数Default Command Timeout通用30SQL 执行超时秒数3.2 连接字符串到底该怎么写如果你是 SqlSugar MySql.Data 的组合推荐用这个Server127.0.0.1;Port3306;Databasetestdb;Uidroot;Pwd你的密码;CharSetutf8mb4;SslModeNone;AllowPublicKeyRetrievalTrue;ConnectionTimeout15;如果你是 SqlSugar MySqlConnector 的组合推荐用下面这个。注意 MySqlConnector 的参数名和 MySql.Data 有细微差别比如Character Set带空格User Id、Password这种写法它更认Server127.0.0.1;Port3306;Databasetestdb;User Idroot;Password你的密码;Character Setutf8mb4;SslModeNone;AllowPublicKeyRetrievalTrue;PipeliningFalse;Connection Timeout15;很多人在这个阶段最容易犯的错是照抄网上的连接字符串但没注意自己用的是哪个驱动。MySql.Data 和 MySqlConnector 是两个不同的库虽然参数大体兼容但在Pipelining、Character Set这些细节上的处理不一样。抄错了就很尴尬程序跑在测试机没问题一到服务器上就报Reading from the stream has failed。3.3 关于 SslMode 的一个容易踩的细节SslMode有几个取值None、Preferred、Required、VerifyCA、VerifyFull。很多初学者喜欢用Preferred觉得“能用 TLS 就用 TLS不能就算了”这种模棱两可的设置在 MySQL 8.0.29 上反而容易出问题。我遇到过的现象是连接字符串设了SslModePreferred偶尔能连上偶尔报Reading from the stream has failed。后来发现是驱动在 TLS 和明文之间切换时与连接池里的旧连接产生了握手状态混乱。解决办法就是别用Preferred要么明确None要么明确Required。另外提醒一点AllowPublicKeyRetrievalTrue和SslModeRequired同时设置时其实并不需要公钥获取因为 TLS 已经保证了链路加密。所以如果你走 TLS可以把AllowPublicKeyRetrieval去掉或设为 False减少一个攻击面。4. SqlSugar 工程内的配置坑包版本、DbType 和 CodeFirst 初始化当你把底层连接打顺了接下来要面对的就是 SqlSugar 这一侧的配置问题。很多人会把这段忽略以为 SqlSugar 只是个 ORM连接字符串丢进去就能跑实际上有几个坑相当隐蔽。4.1 标准 ConnectionConfig 写法先给一个经过验证的 SqlSugar 初始化写法using SqlSugar; var db new SqlSugarClient(new ConnectionConfig { ConnectionString Server127.0.0.1;Port3306;Databasetestdb;Uidroot;Pwd你的密码;CharSetutf8mb4;SslModeNone;AllowPublicKeyRetrievalTrue;, DbType DbType.MySql, IsAutoCloseConnection true, InitKeyType InitKeyType.Attribute });需要注意InitKeyType。如果你用实体类特性标注主键用InitKeyType.Attribute如果希望 SqlSugar 直接从数据库表结构读取主键用InitKeyType.SystemTable。这个配置本身不直接导致连接报错但它会影响 SqlSugar 首次访问表结构时是否要查 information_schema在某些异常场景下会放大超时问题。4.2 DbType.MySql 和 DbType.MySqlConnector 别搞混SqlSugar 的DbType枚举里既有MySql也有MySqlConnector这是很多人忽略的。它们分别对应底层走 MySql.Data 还是 MySqlConnector。如果你在项目里引用的是 MySql.Data就写DbType.MySql如果你引用的是 MySqlConnector就写DbType.MySqlConnector。写错的情况下SqlSugar 会尝试用自己的驱动适配层去加载不匹配的程序集轻则抛Could not load file or assembly重则出现连接建立了但类型转换失败的怪异问题。怎么确认项目里到底引了哪个驱动最直接的办法是打开.csproj文件看 PackageReferenceItemGroup PackageReference IncludeSqlSugarCore Version5.1.4.183 / PackageReference IncludeMySql.Data Version8.0.29 / /ItemGroup或者ItemGroup PackageReference IncludeSqlSugarCore Version5.1.4.183 / PackageReference IncludeMySqlConnector Version2.2.6 / /ItemGroup不建议两个驱动同时引除非你有必须这么做的理由。两套驱动并存时程序集绑定冲突的概率会急剧上升。4.3 Could not load file or assembly 的处理思路这个报错出现时先别急着翻代码。按这个顺序排查第一步打开“输出”窗口看完整异常里提示的程序集版本号。比如它说需要MySql.Data, Version8.0.21.0而你项目里装的是8.0.29.0说明 SqlSugarCore 某个依赖写死了低版本。第二步在.csproj里把 MySql.Data 的版本固定然后重新生成。常见做法是让项目中显式引用的 MySql.Data 版本不低于 SqlSugarCore 依赖的版本。第三步如果你的项目是 .NET Framework可能还要处理 binding redirect。Visual Studio 里右键项目选择“添加绑定重定向”可以自动生成。别手动改app.config容易改漏。还有一个隐蔽场景你本地跑得好好的发布到服务器上报这个错绝大多数是发布目录里混入了旧版 dll。清理bin和obj目录重新发布即可。4.4 CodeFirst 初始化相关的排序规则问题SqlSugar 的db.CodeFirst.InitTablesT()在 MySQL 8 上可能抛出Unknown collation: utf8mb4_0900_ai_ci这个报错是驱动不认 MySQL 8.0 新增的默认排序规则。表面上看是 SqlSugar 建表语句带出的 COLLATE 子句实际是 MySQL 8 实例默认字符集排序规则是utf8mb4_0900_ai_ci而老驱动去解析 DDL 时对不上号。两条路可以走升级驱动。MySql.Data 8.0.29、MySqlConnector 2.x 对utf8mb4_0900_ai_ci支持都没问题。如果项目暂时不能升级驱动就把 MySQL 库的默认排序规则改成utf8mb4_general_ci。查询当前默认值SELECT default_character_set_name, default_collation_name FROM information_schema.SCHEMATA WHERE schema_name testdb;需要修改时把应用连的库改成utf8mb4_general_ci。这是一个兼容性和新特性之间的取舍业务上一般不会感知到差异。5. 从报错堆栈到根因的完整排查链路不管前面的分类多清晰真正遇到问题的时候你手里拿到的往往只是一堆堆栈。下面这套排查链路我每次都会走一遍按这个顺序基本能在十分钟内定位到根因。5.1 第一步把 SqlSugar 摘出去裸测驱动最先要判断的是报错到底来自 SqlSugar 还是来自底层驱动。新建一个控制台项目只引用驱动不引用 SqlSugar写一段最原始的 ADO.NET 代码using var conn new MySqlConnection(Server127.0.0.1;Port3306;Databasetestdb;Uidroot;Pwd你的密码;CharSetutf8mb4;SslModeNone;AllowPublicKeyRetrievalTrue;); conn.Open(); using var cmd conn.CreateCommand(); cmd.CommandText SELECT VERSION(); Console.WriteLine(cmd.ExecuteScalar());如果这段代码在Open()就报错那问题基本在驱动和服务器之间SqlSugar 只是无辜的传话人回到第 2、3 节去调认证和连接字符串。如果这段能正常打出 MySQL 版本号而 SqlSugar 一跑就报错那就是 SqlSugar 配置的问题回到第 4 节查DbType、包版本和InitKeyType。这一步能帮你节省至少一半的排查时间。5.2 第二步用 MySQL 命令行交叉验证裸测驱动仍然失败的时候不要急着改代码先用服务器自带的命令行客户端连一下mysql -h127.0.0.1 -P3306 -uroot -p输入密码后如果能进入命令行说明 MySQL 服务本身正常、端口在监听、账号密码也正确。这时候问题高度集中在驱动的连接参数尤其是SslMode、AllowPublicKeyRetrieval。如果命令行也连不上那就要分情况看Access denied for user账号或密码错或者该用户不允许从这个来源登录。Cant connect to MySQL server检查端口、bind-address、防火墙。连接后立刻断开检查 MySQL 的wait_timeout或连接被某种中间层拦截。命令行能通但程序不通是典型的驱动参数问题。不要怀疑人生先回去把第 3 节的表格逐项核对一遍。5.3 第三步检查账号的 host 权限MySQL 的账号是由user host共同决定的。很多人只看到mysql.user表里有个rootlocalhost就以为 root 到处都能连其实localhost和127.0.0.1在某些解析场景下还不一定是同一个匹配项。看一下当前账号情况SELECT user, host, plugin FROM mysql.user;如果你的应用要从另一台服务器连接而用户表里只有rootlocalhost那必然报权限错误。解决办法是给应用单独建一个允许任意来源的账号CREATE USER app_user% IDENTIFIED BY 强密码; GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO app_user%; FLUSH PRIVILEGES;有一个细节值得注意MySQL 匹配用户时会优先匹配更具体的主机记录。如果你既存在app_userlocalhost又存在app_user%从本机连接时会命中前者。如果前者密码不对就会出现“我明明改了密码怎么还报 Access denied”的怪事。5.4 第四步最小可复现 Demo把问题缩小到一个最小项目里复现既能验证你的假设也能让你带着一段完整的可运行代码去问别人省得来回猜。一个最小 Demo 的Program.cs长这样using SqlSugar; var db new SqlSugarClient(new ConnectionConfig { ConnectionString Server127.0.0.1;Port3306;Databasetestdb;Uidroot;Pwd你的密码;CharSetutf8mb4;SslModeNone;AllowPublicKeyRetrievalTrue;, DbType DbType.MySql, IsAutoCloseConnection true }); try { var result db.Ado.GetInt(SELECT 1); Console.WriteLine($连接成功SELECT 1 结果{result}); } catch (Exception ex) { Console.WriteLine(ex.ToString()); }这段代码如果跑通说明 SqlSugar 连接链路没问题接下来回到具体业务代码里排查如果跑不通异常堆栈会直接告诉你问题在认证、SSL、加载程序集还是元数据层。5.5 第五步不要忽略连接池缓存最后单独提醒一个东西连接池。你在测试环境改了连接字符串有时候不怎么生效是因为连接池里还留着旧连接。尤其 MySQL 8 默认的wait_timeout是 8 小时如果池里那个连接没被回收应用可能一直用旧连接续命。解决方式其实很简单修改连接字符串后重启应用进程。别用“刷新页面”这种程度的方式去验证不够彻底。如果用的是 ASP.NET Core重启应用池或者直接发布一个新版本再试。别问我怎么知道“改了没生效”有多耽误时间的。6. 生产环境实跑后才知道的几个隐藏注意事项连接终于打通了不代表事情就结束了。下面几个问题是我在生产环境跑了一段时间之后才真正体会到的每一个都值得提前注意。6.1 字符集别只停留在连接字符串很多人把CharSetutf8mb4写上就以为字符集万事大吉实际上 MySQL 服务端、数据库、表、字段都有各自的字符集设置。连接字符串只负责告诉驱动“你以什么字符集来编码”但表字段如果还是latin1你写入中文照样乱码。建议上线前统一检查一遍SELECT table_schema, default_character_set_name, default_collation_name FROM information_schema.SCHEMATA WHERE table_schema NOT IN (information_schema,performance_schema,mysql,sys);如果发现某个库不是utf8mb4可以谨慎地转换。但要先备份别上来就 ALTER字符集转换会重写整张表数据量大的时候会锁很久。另外再说一次排序规则。MySQL 8 默认的utf8mb4_0900_ai_ci在老驱动下可能引发报错。如果你对排序规则没有特殊要求建议统一用utf8mb4_general_ci兼容性和业务覆盖度都够用。6.2 时区问题比想象中更容易出现SqlSugar 连上 MySQL 8.0.29 后如果发现时间字段查出来总是差 8 小时先别急着改业务代码。大概率是 MySQL 会话时区和 .NET 进程本地时区不一致导致的。先用这条 SQL 看看SELECT global.time_zone, session.time_zone;如果结果是SYSTEM说明用的是操作系统时区。此时检查 MySQL 所在服务器的系统时区以及 .NET 容器的时区两边对齐后再看数据。MySqlConnector 可以在连接字符串里通过Connection Timezone参数影响时间读取行为但我的建议是先把服务器时区统一成08:00或UTC再做驱动层面的参数调整。直接在代码里对 DateTime 做加法属于表面补丁后面排查别的关联数据时会很痛苦。6.3 连接池和 SqlSugar 生命周期SqlSugar 的IsAutoCloseConnection true表示每次用完连接后自动关闭但这不代表你可以无脑在每个方法里new SqlSugarClient。连接对象的创建和销毁有成本而且如果你在事务里不小心把IsAutoCloseConnection设为 true事务提交前连接就可能被回收导致异常。我的实践经验是在 ASP.NET Core 里把 ISqlSugarClient 注册成单例或作用域服务不搞按需 new。示例services.AddSingletonISqlSugarClient(sp { var config sp.GetRequiredServiceIConfiguration(); return new SqlSugarClient(new ConnectionConfig { ConnectionString config.GetConnectionString(AppDb), DbType DbType.MySql, IsAutoCloseConnection true, InitKeyType InitKeyType.Attribute }); });注册成单例并不危险因为 SqlSugarClient 内部会自己管理连接池只要你的 SQL 没有长时间占用连接单例反而是性能更好的选择。6.4 别用 root 上生产这一点放最后说因为真的太常见了。开发环境用 root 连 MySQL 没问题但生产环境一旦被人拿到应用配置就等于拿到了整个实例的最高权限。建议给应用单独建账号并按实际需要授权。比如一个只做增删改查的应用给SELECT, INSERT, UPDATE, DELETE就够了。哪怕后面发现少权限再补授权也就是一条 SQL 的事但一开始就给全部权限出事的时候后悔都来不及。我个人经历里最贵的一次教训是某次发版时不小心把生产环境指向了测试库还因为应用账号权限过大连清数据的操作都成功执行了。从那以后我对生产账号权限卡得极严宁可被同事嫌“怎么又没权限”也不愿账号失控。权限复核脚本可以定期用SELECT user, host, plugin FROM mysql.user WHERE user ! root;把不认识的账号清掉把明显过宽的授权收回来。这个习惯能帮你避开很多和 SqlSugar 无关但和“数据库安全”有关的深夜故障。最后再分享一条操作心得每次把 SqlSugar 和 MySQL 8.0.29 这套组合做版本升级时别只把 NuGet 包升上去就完事。升级完先跑一遍最小 Demo确认认证、SSL、字符集这三个点没被新版本影响再放开到业务模块。我把这个流程写成了一张核对表放在项目文档里每次升级就按表走一遍。MySQL 8 的版本迭代比想象中频繁连接行为也一直在调整提前设好防线后面才不会老被“连接报错”追着跑。