windows NLB实现MSSQL读写分离--从数据库集群读负载均衡
主从模式,几乎大部分出名的数据库都支持的一种集群模式。
当Web站点的访问量上去之后,很多站点,选择读写分离,减轻主数据库的的压力。当然,一主多从也可以作用多个功能,比如备份。这里主要演示如何实现从数据库集群的读负载均衡
搭建一主三从的MSSQL集群
192.168.99.250 //主服务器 192.168.99.8 //从服务器(WIN-6S3JNU8C4TB) 192.168.99.10 //从服务器(WIN-HF1GQ5U288H) 192.168.99.11 //从服务器(WIN-EAPJ2QB5AGM)
本地发布
一般而言,我们只需要安装数据库引擎服务即可。复制服务需要另外安装,所以我们进行发布/订阅模式的话,需要安装复制组件
192.168.99.250 主服务器发布数据同步,接三台从服务器订阅发布。
本地订阅
同样本地订阅,也需要安装复制组件。
模拟写数据
using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; using System.Text; using System.Linq; namespace Write { class Program { static void Main(string[] args) { Console.WriteLine("开始写入模拟数据"); var ef = new EFDbContext(); var rand = new Random(); StringBuilder str1 = new StringBuilder(); str1.Append("赵、钱、孙、李、周、吴、郑、王、冯、陈、楮、卫、蒋、沈、韩、杨、朱、秦、尤、许、何、吕、施、张、孔、曹"); var familyNameItems = str1.ToString().Split("、"); StringBuilder str2 = new StringBuilder(); str2.Append("宏子 婷婷 蓉蓉 丽娜 娜 钰 天霞 官君 红梅 淑慧 海娟 洪山 "); str2.Append("盼丽 艳红 甜甜 璨 彬彬 银红 晨曦 婷 广荣 蓓 小艳 欣如 辅仁 嘉 雯婷 玉红 晨霞 "); str2.Append("涵 明 丽娜 青 茵 瑞微 逸群 思阳 臻 勇 志光 克涛 靖华 慧霞 卫伟 藜文 清华 莎 晓新 "); str2.Append("安安 荌荌 安卉 安娜 安妮 安然 傲冬 傲晴 傲雪 白雪 白云 碧螺 碧菡 碧玉 冰蓝 "); str2.Append("含烟 含玉 涵菡 晗蕾 涵韵 晗玥 寒凝 寒香 寒雁 和悌 和美 和怡 和雅 和璧 和玉 "); str2.Append("红螺 虹雨 虹彩 虹英 虹颖 虹影 怀玉 慧心 慧颖 慧雅 慕青 问兰 尔岚 元香 曼文"); var firstNameItems = str2.ToString().Split(" "); while (true) { string userName = "15" + rand.Next(100000000, 999999999).ToString(); ef.OAUser.Add(new OAUser { UserName = userName, PassWord = "123456", Salt = rand.Next(1111111, 6666666).ToString(), NickName = familyNameItems[rand.Next(0, familyNameItems.Count())] + firstNameItems[rand.Next(0, firstNameItems.Count())], IsFrozen = false, CreateTime = DateTime.Now }); ef.SaveChanges(); Console.WriteLine($"成功将用户{userName}资料写入"); } } } public class EFDbContext : DbContext { public EFDbContext() { base.ChangeTracker.AutoDetectChangesEnabled = false; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("Data Source=192.168.99.250;Initial Catalog=Test;User Id=sa;Password=123;", n => n.UseRowNumberForPaging()); } /// <summary> /// 用户表 /// </summary> public DbSet<OAUser> OAUser { set; get; } } /// <summary> /// 用户表 /// </summary> [Table("OAUser")] public class OAUser { /// <summary> /// 主健 /// </summary> [Key] public int Id { set; get; } /// <summary> /// 用户账号 /// </summary> [Required, MaxLength(15), MinLength(6)] public string UserName { set; get; } /// <summary> /// 用户密码 /// </summary> [Required, StringLength(64)] public string PassWord { set; get; } /// <summary> /// 昵称 /// </summary> [Required, StringLength(30)] public string NickName { get; set; } /// <summary> /// 加密盐 /// </summary> [Required, StringLength(20)] public string Salt { set; get; } /// <summary> /// 是否冻结 /// </summary> public bool IsFrozen { set; get; } /// <summary> /// 创建时间 /// </summary> public DateTime CreateTime { set; get; } } }
搭建NLB集群
192.168.99.120 //公共IP 192.168.99.8 192.168.99.10 192.168.99.11
模拟读取数据库
using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; using System.Text; using System.Linq; namespace Read { class Program { static void Main(string[] args) { Console.WriteLine("模拟大量读取"); var ef = new EFDbContext(); var i = 0; while (i < 1000) { i++; var user = ef.OAUser.OrderBy(n => Guid.NewGuid()).Take(1).FirstOrDefault(); Console.WriteLine($"读取用户{user.UserName}资料"); } Console.WriteLine("读取完毕"); } } public class EFDbContext : DbContext { public EFDbContext() { base.ChangeTracker.AutoDetectChangesEnabled = false; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("Data Source=192.168.99.120;Initial Catalog=oa;User Id=sa;Password=123;", n => n.UseRowNumberForPaging()); } /// <summary> /// 用户表 /// </summary> public DbSet<OAUser> OAUser { set; get; } } /// <summary> /// 用户表 /// </summary> [Table("OAUser")] public class OAUser { /// <summary> /// 主健 /// </summary> [Key] public int Id { set; get; } /// <summary> /// 用户账号 /// </summary> [Required, MaxLength(15), MinLength(6)] public string UserName { set; get; } /// <summary> /// 用户密码 /// </summary> [Required, StringLength(64)] public string PassWord { set; get; } /// <summary> /// 昵称 /// </summary> [Required, StringLength(30)] public string NickName { get; set; } /// <summary> /// 加密盐 /// </summary> [Required, StringLength(20)] public string Salt { set; get; } /// <summary> /// 是否冻结 /// </summary> public bool IsFrozen { set; get; } /// <summary> /// 创建时间 /// </summary> public DateTime CreateTime { set; get; } } }
经过多次测试发现,每次只在一台从服务器上面进行读取,纳闷
后来输出发现
Console.WriteLine(con.ClientConnectionId);
连接ID一样,重新启动,刚连接ID会有变动,然后果另一个从服务器进行大量读取。
初步猜测,应该是连接池的搞的鬼,如果关闭连接,但是实际上,并没有关闭数据库连接,而是归还连接池。重新打开数据库,又重连接池分配一条连接。
目前不清楚连接池的分配规则,从结果来看,应该是每次都是分配了相当的程序池连接。
现在暂进不测了,下班走,改天,部署WEB结构,然后用压力测试测试,看看数据库读取是否均衡。