天天看點

CYQ.Data V5 分布式自動化緩存設計介紹

前方:

其實完成這個功能之前,我就在思考:是先把想法寫了來,和大夥讨論讨論後再實作,還是實作後再寫文論述自己的思維。

忽然腦後傳來一個聲音說:你發文後會進入發呆階段。

是以還是靜下心,讓我輕輕地把代碼撸完再說。

最近這幾天,自己在大腦裡演練過各種技術難點,解決方案,推敲了各種該解決的問題,覺的差不多了,才決定撸碼。

忽然發覺,原來代碼是可以寫在大腦裡的。

要是你看到一個員工坐着2天沒寫一行代碼,說明人家是高手,正在大腦程式設計。

好,不扯,回正文!

傳統orm的二級緩存為何失效?

有些orm會提供:如hibernate。

有些不提供:如ef,不提供是因為知道提供了也沒啥鳥用,因為:

1:你不能強迫一個項目全部用單實體程式設計,多表時,使用者更偏向于執行sql語句。

2:沒有分布式緩存做為基礎,解決不了多應用程式部署的緩存政策問題。

是以:

1:若控制不了整個項目使用者的sql語句,單機的搞不了。

2:沒有分布式緩存做基礎,分布式的都搞不了。

這也是為啥ef一直不提供,是因為看到hibernate雖然提供但并沒多大卵用的原因吧!

疑惑資料庫已有緩存,為何架構還要造孽?

主要原因:

1:資料庫從請求到建立緩存,需要時間(架構緩存可以減緩資料庫緩存失效時壓力)

2:資料庫是有連結數限制的,不可能允許大量并發的直連,需要外界分壓。

3:資料庫的緩存是單機性。

4:資料庫發資料往伺服器的時間比本機緩存的長。

自動緩存設計前的一些思考:

1:一開始我思考的緩存政策,是細化到行或列,于是了解資料庫自身緩存後發現資料庫目前也隻是做了以表為機關。

2:mssql是有提供sqldependency的緩存依賴項的,它可以從資料庫層面通知你的資料何時失效。

3:但是sqldependency和sqlcommand依賴太深,無法在所有資料庫層面通用。

4:sqldependency的緩存依賴隻能在本地緩存。

5:其它資料庫不支援依賴通知。

6:是以方案隻能通過全局執行與分析,來處理緩存及失效政策。

7:單機時:全局攔截分析,如何分析出表?

8:應用分布式時:緩存及時失效?

9:使用者直接修改資料庫時:緩存如何失效?

還有好多好多問題,一直在思考......

緩存什麼?

1:緩存單個對象時,是直接存檔對象的,cache傳回時會根據本地或是遠端選擇是否clone傳回。。

2:緩存清單時:隻存檔字段類型僅包含:(數字、布爾、字元、時間、guid)的字段,并轉成json字元串存檔。

技術細節:

  a:一個對象存檔在本機時,存檔的是引用(可能出現誤寫操作);存檔在分布式時,存檔的不是引用,這會在使用時出現不确定性。

  b:大對象的存檔,在緩存來去間需要序列化和反序列化,性能上降低很多。

是以:将清單轉成json存檔,拿到時再還原,可以同時解決a和b的問題。

簡單的說,如果對象有長字段,或者有二進制資料,是不會被緩存的,是以mssql的timestamp字段就不要用了;

如果要用:appconfig.db.hiddenfields="字段名",把它隐藏了也行。

緩存時間?

考慮到通常通路量低時都是在中午和晚上的時間,是以,将緩存的對象的時間随機分布(早上的分布在中午失效,下午的分布在晚上失效)

考慮到分頁時的查詢,通常都關注前面幾頁,是以前面幾頁的資料,時間如上的時間段分布。

分頁後面的資料,隻預設2分鐘的緩存時間。

其它規則有待讨論......

緩存多大?

1:在單機狀态,檢測到記憶體的可用比例低于15%時,則不再接受緩存。

2:在分布式緩存狀态,暫時有多少扔多少。

緩存如何失效:

1:攔截請求:(包括(maction)增删改查+(mproc)執行自定義語句+(mdatatable)批量方法)

2:分析語句的關聯表(單表的可以拿表名,視圖的拿關聯結構涉及的表名,存儲過程(目前沒法),自定義sql(語句分析出表名),批量(直接拿表名)

3:技術難點:如何從未知的sql或視圖中準确的分析出所有關聯的表。

4:緩存失效:執行以下方法應該失效:增删改,執行exenonquery,批量語句。

5:技術難點:

  1:對于視圖(關聯了多個表,如何根據一個表名,關聯到相應涉及的視圖語句失效?)

  2:對于分布式的應用,a服務更新,如何b伺服器也失效。

如何處理修改頻繁的表:

1:一開始想增加配置,讓使用者設定不參與緩存的表,認真思考後,發現根據緩存失效的時間和次數,可自動分析判斷一個表是否修改頻繁。

2:表操作相關增删查時,該表被置為失效(相關緩存會被移除),此時設定好時間間隔(6秒),在此時間段對該表相關的不緩存,同時送出的緩存删指令也可以無視。

3:對被分析出為修改頻繁的表該如何處理?延長相應的不緩存時間,或是??還需要思考!!!

緩存失效的粒度能不能小?

1:目前的失效,和資料庫一樣,是以表為機關的。

2:對于插入操作,不會影響某一條資料的讀取(是以單條資料的查詢,是不應該受到插入操作的影響的)

3:還有其它情形是必然不會影響的?

架構有自動緩存,業務需不需要緩存?

1:資料庫有自動緩存,架構的也可以自動緩存。

2:架構有自動緩存,同理業務也可以有緩存。

架構能處理的粒度是有限的,不能細到具體的行或列的緩存級别,是以在業務複雜和并發到一定量後,業務緩存是必要的。

資料庫有緩存,業務也可做緩存,為何還思考往架構增加自動緩存?

1:資料庫的預設緩存是固定的,需要配置,各種資料庫環境不一緻。

2:資料庫連結池預設是固定的。

3:業務加緩存的事,往往是後期的動作。

現另一個現狀是:

1:.net 群體,存在很多國中級的開發人員,這部分人員的技術成長相對較慢,對緩存或性能調優并不熟。

2:國内有很多的中小網站,預設都抗不起并發,攻擊成本很小,幾百上千個并發就可以挂你站了。

是以,既然有現實的問題,就可以有對應的解決方案。

v5架構此功能的出現,就是為了從基礎層面統一解決這些問題。

隻有當.net行業不在有慢網站的存在,整體提升檔次了,有良好的口碑,才會引進更多的boss選用 .net,大夥所期待的.net春天也就近了。

v5的目前解決的問題:

總體而言,要實作這個功能,核心要解決以下問題:

下面我來将技術一點一點出賣:

5:aop攔截問題:

首先,要實作這功能,就得全局攔截,掃蕩過源碼或用過v5的同學,聽說過架構本身就有aop的吧;

其次,得改造這個aop:架構預設有一個空aop,當外部有aop裝載的時候,會替換掉這個空aop。

要實作這個自動緩存:本想在空aop裡實作,放着浪費,但若使用者自定義的aop被裝載,又會被替換掉,走不通...

方案想了三四個,思考了三四夜,最後還是在撸碼時才确定了現在的模式(這個告訴我們,想的差不多了就該撸碼了,要100%想通再撸不太靠譜):

于是,我這樣做了:

原有的aop,改名成interaop,不過是挂名的,因為它沒有繼承iaop接口,而且從原本的單例變更成多例模式。

這裡可以貼兩行代碼,意思是:在bengin和end方法調用了外部aop的接口,并根據外部aop的狀态決定後續的執行流程:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public aopresult begin(aopenum action)

代碼最後很少,但沒想出來之前,2天都搞不定。

1:基礎單表、視圖操作

a:單表,這個是最簡單的,傳遞進來的就是表名;

b:視圖,這個麻煩一點,傳遞的是視圖名;

于是,如何從視圖擷取相關參與的表名?你現在應該不知道,我來告訴你吧:

dbdatareader sdr=....

datatable dt = sdr.getschematable();

這條語句,可以通殺所有的資料庫,不用去n種資料庫裡搜各種中繼資料藏在哪了!!!

2:多表sql語句操作:

對于sql語句,可以用上面的方法,執行一個datareader再拿,但我弄了一個簡單的方法來找關聯表:

41

internal static list gettablenamesfromsql(string sql)

有可能會找多,找到後,再過濾一下名稱是不是資料庫裡的表就可以了。

3:直接操作資料庫

一開始設定的思維,是動态建立一個表,字段大概是這樣的:

表名 更新時間

然後如果手工操作資料庫,可以手工更改時間,也可以用觸發器引發這裡的更新。

然後背景線程定時掃這個表,就知道有沒有表被更新了。

不過--------v5目前并木有實作它,隻是開放了一個接口,可以讓你在代碼裡調用移除緩存。

這個方法就是:

public abstract partial class cachemanage

{

}

4:跨伺服器操作

這個本來是簡單的,後來又想麻煩了,因為要兼顧性能問題,緩存移除可能會頻繁的問題。

後來,通過增加了緩存類型,來識别本地緩存或分布式緩存,來差別寫代碼:

private static void setbasekey(string basekey, string key)

6:緩存失效問題  

這個問題,流程本來很簡單的:

但思考到cache多,而且分布式時,傳回會卡,是以删除cache操作就變成線程處理了。

後來為了避免線程多開,又把類改成了單例(一開始是多執行個體的)

現在,又把這線程的線程開啟,放到localcache裡和另一個線程作伴了,然後這個單例類又變更成了靜态類。

v5架構怎麼使用這功能:

更新版本到最新版即可!

總結:

1:沒有這個功能之前:架構解決了三大問題:程式設計架構的統一(自動化)、資料庫壓力(讀寫分離)、伺服器壓力(分布式緩存)。

2:此功能的存在:是針對從基礎層面提升行業項目的整體水準。

3:最近大腦有點發春,一個個創新idea不斷的從我大腦冒出來,折騰的我好累:

要思考架構、落實架構代碼、要寫文分享,寫架構demo、群裡解答。

4:開源不賺錢,又投入這麼多精力,隻能把它當理想了,希望它有天成為.net項目的标配資料層。

5:我部落格是有打贊插件的,哈。