資料庫並發設計
大數據量高並發訪問資料庫結構的設計
如果不能設計一個合理的資料庫模型,不僅會增加客戶端和伺服器段程序的編程和維護的難度,而且將會影響系統實際運行的性能。所以,在一個系統開始實施之前,完備的資料庫模型的設計是必須的。
在一個系統分析、設計階段,因為數據量較小,負荷較低。我們往往只注意到功能的實現,而很難注意到性能的薄弱之處,等到系統投入實際運行一段時間後,才發現系統的性能在降低,這時再來考慮提高系統性能則要花費更多的人力物力,而整個系統也不可避免的形成了一個打補丁工程。
所以在考慮整個系統的流程的時候,我們必須要考慮,在高並發大數據量的訪問情況下,我們的系統會不會出現極端的情況。(例如:對外統計系統在7月16日出現的數據異常的情況,並發大數據量的的訪問造成,資料庫的響應時間不能跟上數據刷新的速度造成。具體情況是:在日期臨界時(00:00:00),判斷資料庫中是否有當前日期的記錄,沒有則插入一條當前日期的記錄。在低並發訪問的情況下,不會發生問題,但是當日期臨界時的訪問量相當大的時候,在做這一判斷的時候,會出現多次條件成立,則資料庫里會被插入多條當前日期的記錄,從而造成數據錯誤。),資料庫的模型確定下來之後,我們有必要做一個系統內數據流向圖,分析可能出現的瓶頸。
為了保證資料庫的一致性和完整性,在邏輯設計的時候往往會設計過多的表間關聯,盡可能的降低數據的冗餘。(例如用戶表的地區,我們可以把地區另外存放到一個地區表中)如果數據冗餘低,數據的完整性容易得到保證,提高了數據吞吐速度,保證了數據的完整性,清楚地表達數據元素之間的關系。而對於多表之間的關聯查詢(尤其是大數據表)時,其性能將會降低,同時也提高了客戶端程序的編程難度,因此,物理設計需折衷考慮,根據業務規則,確定對關聯表的數據量大小、數據項的訪問頻度,對此類數據表頻繁的關聯查詢應適當提高數據冗餘設計但增加了表間連接查詢的操作,也使得程序的變得復雜,為了提高系統的響應時間,合理的數據冗餘也是必要的。設計人員在設計階段應根據系統操作的類型、頻度加以均衡考慮。
另外,最好不要用自增屬性欄位作為主鍵與子表關聯。不便於系統的遷移和數據恢復。對外統計系統映射關系丟失(******************)。
原來的表格必須可以通過由它分離出去的表格重新構建。使用這個規定的好處是,你可以確保不會在分離的表格中引入多餘的列,所有你創建的表格結構都與它們的實際需要一樣大。應用這條規定是一個好習慣,不過除非你要處理一個非常大型的數據,否則你將不需要用到它。(例如一個通行證系統,我可以將USERID,USERNAME,USERPASSWORD,單獨出來作個表,再把USERID作為其他表的外鍵)
表的設計具體注意的問題:
1、數據行的長度不要超過8020位元組,如果超過這個長度的話在物理頁中這條數據會佔用兩行從而造成存儲碎片,降低查詢效率。
2、能夠用數字類型的欄位盡量選擇數字類型而不用字元串類型的(電話號碼),這會降低查詢和連接的性能,並會增加存儲開銷。這是因為引擎在處理查詢和連接回逐個比較字元串中每一個字元,而對於數字型而言只需要比較一次就夠了。
3、對於不可變字元類型char和可變字元類型varchar都是8000位元組,char查詢快,但是耗存儲空間,varchar查詢相對慢一些但是節省存儲空間。在設計欄位的時候可以靈活選擇,例如用戶名、密碼等長度變化不大的欄位可以選擇CHAR,對於評論等長度變化大的欄位可以選擇VARCHAR。
4、欄位的長度在最大限度的滿足可能的需要的前提下,應該盡可能的設得短一些,這樣可以提高查詢的效率,而且在建立索引的時候也可以減少資源的消耗。
5、基本表及其欄位之間的關系, 應盡量滿足第三範式。但是,滿足第三範式的資料庫設計,往往不是最好的設計。為了提高資料庫的運行效率,常常需要降低範式標准:適當增加冗餘,達到以空間換時間的目的。
6、若兩個實體之間存在多對多的關系,則應消除這種關系。消除的辦法是,在兩者之間增加第三個實體。這樣,原來一個多對多的關系,現在變為兩個一對多的關系。要將原來兩個實體的屬性合理地分配到三個實體中去。這里的第三個實體,實質上是一個較復雜的關系,它對應一張基本表。一般來講,資料庫設計工具不能識別多對多的關系,但能處理多對多的關系。
7、主鍵PK的取值方法,PK是供程序員使用的表間連接工具,可以是一無物理意義的數字串, 由程序自動加1來實現。也可以是有物理意義的欄位名或欄位名的組合。不過前者比後者好。當PK是欄位名的組合時,建議欄位的個數不要太多,多了不但索引佔用空間大,而且速度也慢。
8、主鍵與外鍵在多表中的重復出現, 不屬於數據冗餘,這個概念必須清楚,事實上有許多人還不清楚。非鍵欄位的重復出現, 才是數據冗餘!而且是一種低級冗餘,即重復性的冗餘。高級冗餘不是欄位的重復出現,而是欄位的派生出現。
〖例4〗:商品中的「單價、數量、金額」三個欄位,「金額」就是由「單價」乘以「數量」派生出來的,它就是冗餘,而且是一種高級冗餘。冗餘的目的是為了提高處理速度。只有低級冗餘才會增加數據的不一致性,因為同一數據,可能從不同時間、地點、角色上多次錄入。因此,我們提倡高級冗餘(派生性冗餘),反對低級冗餘(重復性冗餘)。
9、中間表是存放統計數據的表,它是為數據倉庫、輸出報表或查詢結果而設計的,有時它沒有主鍵與外鍵(數據倉庫除外)。臨時表是程序員個人設計的,存放臨時記錄,為個人所用。基表和中間表由DBA維護,臨時表由程序員自己用程序自動維護。
10、防止資料庫設計打補丁的方法是「三少原則」
(1) 一個資料庫中表的個數越少越好。只有表的個數少了,才能說明系統的E--R圖少而精,去掉了重復的多餘的實體,形成了對客觀世界的高度抽象,進行了系統的數據集成,防止了打補丁式的設計;
(2) 一個表中組合主鍵的欄位個數越少越好。因為主鍵的作用,一是建主鍵索引,二是做為子表的外鍵,所以組合主鍵的欄位個數少了,不僅節省了運行時間,而且節省了索引存儲空間;
(3) 一個表中的欄位個數越少越好。只有欄位的個數少了,才能說明在系統中不存在數據重復,且很少有數據冗餘,更重要的是督促讀者學會「列變行」,這樣就防止了將子表中的欄位拉入到主表中去,在主表中留下許多空餘的欄位。所謂「列變行」,就是將主表中的一部分內容拉出去,另外單獨建一個子表。這個方法很簡單,有的人就是不習慣、不採納、不執行。
資料庫設計的實用原則是:在數據冗餘和處理速度之間找到合適的平衡點。「三少」是一個整體概念,綜合觀點,不能孤立某一個原則。該原則是相對的,不是絕對的。「三多」原則肯定是錯誤的。試想:若覆蓋系統同樣的功能,一百個實體(共一千個屬性) 的E--R圖,肯定比二百個實體(共二千個屬性)的E--R圖,要好得多。
提倡「三少」原則,是叫讀者學會利用資料庫設計技術進行系統的數據集成。數據集成的步驟是將文件系統集成為應用資料庫,將應用資料庫集成為主題資料庫,將主題資料庫集成為全局綜合資料庫。集成的程度越高,數據共享性就越強,信息孤島現象就越少,整個企業信息系統的全局E—R圖中實體的個數、主鍵的個數、屬性的個數就會越少。
提倡「三少」原則的目的,是防止讀者利用打補丁技術,不斷地對資料庫進行增刪改,使企業資料庫變成了隨意設計資料庫表的「垃圾堆」,或資料庫表的「大雜院」,最後造成資料庫中的基本表、代碼表、中間表、臨時表雜亂無章,不計其數,導致企事業單位的信息系統無法維護而癱瘓。
「三多」原則任何人都可以做到,該原則是「打補丁方法」設計資料庫的歪理學說。「三少」原則是少而精的原則,它要求有較高的資料庫設計技巧與藝術,不是任何人都能做到的,因為該原則是杜絕用「打補丁方法」設計資料庫的理論依據。
11、在給定的系統硬體和系統軟體條件下,提高資料庫系統的運行效率的辦法是:
(1) 在資料庫物理設計時,降低範式,增加冗餘, 少用觸發器, 多用存儲過程。
(2) 當計算非常復雜、而且記錄條數非常巨大時(例如一千萬條),復雜計算要先在資料庫外面,以文件系統方式用編程語言計算處理完成之後,最後才入庫追加到表中去。
(3) 發現某個表的記錄太多,例如超過一千萬條,則要對該表進行水平分割。水平分割的做法是,以該表主鍵PK的某個值為界線,將該表的記錄水平分割為兩個表。若發現某個表的欄位太多,例如超過八十個,則垂直分割該表,將原來的一個表分解為兩個表。
(4) 對資料庫管理系統DBMS進行系統優化,即優化各種系統參數,如緩沖區個數。
(5) 在使用面向數據的SQL語言進行程序設計時,盡量採取優化演算法。
總之,要提高資料庫的運行效率,必須從資料庫系統級優化、資料庫設計級優化、程序實現級優化,這三個層次上同時下功夫。
主鍵設計:
1、不建議用多個欄位做主鍵,單個表還可以,但是關聯關系就會有問題,主鍵自增是高性能的。
2、一般情況下,如果有兩個外鍵,不建議採用兩個外鍵作為聯合住建,另建一個欄位作為主鍵。除非這條記錄沒有邏輯刪除標志,且該表永遠只有一條此聯合主鍵的記錄。
3、一般而言,一個實體不能既無主鍵又無外鍵。在E—R 圖中, 處於葉子部位的實體, 可以定義主鍵,也可以不定義主鍵(因為它無子孫), 但必須要有外鍵(因為它有父親)。
主鍵與外鍵的設計,在全局資料庫的設計中,佔有重要地位。當全局資料庫的設計完成以後,有個美國資料庫設計專家說:「鍵,到處都是鍵,除了鍵之外,什麼也沒有」,這就是他的資料庫設計經驗之談,也反映了他對信息系統核心(數據模型)的高度抽象思想。因為:主鍵是實體的高度抽象,主鍵與、外鍵的配對,表示實體之間的連接。
2. 如何在高並發環境下設計出無鎖的資料庫操作
一個在線2k的游戲,每秒鍾並發都嚇死人。傳統的hibernate直接插庫基本上是不可行的。我就一步步推導出一個無鎖的資料庫操作。
1. 並發中如何無鎖。
一個很簡單的思路,把並發轉化成為單線程。java的Disruptor就是一個很好的例子。如果用java的concurrentCollection類去做,原理就是啟動一個線程,跑一個Queue,並發的時候,任務壓入Queue,線程輪訓讀取這個Queue,然後一個個順序執行。
在這個設計模式下,任何並發都會變成了單線程操作,而且速度非常快。現在的node.js, 或者比較普通的ARPG服務端都是這個設計,「大循環」架構。
這樣,我們原來的系統就有了2個環境:並發環境 + 」大循環「環境
並發環境就是我們傳統的有鎖環境,性能低下。
」大循環「環境是我們使用Disruptor開辟出來的單線程無鎖環境,性能強大。
2. 」大循環「環境 中如何提升處理性能。
一旦並發轉成單線程,那麼其中一個線程一旦出現性能問題,必然整個處理都會放慢。所以在單線程中的任何操作絕對不能涉及到IO處理。那資料庫操作怎麼辦?
增加緩存。這個思路很簡單,直接從內存讀取,必然會快。至於寫、更新操作,採用類似的思路,把操作提交給一個Queue,然後單獨跑一個Thread去一個個獲取插庫。這樣保證了「大循環」中不涉及到IO操作。
問題再次出現:
如果我們的游戲只有個大循環還容易解決,因為裡面提供了完美的同步無鎖。
但是實際上的游戲環境是並發和「大循環」並存的,即上文的2種環境。那麼無論我們怎麼設計,必然會發現在緩存這塊上要出現鎖。
3. 並發與「大循環」如何共處,消除鎖?
我們知道如果在「大循環」中要避免鎖操作,那麼就用「非同步」,把操作交給線程處理。結合這2個特點,我稍微改下資料庫架構。
原本的緩存層,必然會存在著鎖,例如:
public TableCache
{
private HashMap<String, Object> caches = new ConcurrentHashMap<String, Object>();
}
這個結構是必然的了,保證了在並發的環境下能夠准確的操作緩存。但是」大循環「卻不能直接操作這個緩存進行修改,所以必須啟動一個線程去更新緩存,例如:
private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
EXECUTOR.execute(new LatencyProcessor(logs));
class LatencyProcessor implements Runnable
{
public void run()
{
// 這里可以任意的去修改內存數據。採用了非同步。
}
}
OK,看起來很漂亮。但是又有個問題出現了。在高速存取的過程中,非常有可能緩存還沒有被更新,就被其他請求再次獲取,得到了舊的數據。
4. 如何保證並發環境下緩存數據的唯一正確?
我們知道,如果只有讀操作,沒有寫操作,那麼這個行為是不需要加鎖的。
我使用這個技巧,在緩存的上層,再加一層緩存,成為」一級緩存「,原來的就自然成為」二級緩存「。有點像CPU了對不?
一級緩存只能被」大循環「修改,但是可以被並發、」大循環「同時獲取,所以是不需要鎖的。
當發生資料庫變動,分2種情況:
1)並發環境下的資料庫變動,我們是允許有鎖的存在,所以直接操作二級緩存,沒有問題。
2)」大循環「環境下資料庫變動,首先我們把變動數據存儲在一級緩存,然後交給非同步修正二級緩存,修正後刪除一級緩存。
這樣,無論在哪個環境下讀取數據,首先判斷一級緩存,沒有再判斷二級緩存。
這個架構就保證了內存數據的絕對准確。
而且重要的是:我們有了一個高效的無鎖空間,去實現我們任意的業務邏輯。
最後,還有一些小技巧提升性能。
1. 既然我們的資料庫操作已經被非同步處理,那麼某個時間,需要插庫的數據可能很多,通過對表、主鍵、操作類型的排序,我們可以刪除一些無效操作。例如:
a)同一個表同一個主鍵的多次UPdate,取最後一次。
b)同一個表同一個主鍵,只要出現Delete,前面所有操作無效。
2. 既然我們要對操作排序,必然會存在一個根據時間排序,如何保證無鎖呢?使用
private final static AtomicLong _seq = new AtomicLong(0);
即可保證無鎖又全局唯一自增,作為時間序列。
3. 資料庫訪問層如何優化實現高並發
資料庫訪問層是一個靜態的單例來實現的,裡面就是
conn.open();
Adapter.fill(ds);
conn.close();
之類的方法,其他通過調用這些方法來獲得數據.
2:我理解的是應該只有 1 個,那麼1個效率是不是太慢? 而且數據請求的是序列的還是錯序的?(裡面沒有使用非同步).
高並發也是關於連接池的
連接池就是一個線程.維護了連接的一個隊列
對於一個連接字元串.默認的連接池是打開,並且默認最大值是 100個
如果Close之後這個連接其實是保持在連接池中,並沒有立既銷毀,
而是下一個 new Connection().Open()
的時候直接使用的
對於同樣的連接字元串,如果再來一個數據連接請求,最大值沒有達到 100,
那麼,會創建一個連接,如果已經達到了 100,會拋出連接池已滿的異常.
如果你要高並發,建議你增大連接池大小,指定MaxPoolCount =1000或是更大(好像是這樣拼的具體查msdn) 連接池對應連接字元串,如果字元串不同,少個多個空格,連接池都不同 連接池允許應用程序從連接池中獲得一個連接並使用這個連接,而不需要為每一個連接請求重新建立一個連接。一旦一個新的連接被創建並且放置在連接池中,應用程序就可以重復使用這個連接而不必實施整個資料庫連接創建過程。
當應用程序請求一個連接時,連接池為該應用程序分配一個連接而不是重新建立一個連接;當應用程序使用完連接後,該連接被歸還給連接池而不是直接釋放。
如果連接生存期已過期,或者連接池管理程序檢測到與伺服器的連接已斷開,連接池管理程序將從池中移除該連接。只有在嘗試與伺服器進行通信後,才可以檢測到這種情況。如果發現某連接不再連接到伺服器,則會將其標記為無效。連接池管理程序會定期掃描連接池,查找已釋放到池中並標記為無效的對象。找到後,這些連接將被永久移除。
4. 一個系統在用戶多,高並發的情況下,資料庫該如何設計
資料庫建立多表關聯,關鍵業務數據欄位和查詢欄位建立索引,對唯一性建立好,同時多任務並發時程序設計時注意數據的合理性檢驗和用戶處理數據有問題時的友好提示見面,建立好的結構文檔說明,同時對關鍵欄位的關系型作好記錄,有效地設計多表的結構安排,盡量減少數據的冗餘,同時又要避免對歷史數據的影響,保持良好的數據管理
5. 資料庫並發控制的主要方法
MVCC:多版本並發控制。
6. 資料庫的並發控制
就是,連接,比如10個人同時連接在資料庫上,就是10個並發數,很多軟體都用這個來收費,並發數
7. 怎麼設計資料庫和用什麼方法去應對並發讀寫
大數據並發處理解決方案:
1、HTML靜態化
效率最高、消耗最小的就是純靜態化的html頁面,所以盡可能使網站上的頁面採用靜態頁面來實現,這個最簡單的方法其實也是最有效的方法。但是對於大量內容並且頻繁更新的網站,無法全部手動去挨個實現,於是出現了常見的信息發布系統CMS,像常訪問的各個門戶站點的新聞頻道,甚至他們的其他頻道,都是通過信息發布系統來管理和實現的,信息發布系統可以實現最簡單的信息錄入自動生成靜態頁面,還能具備頻道管理、許可權管理、自動抓取等功能,對於一個大型網站來說,擁有一套高效、可管理的CMS是必不可少的。
2、圖片伺服器分離
對於Web伺服器來說,不管是Apache、IIS還是其他容器,圖片是最消耗資源的,於是有必要將圖片與頁面進行分離,這是基本上大型網站都會採用的策略,他們都有獨立的圖片伺服器,甚至很多台圖片伺服器。這樣的架構可以降低提供頁面訪問請求的伺服器系統壓力,並且可以保證系統不會因為圖片問題而崩潰,在應用伺服器和圖片伺服器上,可以進行不同的配置優化,比如apache在配置ContentType的時候可以盡量少支持,盡可能少的LoadMole,保證更高的系統消耗和執行效率。 這一實現起來是比較容易的一現,如果伺服器集群操作起來更方便,如果是獨立的伺服器,新手可能出現上傳圖片只能在伺服器本地的情況下,可以在令一台伺服器設置的IIS採用網路路徑來實現圖片伺服器,即不用改變程序,又能提高性能,但對於伺服器本身的IO處理性能是沒有任何的改變。
3、資料庫集群和庫表散列
大型網站都有復雜的應用,這些應用必須使用資料庫,那麼在面對大量訪問的時候,資料庫的瓶頸很快就能顯現出來,這時一台資料庫將很快無法滿足應用,於是需要使用資料庫集群或者庫表散列。
4、緩存
緩存一詞搞技術的都接觸過,很多地方用到緩存。網站架構和網站開發中的緩存也是非常重要。架構方面的緩存,對Apache比較熟悉的人都能知道Apache提供了自己的緩存模塊,也可以使用外加的Squid模塊進行緩存,這兩種方式均可以有效的提高Apache的訪問響應能力。
網站程序開發方面的緩存,Linux上提供的Memory Cache是常用的緩存介面,可以在web開發中使用,比如用Java開發的時候就可以調用MemoryCache對一些數據進行緩存和通訊共享,一些大型社區使用了這樣的架構。另外,在使用web語言開發的時候,各種語言基本都有自己的緩存模塊和方法,PHP有Pear的Cache模塊,Java就更多了,.net不是很熟悉,相信也肯定有。
5、鏡像
鏡像是大型網站常採用的提高性能和數據安全性的方式,鏡像的技術可以解決不同網路接入商和地域帶來的用戶訪問速度差異,比如ChinaNet和ENet之間的差異就促使了很多網站在教育網內搭建鏡像站點,數據進行定時更新或者實時更新。在鏡像的細節技術方面,這里不闡述太深,有很多專業的現成的解決架構和產品可選。也有廉價的通過軟體實現的思路,比如Linux上的rsync等工具。
6、負載均衡
負載均衡將是大型網站解決高負荷訪問和大量並發請求採用的終極解決辦法。 負載均衡技術發展了多年,有很多專業的服務提供商和產品可以選擇。
硬體四層交換
第四層交換使用第三層和第四層信息包的報頭信息,根據應用區間識別業務流,將整個區間段的業務流分配到合適的應用伺服器進行處理。第四層交換功能就象是虛IP,指向物理伺服器。它傳輸的業務服從的協議多種多樣,有HTTP、FTP、NFS、Telnet或其他協議。這些業務在物理伺服器基礎上,需要復雜的載量平衡演算法。在IP世界,業務類型由終端TCP或UDP埠地址來決定,在第四層交換中的應用區間則由源端和終端IP地址、TCP和UDP埠共同決定。
在硬體四層交換產品領域,有一些知名的產品可以選擇,比如Alteon、F5等,這些產品很昂貴,但是物有所值,能夠提供非常優秀的性能和很靈活的管理能力。Yahoo中國當初接近2000台伺服器使用了三四台Alteon就搞定了。
8. 多線程如何並發訪問SQLite資料庫
SQLite作為一款小型的嵌入式資料庫,本身沒有提供復雜的鎖定機制,無法內部管理多路並發下的數據操作同步問題,更談不上優化,所以涉及到多路並發的情況,需要外部進行讀寫鎖控制,否則SQLite會返回SQLITE_BUSY錯誤,以駁回相關請求。
返回SQLITE_BUSY主要有以下幾種情況:
1。當有寫操作時,其他讀操作會被駁回
2。當有寫操作時,其他寫操作會被駁回
3。當開啟事務時,在提交事務之前,其他寫操作會被駁回
4。當開啟事務時,在提交事務之前,其他事務請求會被駁回
5。當有讀操作時,其他寫操作會被駁回
6。讀操作之間能夠並發執行
基於以上討論,可以看出這是一個典型的讀者寫者問題,讀操作要能夠共享,寫操作要互斥,讀寫之間也要互斥
可以設計如下的方案解決並發操作資料庫被鎖定的問題,同時保證讀操作能夠保持最大並發
1。採用互斥鎖控制資料庫寫操作
2。只有擁有互斥鎖的線程才能夠操作資料庫
3。寫操作必須獨立擁有互斥鎖
4。讀操作必須能夠共享互斥鎖,即在第一次讀取的時候獲取互斥鎖,最後一次讀取的時候釋放互斥鎖
9. 資料庫並發控制中使用什麼可以獲得更高的並發度
資料庫並發控制中使用可以獲得更高的並發度好像沒有,只有鎖這種方式。可以用樂觀鎖。當發生死鎖時,可以使用等待圖法,消除死鎖。
並發控制保證事務4個特性,acid:A:原子性(Atomicity) 事務是資料庫的邏輯工作單位,事務中包括的諸操作要麼全做,要麼全不做。C:一致性(Consistency) 事務執行的結果必須是使資料庫從一個一致性狀態變到另一個一致性狀態。
資料庫管理系統中的並發控制:
資料庫管理系統(DBMS)中的並發控制的任務是確保在多個事務同時存取資料庫中同一數據時不破壞事務的隔離性和統一性以及資料庫的統一性。
資料庫並發控制現有兩處火車票售票點,同時讀取某一趟列車車票資料庫中車票余額為X。兩處售票點同時賣出一張車票,同時修改余額為X -1寫回資料庫,這樣就造成了實際賣出兩張火車票而資料庫中的卻記錄只少了一張。
資料庫並發控制產生這種情況的原因是因為兩個事務讀入同一數據並同時修改,其中一個事務提交的結果破壞了另一個事務提交的結果,導致其數據的修改被丟失,破壞了事務的隔離性。並發控制要解決的就是這類問題。