SQL Server的主從通過釋出訂閱來實現
主庫把增刪改操作釋出到釋出伺服器,從庫通過訂閱釋出伺服器,釋出伺服器把操作推播到從庫進行同步。
新建一個主庫「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的主從複製就完成了!
主庫把增刪查改的操作寫入到binlog紀錄檔。
從庫開啟兩個執行緒,一個IO執行緒,負責讀取binlog紀錄檔到relay紀錄檔。一個SQL執行緒從relay紀錄檔讀取資料寫入從庫DB
拉取映象
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';
啟動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的主從搭建就完成了!
這裡用.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秒的延遲,這個可以調優,這裡就不說多內容了,有興趣的可以去查資料操作一下。
到這裡全部就完成了!
原始碼地址:https://github.com/weixiaolong325/EFCoreReadWriteSeparate