注册
SqlSugar框架连接达梦主备实现高可用及此框架高并发条件下程序与数据库断连的解决方法
培训园地/ 文章详情 /

SqlSugar框架连接达梦主备实现高可用及此框架高并发条件下程序与数据库断连的解决方法

Pilgrims 2026/04/28 435 0 0

一、SqlSugar连接达梦数据库主备集群

1:环境说明

(1)数据库版本:DM8(2节点实时主备)
(2)开发工具:visual studio 2022
(3).NET版本:.NETCore 5.0.0
(4)SqlsugarCore版本:SqlSugarCore 5.1.4.167
(5)SqlSugarCore.DM版本:SqlSugarCore.DM 8.6.0
(6)DmProvider.dll版本:8.3.1.27409

2:配置达梦服务名连接方式

配置服务名连接数据库,是实现高可用的一个前提。通过读取服务名的配置信息,应用程序能根据具体要求去动态连接数据库节点,比如实现只连接主库、读写分离等要求。
一般情况下,在数据库安装完成后,在安装环境下会自动生成服务名的配置文件dm_svc.conf。如果没有,在对应的路径下创建也可。
windows环境下,该文件在C:\Windows\System32下;linux环境下,该文件的路径为/etc下。
本次以配置两节点主备集群的服务名为例,配置参考如下:

TIME_ZONE=(480)
LANGUAGE=(cn)
DMDW=(192.168.100.88:7236,192.168.100.88:7237)
[DMDW]
LOGIN_MODE=(1)

该配置的含义是,默认连接集群中的主库,无论主库在那个节点上。
配置完成后,可通过disql工具验证配置情况,如图:
image.png
配置服务名后,数据库的连接便可以不用指定具体的IP和端口,直接指定服务名即可连接集群,并且只会连接主库。
到此,数据库的服务名连接方式就完成了。

3:验证调试连接方式

(1)示例代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using SqlSugar;

namespace SqlSugarScopeConnectionDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // 数据库连接字符串配置,需自行修改
            var connectionString = XXXXXXXXXXXXXXXX";
            // 输出实际传递给数据库的连接字符串
            Console.WriteLine("实际传递给数据库的连接字符串:");
            Console.WriteLine(connectionString);
            // 使用 SqlSugarScope 管理数据库连接
            SqlSugarScope db = new SqlSugarScope(new ConnectionConfig()
            {
                ConnectionString = connectionString,
                DbType = DbType.Dm, // 根据数据库类型调整
                IsAutoCloseConnection = true // 自动关闭连接
            });
            try
            {
                // 测试连接并执行简单查询以验证连接是否成功
                db.Ado.Open(); // 打开连接
                Console.WriteLine("数据库连接成功!");

                // 输出连接成功后使用的 IP 和端口信息
                var usedConnection = db.Ado.Connection;
                Console.WriteLine($"当前连接的节点信息: {usedConnection.Database}");
                // 执行查询: SELECT SYSDATE FROM DUAL
                var sysdate = db.Ado.SqlQuery<DateTime>("SELECT SYSDATE FROM DUAL").FirstOrDefault();
                Console.WriteLine($"查询结果 - SYSDATE: {sysdate}");
                // 并发操作模拟:插入和查询
                Console.WriteLine("开始并发数据操作...");
                int concurrencyLevel = 100; // 并发任务数量
                var tasks = new List<Task>();
                // 记录开始时间
                var stopwatch = Stopwatch.StartNew();

                for (int i = 0; i < concurrencyLevel; i++)
                {
                    tasks.Add(Task.Run(() => PerformConcurrentOperations(db)));
                }
                await Task.WhenAll(tasks);
                // 记录结束时间并输出耗时
                stopwatch.Stop();
                Console.WriteLine($"并发数据操作完成!总耗时: {stopwatch.ElapsedMilliseconds} 毫秒");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"连接失败! 错误信息: {ex.Message}");
                Console.WriteLine($"错误代码: {ex.HResult}"); // 返回错误代码
            }
            finally
            {
                db.Ado.Close(); // 确保关闭连接
            }
        }
        static void PerformConcurrentOperations(SqlSugarScope db)
        {
            try
            {
                // 执行查询操作
                var data = db.Queryable<TestTable>().ToList();
                Console.WriteLine($"查询成功,获取到 {data.Count} 条记录。");

                // 执行插入操作
                var insertResult = db.Insertable(new TestTable()
                {
                    Name = $"TestData-{Guid.NewGuid()}",
                    CreateTime = DateTime.Now
                }).ExecuteCommand();

                if (insertResult > 0)
                {
                    Console.WriteLine("插入操作成功。");
                }
                else
                {
                    Console.WriteLine("插入操作失败。");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"并发操作错误: {ex.Message}");
            }
        }
    }
    [SugarTable("TestTable")]
    public class TestTable
    {
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime CreateTime { get; set; }
    }
}

