最近被sql server Alwayson高可用組和讀寫分離,弄得神魂顛倒,身心俱疲。遇到了下面一些問題,提醒自己也給后來人做些記錄。
EntityFramework支不支持Alwayson?
起因:
因為要進行數據庫的優化,所以想在現有的sql server基礎上采用微軟的Alwayson解決方案,實現讀寫分離把數據庫的壓力減小一下。
之前兩篇文章關於Alwayson的都是建立在直接使用Ado.net的基礎上,因為EF也是基於Ado.net的orm所以,我認為也是支持Alwayson的。
寫一個測試程序吧:
、
代碼很簡單,我就省略了注釋,數據庫有三個字段如下:
USE [test] GO /****** Object: Table [dbo].[test1] Script Date: 2016/05/23 10:43:41 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[test1]( [id] [int] IDENTITY(1,1) NOT NULL, [name] [datetime] NULL, [test_id] [int] NULL, CONSTRAINT [PK_test1] 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] GO
private void button1_Click(object sender, EventArgs e) { using (testEntities dbcontext = new testEntities()) { try { var list = (from info in dbcontext.test1 select info).ToList(); dataGridView1.DataSource = list; } catch (Exception ex) { //do nothing } } } private void button2_Click(object sender, EventArgs e) { var count = Convert.ToInt32(textBox1.Text); using (testEntities dbcontext = new testEntities()) { for(var i = 1 ; i <= count;i++) { test1 t = new test1() { name = System.DateTime.Now, test_id = i }; dbcontext.test1.Add(t); } dbcontext.SaveChanges(); } } private void button3_Click(object sender, EventArgs e) { using (testEntities dbcontext = new testEntities()) { var item = dbcontext.test1.Where(p => p.id != 0); dbcontext.test1.RemoveRange(item); dbcontext.SaveChanges(); } }
環境簡介:
1.主副本:193.160.26.28
2.輔助副本:193.160.26.32
3.偵聽器:193.160.26.30
4.客戶端:10.167.218.27
DB First方式連接字符串:(怎么做可以自行查詢)
①不進行讀寫分離的時候
連接字符串如下:
<connectionStrings> <add name="testEntities" connectionString="metadata=res://*/test.csdl|res://*/test.ssdl|res://*/test.msl;provider=System.Data.SqlClient;provider connection string="data sou rce=tcp:193.160.26.30,1433;initial catalog=test;persist security info=True;user id=sa;password=123456;"" providerName="System.Data.EntityClient"/> </connectionStrings>
期待結果:可以讀,可以寫,並且都是在主副本上進行的。
實際結果:
上圖說明,不使用只讀的時候,和我們的期待結果一致。
2.采用只讀路由
修改連接字符串如下:
<connectionStrings> <add name="testEntities" connectionString="metadata=res://*/test.csdl|res://*/test.ssdl|res://*/test.msl;provider=System.Data.SqlClient;provider connection string="data source=tcp:193.160.26.30,1433;initial catalog=test;persist security info=True;user id=sa;password=123456;ApplicationIntent=ReadOnly;MultiSubnetFailover=True"" providerName="System.Data.EntityClient"/> </connectionStrings>
期待結果:能讀取(輔助副本),不能寫入(因為設定了只讀)
實際結果:讀取和寫入的時候出現了如下的錯誤:
如上圖所示:
在建立與服務器的連接時出錯。 在連接到 SQL Server 時,在默認的設置下 SQL Server 不允許遠程連接可能會導致此失敗。(訪問接口: TCP 訪問接口,錯誤: 0 - 無法識別這種主機)(.Net SqlClient 數據訪問接口)
為什么呢?
I.最先猜想到的是sql Client的問題,是不是不支持ReadOnly,查了一下msdn,上面解釋如下:
https://msdn.microsoft.com/zh-cn/library/system.data.sqlclient(v=vs.110).aspx 意思就是4.0以上版本是支持 ApplicationIntent 的值。可能的值為 ReadWrite 和 ReadOnly。
查看一下:目前使用的sql Client,是支持的。
II.不是sqlClinet的問題,又仔細看了一下錯誤的,host不能識別,考慮是不是DNS的問題,在客戶端使用cmd中的ping命令,ping一下偵聽器地址
在主副本或者輔助副本中ping一下:
感覺好像是DNS的問題:把程序拷貝到主副本或者輔助副本中執行:
結果:
讀過程:
讀過程沒出現問題。
寫過程:出現了只讀異常,貌似和我的期待結果一樣。
分析問題:
因為我的客戶端和兩天虛擬機從屬於不同的域賬戶,所以這可能就是問題的點。
解決問題:
怎么才能解決呢?
第一個方法:把客戶端加到和虛擬機同一個域當中,但是這樣做是很有風險的,不能保證每一個客戶端都和服務器在同一個域當中。所以我不能采用這樣的方式
第二個方法:仔細回覽了一下只讀路由配置的腳本, 下面是微軟官方提供的示例
ALTER AVAILABILITY GROUP [AG1]
MODIFY REPLICA ON
N'COMPUTER01' WITH
(SECONDARY_ROLE (ALLOW_CONNECTIONS = READ_ONLY));
ALTER AVAILABILITY GROUP [AG1]
MODIFY REPLICA ON
N'COMPUTER01' WITH
(SECONDARY_ROLE (READ_ONLY_ROUTING_URL = N'TCP://COMPUTER01.contoso.com:1433'));
在進行指定只讀路由的時候,發現他使用的是實例名字COMPUTER01.contoso.com,所以猜想可能和這個有關系,要使用IP地址估計問題就能解決
修改前一篇的只讀路由:http://www.cnblogs.com/dcz2015/p/5444438.html 如下:
①刪除只讀路由表:
ALTER AVAILABILITY GROUP [testAG] MODIFY REPLICA ON N'WIN-14VNU7CGQO1' WITH ( PRIMARY_ROLE (READ_ONLY_ROUTING_LIST=NONE) ); GO ALTER AVAILABILITY GROUP [testAG] MODIFY REPLICA ON N'WIN-14VNU7CGQO2' WITH ( PRIMARY_ROLE (READ_ONLY_ROUTING_LIST=NONE) ); GO
②添加只讀路由:
--①配置A副本的只讀路由屬性(ReadOnly代表‘只讀意向’) ALTER AVAILABILITY GROUP [testAG] MODIFY REPLICA ON N'WIN-14VNU7CGQO1' WITH (SECONDARY_ROLE (ALLOW_CONNECTIONS = READ_ONLY));
--②配置A副本的只讀路由URL ALTER AVAILABILITY GROUP [testAG] MODIFY REPLICA ON N'WIN-14VNU7CGQO1' WITH (SECONDARY_ROLE (READ_ONLY_ROUTING_URL = N'tcp://193.160.26.32:1433')); --③配置B副本的只讀路由屬性 ALTER AVAILABILITY GROUP [testAG] MODIFY REPLICA ON N'WIN-14VNU7CGQO2' WITH (SECONDARY_ROLE (ALLOW_CONNECTIONS = READ_ONLY));
--④配置B副本的只讀路由URL ALTER AVAILABILITY GROUP [testAG] MODIFY REPLICA ON N'WIN-14VNU7CGQO2' WITH (SECONDARY_ROLE (READ_ONLY_ROUTING_URL = N'tcp://193.160.26.28:1433')); --⑤配置A副本作為主副本時候的只讀路由表 ALTER AVAILABILITY GROUP [testAG] MODIFY REPLICA ON N'WIN-14VNU7CGQO1' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST=('WIN-14VNU7CGQO2','WIN-14VNU7CGQO1'))); --⑥配置B副本作為主副本時候的只讀路由表 ALTER AVAILABILITY GROUP [testAG] MODIFY REPLICA ON N'WIN-14VNU7CGQO2' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST=('WIN-14VNU7CGQO1','WIN-14VNU7CGQO2'))); GO
在客戶端進行測試:
讀過程:
寫過程: