DotNetNext / SqlSugar

.Net aot ORM Fastest ORM Simple Easy VB.NET Sqlite orm Oracle ORM Mysql Orm 虚谷数据库 postgresql ORm SqlServer oRm 达梦 ORM 人大金仓 ORM 神通ORM C# ORM , C# ORM .NET ORM NET5 ORM .NET6 ORM ClickHouse orm QuestDb ,TDengine ORM,OceanBase orm,GaussDB orm ,Tidb orm Object/Relational Mapping
https://www.donet5.com/Home/Doc
MIT License
5.35k stars 1.34k forks source link

针对MysqlConnector有必要单独开一个仓库维护 #974

Closed brucehu123 closed 2 years ago

brucehu123 commented 2 years ago

sqlsugar目前在nuget 有 sqlSugarCore.MySqlConnector,支持使用MysqlConnector连接mysql,但是目前在是使用上有很大的问题

比如:打开连接使用同步方法,connection.open(),执行命令使用异步方法 command.ExecuteNonQueryAsync。。。 这么用可能只是沿用之前Mysql.Data的用法,但这在MysqlConnector是被严格禁止的。因为这会造成很多问题,比如,连接池泄漏

其他还有很多细节,不一一列举

希望能够针对MysqlConnector单独分一个仓库维护

DotNetNext commented 2 years ago

MySqlConnector 不至于这么差吧,你能提供 重可以重现的DEMO吗?

DotNetNext commented 2 years ago

或者你使用SqlSugarScope 单例模式试试 SqlSugarScope 是线程安全对象 可以解决很多线程问题

brucehu123 commented 2 years ago

MySqlConnector 不至于这么差吧,你能提供 重可以重现的DEMO吗?

这个不是差的问题,应该是用法的问题。晚点时候我提供一个可以重现的demo

或者你使用SqlSugarScope 单例模式试试 SqlSugarScope 是线程安全对象 可以解决很多线程问题

SqlsugarScope 没试过。今天有空了也试下

DotNetNext commented 2 years ago

SqlsugarScope 记得用单例,你可以测试一下,如果SqlSugarScope OK那么SqlSugarClient100%是你线程安全没注意好

DotNetNext commented 2 years ago

用例有了吗

brucehu123 commented 2 years ago

用例有了吗

我们项目目前还在压测,周末我整理一个demo出来

brucehu123 commented 2 years ago

我们项目中遇到的连接池泄漏跟SqlSugar.MySqlConnector没有关系,是由于ProxySQL的bug导致的 bug连接:unknown command ProxySQL暂时不支持reset connection

针对同步打开连接的问题: 我这边今天做了一个小demo:demo

分别对比了SqlSugarClient,SqlSugarScope,MySqlConnector(OpenAsync)的并发情况, 并发线程数 20 的时候,三者相差不大, 并发线程数 50 的时候,SqlSugarClient,SqlSugarScope 差不多,MySqlConnector 异步打开连接明显胜出 并发线程数 100 的时候,SqlSugarClient,SqlSugarScope 差不多,MySqlConnector 异步打开连接Tps是前两者的2倍左右 并发线程数超过300的时候,SqlSugarClient开始偶尔报连接池泄漏,MySqlConnector 异步打开连接不会报

基本可以看出,并发越高,SqlSugarClient和MySqlConnector 异步打开连接的tps差距越大

@donet5 作者可以测试下,情况是否跟我一致

我这边测试样本数据 800w

brucehu123 commented 2 years ago

可以参考下最佳实践:MySqlConnector Best Practices

DotNetNext commented 2 years ago

你把自动释放关了, 这样测试一下看看,是否能解决,如果可以就是你说的原因

      using (var db = GetDb())
                            {
                                await (db.Ado.Connection as MySqlConnector.MySqlConnection).OpenAsync();
                                var param = new { userid = random.Next(0, 10000000) };
                                var result = await db.Ado.SqlQuerySingleAsync<string>("SELECT nickname from users where userid=@userid;", param);

                                await (db.Ado.Connection as MySqlConnector.MySqlConnection).CloseAsync();
                                Console.WriteLine($"结果:{result}");
                            }
brucehu123 commented 2 years ago

你把自动释放关了, 这样测试一下看看,是否能解决,如果可以就是你说的原因

      using (var db = GetDb())
                            {
                                await (db.Ado.Connection as MySqlConnector.MySqlConnection).OpenAsync();
                                var param = new { userid = random.Next(0, 10000000) };
                                var result = await db.Ado.SqlQuerySingleAsync<string>("SELECT nickname from users where userid=@userid;", param);

                                await (db.Ado.Connection as MySqlConnector.MySqlConnection).CloseAsync();
                                Console.WriteLine($"结果:{result}");
                            }

是的,这么改之后tps基本一致了。

DotNetNext commented 2 years ago

我测试过了 确实有你说的问题 mysql.data就不受影响 ,我想想 MysqlConnector 如何去改

DotNetNext commented 2 years ago

SqlSugarCore.MySqlConnector 更新到5.0.45 你测试一下,我本地OK了

DotNetNext commented 2 years ago

我发现以前版本也没有问题的, MAX Pool Size=100或者去掉 就可以了

DotNetNext commented 2 years ago

我就说嘛 MysqlConnector 怎么会这么垃圾肯定是用法问题

brucehu123 commented 2 years ago

优秀,效率太高了!! 测试过已经没问题了!

brucehu123 commented 2 years ago

@donet5 昨天我做了下全面的测试。似乎还有一些问题,tps提高了,但是线程池泄漏的问题还在。 我把线程调到300并发的时候,连接池泄漏发生的概率比较高。即便我把最大连接池设置成100,依然会发生泄漏。我认为这个时候,程序应该是有死锁发生了。 我用MySqlConnector OpenAsync方式没有发生过连接池泄漏。

我尝试用MySqlConnector模仿SqlSugar的写法时,同样会发生连接池泄漏,代码如下:

  var connString = "";
                            using (var conn = new MySqlConnection(connString))
                            {
                                using (var cmd = await Task.Run(() =>
                                {
                                    var command = new MySqlCommand("SELECT nickname from users where userid=@userid;", conn);
                                    command.Parameters.AddWithValue("userid", random.Next(0, 10000000));
                                    conn.Open();
                                    return command;
                                }))
                                {
                                    using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
                                        while (await reader.ReadAsync())
                                        {
                                            for (var i = 0; i < reader.FieldCount; i++)
                                            {
                                                var val = reader.GetValue(i);
                                                if (val == DBNull.Value)
                                                    Console.WriteLine("结果:空");
                                                else
                                                    Console.WriteLine($"结果:{val.ToString()}");
                                            }
                                        }
                                }
                            }

Using Synchronous Methods can have adverse effects on the managed thread pool and cause slowdowns or lock-ups if not properly tuned. The recommended approach is to use all Asynchronous Methods.

MySqlConnector的作者在最佳实践里也提到过,用同步的方法会有一系列的线程问题,甚至死锁。 我认为使用MySqlConnector应该全部使用异步方法

DotNetNext commented 2 years ago

按你需求改了发布了个版本 5.0.4.6

brucehu123 commented 2 years ago

按你需求改了发布了个版本 5.0.4.6

好的,我明天再测试下

brucehu123 commented 2 years ago

按你需求改了发布了个版本 5.0.4.6

这个版本测试 连接和tps都没问题了!

DotNetNext commented 2 years ago

好的 ,那你在使用上多测测