(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
配置服务名连接数据库,是实现高可用的一个前提。通过读取服务名的配置信息,应用程序能根据具体要求去动态连接数据库节点,比如实现只连接主库、读写分离等要求。
一般情况下,在数据库安装完成后,在安装环境下会自动生成服务名的配置文件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工具验证配置情况,如图:
配置服务名后,数据库的连接便可以不用指定具体的IP和端口,直接指定服务名即可连接集群,并且只会连接主库。
到此,数据库的服务名连接方式就完成了。
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; }
}
}
var connectionString = "server=DMDW;USER ID=SYSDBA;PWD=SYSDBA;connPooling=true;connPoolSize=200;";
运行代码,报错提示如下:
报错提示无法识别DMDW,从而导致连接失败。
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并发操作,如图:
截图可见,这个时候,连接到的节点是DW01,执行100并发的查询及插入,耗时107276 毫秒。进一步排查执行效率低下的原因,在备库DW02的sqllog日志里面,可以看到如下信息。
可见,在备库的日志中,出现了申请会话和释放会话的信息。但是在实际连接信息中,连接到的节点是DW01,那所有的SQL操作也应该是DW01来完成。这应该就是为什么执行效率较低的原因:SqlSugar在处理数据库操作的过程中,会不断地遍历两个节点,从而导致执行耗时。
同时,连接串不变的情况下,验证主备切换后是否能正常连接并执行数据库操作。通过dmmonitor工具,执行switchover进行节点切换,如图:
截图可见,目前DW02已经切换为主库。再次执行程序,报错如下:
可见,虽然主备已经发生切换,但是连接到的节点还是DW01,而此时该节点已经成为备库,只允许读,所以后续的插入操作都报错了。说明这种连接方式,程序只会去连接连接串配置的第一个IP:PORT。
var connectionString = "server=DMDW;svc_conf_path=C:\\windows\\system32\\dm_svc.conf;USER ID=SYSDBA;PWD=SYSDBA;connPooling=true;connPoolSize=110;";
运行代码,如下:
截图可见,程序能自己连接到当前的主节点DW02,且同样的100并发,耗时仅 3863 毫秒。同时,连接串不变的情况下,再次使用达梦的dmmonitor工具进行主备切换,如图:
再次运行测试代码,如图:
可见,即使主备发生切换,程序也能准确连接到主节点,从而实现高可用的需求。
通过服务名的方式连接达梦主备集群,能完全满足SqlSugar框架在对高可用的需求,在配置的连接串中除了要声明服务名的名称,还需要指定服务名配置文件dm_svc.conf的路径。
在实际项目中会遇到,SqlSugar框架下,数据库运行正常,连接访问正常的前提下,一旦开始并发操作,并发操作中的部分任务会失败,提示的错误原因内容如下:
实际的错误代码中,除了-6123的报错,还会有-6001的报错,提示的中文内容均是需要检查字符串是否正确。
检查数据库会话及负载,确认在断连的时候,数据库运行正常,负载正常,日志无异常,使用会话数远低于配置的最大值。
且在中断后几秒内,在未调整任何参数的情况下,程序又可以恢复正常的并发操作,因此基本可以判断问题并非是数据库造成的。
(1)开启并配置线程池,连接串修改为:
var connectionString = "server=IP;PORT=XXXX;user id=XXXX;PWD=XXXX;connPooling=true;connPoolSize=200;";
连接池中配置参数connPooling=true;connPoolSize=110。
重新运行代码:
可见,配置连接池后,该问题消失。需要说明的是,配置的连接池数量应该要略大于实际的并发数,否则也会报错。验证中,100并发需求下,若连接池配置也为100,也可能会出现断联的情况。
(2)优化代码逻辑,确保每个并发都独立创建并使用创建的连接。示例:
说明:修改后,每个并发任务均会独立创建并管理数据库连接,只要并发数不超过数据库配置的最大连接数(MAX_SESSIONS)就是可用的。
(1)高并发情况下,需要配置连接池,并且调整合适的连接池数量。
(2)注意优化代码逻辑,让每个并发任务都独立的创建并使用对应的会话连接,避免出现因资源争抢造成断连情况。
(3)同时,也需同步重关注,框架使用的SqlSugarCore.DM版本版本是否过低,或者与现场的数据库自带版本不一致等情况。
文章
阅读量
获赞
