SQL Server、MySQL主從搭建,EF Core讀寫分離代碼實現
一、SQL Server的主從複製搭建
1.1、SQL Server主從複製結構圖
SQL Server的主從通過發佈訂閱來實現
1.2、基於SQL Server2016實現主從
新建一個主庫「MyDB」
建一個表”SysUser”測試
CREATE TABLE [dbo].[SysUser](
[Id] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[UserName] [varchar](50) NOT NULL,
[Account] [varchar](20) NOT NULL,
[Password] [varchar](100) NOT NULL,
[Phone] [varchar](50) NOT NULL,
[CreateTime] [datetime] NOT NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
搭建發佈服務器
複製》配置分發
這裡創建一個自己的路徑,共享文件夾
分發數據庫
發佈服務器
然後下一步完成
啟用代理
服務確認一下登陸權限
到這裡發佈服務器就建好了。
發佈
發佈就是把主庫的數據或操作發佈到發佈服務器
現在主庫里錄入了兩條數據
新建發佈
選擇發佈的數據庫
發佈類型
這裡有幾種不同發佈方式,根據自己業務場景選擇,互聯網一般是事務發佈,有操作就同步。
選擇同步的表
一直下一步到這裡,勾選初始化訂閱
代理安全性
下一步
發佈名稱
完成
這時候在上面設的發佈服務器的共享文件夾中能看到有發佈文件了
創建訂閱
新建一個從庫「MyDb_Copy」,為一個沒創建表的空庫
新建訂閱
選擇訂閱的發佈
。
選擇推送方式(發佈服務器主動推送),還是拉取方式(從庫服務器拉取方式),一個從庫選推送,多個從庫選擇拉取方式
選擇訂閱數據庫
分發代理安全性
一直下一步,直到完成!
驗證
看從庫數據同步過來了
主庫增加一條數據
從庫看到也同步了
到這裡SQL Server2016的主從複製就完成了!
二、MySQL的主從複製搭建
2.1、MySQL主從複製結構圖
主庫把增刪查改的操作寫入到binlog日誌。
從庫開啟兩個線程,一個IO線程,負責讀取binlog日誌到relay日誌。一個SQL線程從relay日誌讀取數據寫入從庫DB
2.2、基於Docker搭建MySQL的主從
拉取鏡像
docker pull mysql:5.7
準備兩個文件,主庫mysqld.cnf,上傳到目錄 /home/mysql/master
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
#log-error = /var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address = 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
log-bin=mysql-bin
#id不要重複
server-id=11
從庫mysald.cnf,上傳到目錄 /home/mysql/slave
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
#log-error = /var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address = 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
#id不重複
server-id=22
#從庫不需要事務,改MyISAM快些
default-storage-engine=MyISAM
創建主庫容器
docker run --name mysql-master -p 3307:3306 -v /home/mysql/master:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
創建從庫容器
docker run --name mysql-slave -p 3308:3306 -v /home/mysql/slave:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
用連接工具連接上數據庫,這裡用DBeaver
配置主服務
首先,進入容器:
[root@localhost ~]# docker exec -it mysql-master /bin/bash
bash-4.2#
鏈接MySQL
bash-4.2# mysql -u root -p123456
mysql>
修改 root 可以通過任何客戶端連接
mysql> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
Query OK, 0 rows affected (0.00 sec)
mysql>
重啟Master服務器
mysql> exit
Bye
bash-4.2# exit
exit
[root@localhost ~]# docker restart mysql-master
mysql-master
[root@localhost ~]#
再次進入master容器
docker exec -it mysql-master /bin/bash
連接 MySQL
mysql -u root -p123456
查看數據庫狀態:
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000005 | 154 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
mysql>
把File的值「mysql-bin.000005」和 Position的值154記錄下來
配置從服務器
首先,進入容器:
docker exec -it mysql-slave1 /bin/bash
連接 MySQL
mysql -u root -p123456
修改 root 可以通過任何客戶端連接(默認root用戶可以對從數據庫進行編輯的)
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
配置從同步主服務數據,執行如下SQL
change master to
master_host='192.168.101.20',
master_user='root',
master_log_file='mysql-bin.000005',
master_log_pos=154,
master_port=3307,
master_password='123456';
- master_log_file=’mysql-bin.000005′ 上面主庫記錄下來的值
- master_log_pos=154 上面主庫記錄下來的值
啟動slave服務
mysql>start slave;
查看slave狀態
show slave status \G;
驗證主從庫搭建結果
主庫創建數據庫
刷新從庫,也把數據庫同步過來了
主庫創建一張表
CREATE TABLE MyDB.sys_user (
id int auto_increment NOT NULL,
user_name varchar(150) NOT NULL,
account varchar(20) NOT NULL,
password varchar(100) NOT NULL,
phone varchar(50) NOT NULL,
create_time DATETIME NOT NULL,
CONSTRAINT sys_user_PK PRIMARY KEY (id)
)
ENGINE=InnoDB
DEFAULT CHARSET=latin1
COLLATE=latin1_swedish_ci
AUTO_INCREMENT=1;
從庫也同步了
主庫插入數據,從庫也能同步。
到這裡,MySQL的主從搭建就完成了!
三、EF Core代碼讀寫分離實現
這裡用.NET6 +EF Core6.0 +SQLServer演示。
建一個.NET6的web程序
安裝NuGet包
Microsoft.EntityFrameworkCore(6.0.7)
Microsoft.EntityFrameworkCore.SqlServer(6.0.7)
appsetting.json增加 ConnectinStrings節點
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"WriteConnection": "Data Source=.;Database=MyDB;User ID=sa;Password=123456",
"ReadConnection": "Data Source=.;Database=MyDB_Copy;User ID=sa;Password=123456"
}
}
增加一個類DBConnectionOption.cs來接收連接配置
public class DBConnectionOption
{
public string WriteConnection { get; set; }
public string ReadConnection { get; set; }
}
增加一個類SysUser.cs來對應數據庫表SysUser實體
public class SysUser
{
public int Id { get; set; }
public string UserName { get; set; }
public string Account { get; set; }
public string Password { get; set; }
public string Phone { get; set; }
public DateTime CreateTime { get; set; }
}
增加一個類MyDBContext.cs來訪問數庫上下文
public class MyDBContext : DbContext
{
private DBConnectionOption _readWriteOption;
public MyDBContext(IOptionsMonitor<DBConnectionOption> options)
{
_readWriteOption = options.CurrentValue;
}
public DbContext ReadWrite()
{
//把鏈接字符串設為讀寫(主庫)
this.Database.GetDbConnection().ConnectionString = this._readWriteOption.WriteConnection;
return this;
}
public DbContext Read()
{
//把鏈接字符串設為之讀(從庫)
this.Database.GetDbConnection().ConnectionString = this._readWriteOption.ReadConnection;
return this;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(this._readWriteOption.WriteConnection); //默認主庫
}
public DbSet<SysUser> SysUser { get; set; }
}
增加一個類DbContextExtend.cs來擴展上下文修改連接字符串
/// <summary>
/// 拓展方法
/// </summary>
public static class DbContextExtend
{
/// <summary>
/// 只讀
/// </summary>
/// <param name="dbContext"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static DbContext Read(this DbContext dbContext)
{
if (dbContext is MyDBContext)
{
return ((MyDBContext)dbContext).Read();
}
else
throw new Exception();
}
/// <summary>
/// 讀寫
/// </summary>
/// <param name="dbContext"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static DbContext ReadWrite(this DbContext dbContext)
{
if (dbContext is MyDBContext)
{
return ((MyDBContext)dbContext).ReadWrite();
}
else
throw new Exception();
}
}
修改Program.cs,增加
builder.Services.Configure<DBConnectionOption>(builder.Configuration.GetSection("ConnectionStrings"));//注入多個鏈接
builder.Services.AddTransient<DbContext, MyDBContext>();
驗證
在HomeController的Index方法里實現讀寫分離操作
public IActionResult Index()
{
//新增-------------------
SysUser user = new SysUser()
{
UserName="李二狗",
Account="liergou",
Password=Guid.NewGuid().ToString(),
Phone="13345435554",
CreateTime=DateTime.Now
};
Console.WriteLine($"新增,當前鏈接字符串為:{_dbContext.Database.GetDbConnection().ConnectionString}");
_dbContext.ReadWrite().Add(user);
_dbContext.SaveChanges();
//只讀--------------------------------
var dbContext = _dbContext.Read();
var users= _dbContext.Read().Set<SysUser>().ToList();
Console.WriteLine($"讀取SysUser,數量為:{users.Count},當前鏈接字符串為:{_dbContext.Database.GetDbConnection().ConnectionString}");
return View();
}
執行結果:
查看數據庫,新增的數據也查入成功了。
這裡讀程序讀寫分離也完成了!
有沒有細心的朋友發現讀的時候日誌只顯示讀到了3條記錄,而上面一共有4條記錄。
原因是主從同步會有延遲,從庫沒那麼快同步到數據,一般都有個0.幾到1秒的延遲,這個可以調優,這裡就不說多內容了,有興趣的可以去查資料操作一下。
到這裡全部就完成了!
源碼地址://github.com/weixiaolong325/EFCoreReadWriteSeparate