?
?
《Replication的犄角旮旯》系列導(dǎo)讀
Replication的犄角旮旯(一)--變更訂閱端表名的應(yīng)用場(chǎng)景
Replication的犄角旮旯(二)--尋找訂閱端丟失的記錄
Replication的犄角旮旯(三)--聊聊@bitmap
Replication的犄角旮旯(四)--關(guān)于事務(wù)復(fù)制的監(jiān)控
Replication的犄角旮旯(五)--關(guān)于復(fù)制identity列
Replication的犄角旮旯(六)-- 一個(gè)DDL引發(fā)的血案(上)(如何近似估算DDL操作進(jìn)度)
Replication的犄角旮旯(七)-- 一個(gè)DDL引發(fā)的血案(下)(聊聊logreader的延遲)
Replication的犄角旮旯(八)-- 訂閱與發(fā)布異構(gòu)的問(wèn)題
Replication的犄角旮旯(九)-- sp_setsubscriptionxactseqno,賦予訂閱活力的工具
---------------------------------------華麗麗的分割線--------------------------------------------
?
關(guān)于replication中的bitmap,貌似介紹的文檔不多;本文將從對(duì)此參數(shù)做一初步的簡(jiǎn)析,并介紹如何利用這個(gè)參數(shù)處理一些特定環(huán)境下的問(wèn)題;
再次強(qiáng)調(diào), 本方法雖多次經(jīng)受驗(yàn)證無(wú)誤,但多次被MS supporter們建議不要嘗試使用此方法,還望各位DBA三思!
先來(lái)看看@bitmap在哪里出現(xiàn)
我們先創(chuàng)建一個(gè)表的復(fù)制訂閱,表結(jié)構(gòu)如下
1
USE
[
test_aaa
]
2
GO
3
4
/*
***** Object: Table [dbo].[test_b] Script Date: 2014/1/23 16:12:28 *****
*/
5
SET
ANSI_NULLS
ON
6
GO
7
8
SET
QUOTED_IDENTIFIER
ON
9
GO
10
11
SET
ANSI_PADDING
ON
12
GO
13
14
CREATE
TABLE
[
dbo
]
.
[
test_b
]
(
15
[
id1
]
[
int
]
NOT
NULL
,
16
[
id2
]
[
int
]
NOT
NULL
,
17
[
id3
]
[
int
]
NOT
NULL
,
18
[
id4
]
[
int
]
NOT
NULL
,
19
[
name
]
[
varchar
]
(
10
)
NULL
,
20
[
remark1
]
[
varchar
]
(
100
)
NULL
,
21
[
remark2
]
[
varchar
]
(
100
)
NULL
,
22
[
remark3
]
[
varchar
]
(
100
)
NULL
,
23
[
remark4
]
[
varchar
]
(
100
)
NULL
,
24
CONSTRAINT
[
pk_id1_id2_id3_id4
]
PRIMARY
KEY
CLUSTERED
25
(
26
[
id1
]
ASC
,
27
[
id2
]
ASC
,
28
[
id3
]
ASC
,
29
[
id4
]
ASC
30
)
WITH
(PAD_INDEX
=
OFF
, STATISTICS_NORECOMPUTE
=
OFF
, IGNORE_DUP_KEY
=
OFF
, ALLOW_ROW_LOCKS
=
ON
, ALLOW_PAGE_LOCKS
=
ON
)
ON
[
PRIMARY
]
31
)
ON
[
PRIMARY
]
32
33
GO
34
35
SET
ANSI_PADDING
OFF
36
GO
到訂閱庫(kù)的存儲(chǔ)過(guò)程中,找到sp_MSupd_dbotest_b,生成腳本
1
USE
[
test_byxl1
]
2
GO
3
/*
***** Object: StoredProcedure [dbo].[sp_MSupd_dbotest_b] Script Date: 2014/1/23 14:28:46 *****
*/
4
SET
ANSI_NULLS
ON
5
GO
6
SET
QUOTED_IDENTIFIER
ON
7
GO
8
ALTER
procedure
[
dbo
]
.
[
sp_MSupd_dbotest_b
]
9
@c1
int
=
NULL
,
10
@c2
int
=
NULL
,
11
@c3
int
=
NULL
,
12
@c4
int
=
NULL
,
13
@c5
varchar
(
10
)
=
NULL
,
14
@c6
varchar
(
100
)
=
NULL
,
15
@c7
varchar
(
100
)
=
NULL
,
16
@c8
varchar
(
100
)
=
NULL
,
17
@c9
varchar
(
100
)
=
NULL
,
18
@pkc1
int
=
NULL
,
19
@pkc2
int
=
NULL
,
20
@pkc3
int
=
NULL
,
21
@pkc4
int
=
NULL
,
22
@bitmap
binary
(
2
)
23
as
24
begin
25
if
(
substring
(
@bitmap
,
1
,
1
)
&
1
=
1
)
or
26
(
substring
(
@bitmap
,
1
,
1
)
&
2
=
2
)
or
27
(
substring
(
@bitmap
,
1
,
1
)
&
4
=
4
)
or
28
(
substring
(
@bitmap
,
1
,
1
)
&
8
=
8
)
29
begin
30
update
[
dbo
]
.
[
test_b
]
31
set
[
id1
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
1
when
1
then
@c1
else
[
id1
]
end
,
32
[
id2
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
2
when
2
then
@c2
else
[
id2
]
end
,
33
[
id3
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
4
when
4
then
@c3
else
[
id3
]
end
,
34
[
id4
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
8
when
8
then
@c4
else
[
id4
]
end
,
35
[
name
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
16
when
16
then
@c5
else
[
name
]
end
,
36
[
remark1
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
32
when
32
then
@c6
else
[
remark1
]
end
,
37
[
remark2
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
64
when
64
then
@c7
else
[
remark2
]
end
,
38
[
remark3
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
128
when
128
then
@c8
else
[
remark3
]
end
,
39
[
remark4
]
=
case
substring
(
@bitmap
,
2
,
1
)
&
1
when
1
then
@c9
else
[
remark4
]
end
40
where
[
id1
]
=
@pkc1
and
[
id2
]
=
@pkc2
and
[
id3
]
=
@pkc3
and
[
id4
]
=
@pkc4
41
if
@@rowcount
=
0
42
if
@@microsoftversion
>
0x07320000
43
exec
sp_MSreplraiserror
20598
44
end
45
else
46
begin
47
update
[
dbo
]
.
[
test_b
]
48
set
[
name
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
16
when
16
then
@c5
else
[
name
]
end
,
49
[
remark1
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
32
when
32
then
@c6
else
[
remark1
]
end
,
50
[
remark2
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
64
when
64
then
@c7
else
[
remark2
]
end
,
51
[
remark3
]
=
case
substring
(
@bitmap
,
1
,
1
)
&
128
when
128
then
@c8
else
[
remark3
]
end
,
52
[
remark4
]
=
case
substring
(
@bitmap
,
2
,
1
)
&
1
when
1
then
@c9
else
[
remark4
]
end
53
where
[
id1
]
=
@pkc1
and
[
id2
]
=
@pkc2
and
[
id3
]
=
@pkc3
and
[
id4
]
=
@pkc4
54
if
@@rowcount
=
0
55
if
@@microsoftversion
>
0x07320000
56
exec
sp_MSreplraiserror
20598
57
end
58
end
看到這么多@bitmap,是不是有種升仙的感覺(jué)?
@bitmap 是binary類(lèi)型,即二進(jìn)制串;簡(jiǎn)單來(lái)說(shuō),它是用來(lái)表示所操作的字段位置的參數(shù),通過(guò)@bitmap,分發(fā)代理從distribution.dbo.msrepl_commands中讀取命令時(shí)(update操作),才會(huì)知道哪些列進(jìn)行了更新;
我們先來(lái)解析一下這個(gè)存儲(chǔ)過(guò)程;
1、根據(jù)表結(jié)構(gòu)的code,我們知道這個(gè)表共有9個(gè)字段,其中id1~id4被定義為聯(lián)合主鍵;
???? 由于binary(1)表示1個(gè)字節(jié)(8位的2進(jìn)制),因此我們表示9個(gè)字段的@bitmap就只能用binary(2)來(lái)容納了;
其次,有的童鞋說(shuō),他們看到的update存儲(chǔ)過(guò)程只有一個(gè)程序段,而我的例子中有兩部分(29行~44行、46行~57行)。這個(gè)是由于存在聯(lián)合主鍵造成的;即當(dāng)被訂閱的表中含有聯(lián)合主鍵(2個(gè)或以上的字段一同作為主鍵)的時(shí)候才會(huì)出現(xiàn)兩段代碼,前者是更新主鍵列,后者則是更新非主鍵列;
2、根據(jù)更新列的位置不同,@bitmap中的對(duì)應(yīng)的值也不同;
? substring(@bitmap,1,1) & 1 = 1 表示第一列有更新;
? substring(@bitmap,1,1) & 2 = 2 表示第二列有更新;
? substring(@bitmap,1,1) & 4 = 4 表示第三列有更新;
???? 以此類(lèi)推
? substring(@bitmap,1,1) & 128 = 128 表示第八列有更新;
???? 那第九位呢?? =256么?? 由于1個(gè)字節(jié)只有8位,而128=2^7,當(dāng)?shù)诰盼怀霈F(xiàn)時(shí)就要進(jìn)位了
? substring(@bitmap,2,1) & 1 = 1
???? 怎么樣,不難理解吧?
定義4個(gè)字段的聯(lián)合主鍵只是為了舉例說(shuō)明的時(shí)候方便一些,實(shí)際的生產(chǎn)環(huán)境中可能不太經(jīng)常能遇到;
?
再來(lái)看一下@bitmap在哪里可以獲取到呢?我先更新一條記錄,更新之前先關(guān)閉相應(yīng)的分發(fā)代理(此處不需要分發(fā)命令應(yīng)用到訂閱端)
我們?nèi)istribution里看看具體的分發(fā)命令(具體做法請(qǐng)見(jiàn)《 Replication的犄角旮旯(二)--尋找訂閱端丟失的記錄 》)
從命令中我們可以看出,更新的列位置為第5(name)、7(remark2)、8(remark3)列,按照二進(jìn)制的表示方法為(注意反取,即←表示第1至第9位)
0 1101 0000
由于第九位沒(méi)有更新,因此為0,所以bitmap就是 1101 0000,換成十六進(jìn)制就是0xD0,由于bitmap超出1個(gè)字節(jié),因此后面再補(bǔ)0,就是我們看到的0xd000了
可能看到這里,有些童鞋會(huì)說(shuō),這太麻煩了,遇到一個(gè)很寬的表,光數(shù)逗號(hào)就數(shù)死了……盡管我們明白逗號(hào)是為了分割字段的,但系統(tǒng)為什么會(huì)這樣生成呢,這個(gè)問(wèn)題可以參考: http://msdn.microsoft.com/zh-cn/library/ms152489%28v=sql.120%29.aspx
這個(gè)和article的屬性,調(diào)用訂閱端存儲(chǔ)過(guò)程方式有關(guān)(SCALL),這個(gè)不是本文的重點(diǎn),在此不做贅述;
?
根據(jù)上面的算法,我們就可以知道,當(dāng)我們要更新一個(gè)表時(shí),可以根據(jù)更新列的位置,推算出實(shí)際的bitmap值,但這又有什么用呢?
?
-------------------------------------我是華麗麗的分割線-------------------------------------
?
應(yīng)用場(chǎng)景:一個(gè)表(還是說(shuō)商品表吧,比較典型),保存商品信息、簡(jiǎn)介等內(nèi)容,都是varchar(max)或text類(lèi)型;商家在促銷(xiāo)活動(dòng)前通常會(huì)大批量的更新這些內(nèi)容(比如加個(gè)促銷(xiāo)活動(dòng)介紹等等),更新大字段是復(fù)制環(huán)境中最頭疼的問(wèn)題;
由于一條復(fù)制命令有長(zhǎng)度限制(1K左右),如果一條更新記錄中的更新內(nèi)容過(guò)大,就會(huì)被拆分成多條命令寫(xiě)到msrepl_commands中(我曾經(jīng)碰到過(guò)1條記錄的更新操作被拆成100個(gè)復(fù)制命令),如果高峰時(shí)期有用戶大量的進(jìn)行這種操作,那作為DBA就可以升級(jí)為“張三瘋”了;
這時(shí)候我們就可以根據(jù)具體的update命令(一般除了更新必要的字段外,還會(huì)捎帶更新updatetime這樣的時(shí)間戳,具體就去找研發(fā)兄弟們要吧),計(jì)算出相應(yīng)的bitmap,然后在相應(yīng)的存儲(chǔ)過(guò)程中加個(gè)判斷,屏蔽掉這樣的操作;
具體操作就是,在sp_MSupd_dbotest_b這里的第24、25行之間加上
if @bitmap=0xd000
return;
再打開(kāi)分發(fā)代理,這樣就屏蔽了@bitmap=0xd000的全部操作;
按照前面的操作,并沒(méi)有進(jìn)行下面的更新
而我在此之后又從發(fā)布服務(wù)器insert了一條主鍵為2,2,2,2的記錄,以證明該操作在update之后已經(jīng)傳到了訂閱端,而update被屏蔽了;
需要注意的是,由于按位操作是絕對(duì)嚴(yán)格,對(duì)于只更新column1和同時(shí)更新column1、column2將產(chǎn)生不同的bitmap,操作時(shí)一定要謹(jǐn)慎;
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