(2)验证1:直接参照disql的连接方式来写连接串

var connectionString = "server=DMDW;USER ID=SYSDBA;PWD=SYSDBA;connPooling=true;connPoolSize=200;";

运行代码,报错提示如下:
image.png
报错提示无法识别DMDW,从而导致连接失败。

(3)验证2:连接串配置全部的节点信息

var connectionString = "server=(192.168.100.88:7236,192.168.100.88:7237);LOGIN_MODE=(1);User Id=SYSDBA;Password=SYSDBA;connPooling=true;connPoolSize=200";

运行代码,连接成功,执行后续的100并发操作,如图:
image.png
image.png
截图可见,这个时候,连接到的节点是DW01,执行100并发的查询及插入,耗时107276 毫秒。进一步排查执行效率低下的原因,在备库DW02的sqllog日志里面,可以看到如下信息。
image.png
可见,在备库的日志中,出现了申请会话和释放会话的信息。但是在实际连接信息中,连接到的节点是DW01,那所有的SQL操作也应该是DW01来完成。这应该就是为什么执行效率较低的原因:SqlSugar在处理数据库操作的过程中,会不断地遍历两个节点,从而导致执行耗时。
同时,连接串不变的情况下,验证主备切换后是否能正常连接并执行数据库操作。通过dmmonitor工具,执行switchover进行节点切换,如图:
image.png
截图可见,目前DW02已经切换为主库。再次执行程序,报错如下:
image.png
可见,虽然主备已经发生切换,但是连接到的节点还是DW01,而此时该节点已经成为备库,只允许读,所以后续的插入操作都报错了。说明这种连接方式,程序只会去连接连接串配置的第一个IP:PORT。

(4)解决办法:连接串中指定dm_svc.conf路径

var connectionString = "server=DMDW;svc_conf_path=C:\\windows\\system32\\dm_svc.conf;USER ID=SYSDBA;PWD=SYSDBA;connPooling=true;connPoolSize=110;";

运行代码,如下:
image.png
image.png
截图可见,程序能自己连接到当前的主节点DW02,且同样的100并发,耗时仅 3863 毫秒。同时,连接串不变的情况下,再次使用达梦的dmmonitor工具进行主备切换,如图:
image.png
再次运行测试代码,如图:
image.png
可见,即使主备发生切换,程序也能准确连接到主节点,从而实现高可用的需求。

4:总结

通过服务名的方式连接达梦主备集群,能完全满足SqlSugar框架在对高可用的需求,在配置的连接串中除了要声明服务名的名称,还需要指定服务名配置文件dm_svc.conf的路径。

二、sqlsugar程序在高并发情况下与数据库断连的问题处理

1:问题描述

在实际项目中会遇到,SqlSugar框架下,数据库运行正常,连接访问正常的前提下,一旦开始并发操作,并发操作中的部分任务会失败,提示的错误原因内容如下:
image.png
实际的错误代码中,除了-6123的报错,还会有-6001的报错,提示的中文内容均是需要检查字符串是否正确。

2:问题排查

检查数据库会话及负载,确认在断连的时候,数据库运行正常,负载正常,日志无异常,使用会话数远低于配置的最大值。
且在中断后几秒内,在未调整任何参数的情况下,程序又可以恢复正常的并发操作,因此基本可以判断问题并非是数据库造成的。

3:解决方法

(1)开启并配置线程池,连接串修改为:

var connectionString = "server=IP;PORT=XXXX;user id=XXXX;PWD=XXXX;connPooling=true;connPoolSize=200;";

连接池中配置参数connPooling=true;connPoolSize=110。
重新运行代码:
image.png
可见,配置连接池后,该问题消失。需要说明的是,配置的连接池数量应该要略大于实际的并发数,否则也会报错。验证中,100并发需求下,若连接池配置也为100,也可能会出现断联的情况。
(2)优化代码逻辑,确保每个并发都独立创建并使用创建的连接。示例:
image.png
image.png
说明:修改后,每个并发任务均会独立创建并管理数据库连接,只要并发数不超过数据库配置的最大连接数(MAX_SESSIONS)就是可用的。

4:总结

(1)高并发情况下,需要配置连接池,并且调整合适的连接池数量。
(2)注意优化代码逻辑,让每个并发任务都独立的创建并使用对应的会话连接,避免出现因资源争抢造成断连情况。
(3)同时,也需同步重关注,框架使用的SqlSugarCore.DM版本版本是否过低,或者与现场的数据库自带版本不一致等情况。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服