來源:小屁孩大帥-楊一凡 發(fā)布時間:2018-12-05 14:52:52 閱讀量:1086
問題產(chǎn)生的原因?
同步和異步--數(shù)據(jù)訪問次數(shù)增大-
一般程序中,是如何處理并發(fā)和同步的?
代碼層次上,Java同步鎖-
一種是代碼層次上的,如java中的同步鎖,典型的就是同步關(guān)鍵字synchronized
數(shù)據(jù)庫層次上,,比較典型的就是悲觀鎖和樂觀鎖。這里我們重點講解的就是悲觀鎖(傳統(tǒng)的物理鎖)和樂觀鎖
悲觀鎖:--數(shù)據(jù)庫鎖機制---多讀少寫的情況下
對數(shù)據(jù)被外界調(diào)用使用修改保持保守悲觀的態(tài)度;
使整個數(shù)據(jù)再處理中,將數(shù)據(jù)處于鎖定狀態(tài)下;
往往是依靠數(shù)據(jù)庫提供的鎖機制
也只有數(shù)據(jù)庫層提供的鎖機制才能 真正保證數(shù)據(jù)訪問的排他性,
否則,即使在本系統(tǒng) 中實現(xiàn)了加鎖機制,也無法保證外部系 統(tǒng)不會修改數(shù)據(jù)
樂觀鎖:---版本記錄機制實現(xiàn)version--少寫,多度的情況下
大多是基于數(shù)據(jù)版本 Version )記錄機制實現(xiàn)。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標(biāo)識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通
過為數(shù)據(jù)庫表增加一個 “version” 字段來 實現(xiàn)。 讀取出數(shù)據(jù)時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提 交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)
3,悲觀和樂觀的區(qū)別和聯(lián)系
悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數(shù)據(jù)的時候都認(rèn)為別人會修改,所以每次在拿數(shù)據(jù)的時候都會上鎖,這樣別人想拿這個數(shù)據(jù)就會block直到它拿到鎖。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數(shù)據(jù)的時候都認(rèn)為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數(shù)據(jù),可以使用版本號等機制。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,像數(shù)據(jù)庫如果提供類似于write_condition機制的其實都是提供的樂觀鎖。
兩種鎖各有優(yōu)缺點,不可認(rèn)為一種好于另一種,像樂觀鎖適用于寫比較少的情況下,即沖突真的很少發(fā)生的時候,這樣可以省去了鎖的開銷,加大了系統(tǒng)的整個吞吐量。但如果經(jīng)常產(chǎn)生沖突,上層應(yīng)用會不斷的進(jìn)行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。
案例分析
案例一:訂票系統(tǒng)案例,某航班只有一張機票,假定有1w個人打開你的網(wǎng)站來訂票,問你如何解決并發(fā)問題(可擴展到任何高并發(fā)網(wǎng)站要考慮
的并發(fā)讀寫問題)
問題,1w個人來訪問,票沒出去前要保證大家都能看到有票,不可能一個人在看到票的時候別人就不能看了。到底誰能搶到,那得看這個人的“運氣”(網(wǎng)
絡(luò)快慢等)
其次考慮的問題,并發(fā),1w個人同時點擊購買,到底誰能成交?總共只有一張票。
首先我們?nèi)菀紫氲胶筒l(fā)相關(guān)的幾個方案 :
鎖同步同步更多指的是應(yīng)用程序的層面,多個線程進(jìn)來,只能一個一個的訪問,java中指的是syncrinized關(guān)鍵字。鎖也有2個層面,一個是java中談到的對
象鎖,用于線程同步;另外一個層面是數(shù)據(jù)庫的鎖;如果是分布式的系統(tǒng),顯然只能利用數(shù)據(jù)庫端的鎖來實現(xiàn)。
假定我們采用了同步機制或者數(shù)據(jù)庫物理鎖機制,如何保證1w個人還能同時看到有票,顯然會犧牲性能,在高并發(fā)網(wǎng)站中是不可取的。使用hibernate后我們
提出了另外一個概念:樂觀鎖、悲觀鎖(即傳統(tǒng)的物理鎖);
采用樂觀鎖即可解決此問題。樂觀鎖意思是不鎖定表的情況下,利用業(yè)務(wù)的控制來解決并發(fā)問題,這樣即保證數(shù)據(jù)的并發(fā)可讀性又保證保存數(shù)據(jù)的排他性,保
證性能的同時解決了并發(fā)帶來的臟數(shù)據(jù)問題。
hibernate中如何實現(xiàn)樂觀鎖:
前提:在現(xiàn)有表當(dāng)中增加一個冗余字段,version版本號, long類型
原理:
1)只有當(dāng)前版本號》=數(shù)據(jù)庫表版本號,才能提交
2)提交成功后,版本號version ++
實現(xiàn)很簡單:在ormapping增加一屬性optimistic-lock="version"即可,以下是樣例片段
<hibernate-mapping>
<class name="com.insigma.stock.ABC" optimistic-lock="version" table="T_Stock" schema="STOCK">
案例二、股票交易系統(tǒng)、銀行系統(tǒng),大數(shù)據(jù)量你是如何考慮的
首先,股票交易系統(tǒng)的行情表,每幾秒鐘就有一個行情記錄產(chǎn)生,一天下來就有(假定行情3秒一個) 股票數(shù)量×20×60*6 條記錄,一月下來這個表記錄數(shù)
量多大? oracle中一張表的記錄數(shù)超過100w后 查詢性能就很差了,如何保證系統(tǒng)性能?
再比如,中國移動有上億的用戶量,表如何設(shè)計?把所有用于存在于一個表么?
所以,大數(shù)量的系統(tǒng),必須考慮表拆分-(表名字不一樣,但是結(jié)構(gòu)完全一樣),通用的幾種方式:(視情況而定)
1)按業(yè)務(wù)分,比如 手機號的表,我們可以考慮 130開頭的作為一個表,131開頭的另外一張表 以此類推
2)利用oracle的表拆分機制做分表
3)如果是交易系統(tǒng),我們可以考慮按時間軸拆分,當(dāng)日數(shù)據(jù)一個表,歷史數(shù)據(jù)弄到其它表。這里歷史數(shù)據(jù)的報表和查詢不會影響當(dāng)日交易。
當(dāng)然,表拆分后我們的應(yīng)用得做相應(yīng)的適配。單純的or-mapping也許就得改動了。比如部分業(yè)務(wù)得通過存儲過程等
此外,我們還得考慮緩存
這里的緩存,指的不僅僅是hibernate,hibernate本身提供了一級二級緩存。這里的緩存獨立于應(yīng)用,依然是內(nèi)存的讀取,假如我們能減少數(shù)據(jù)庫頻繁的訪
問,那對系統(tǒng)肯定大大有利的。比如一個電子商務(wù)系統(tǒng)的商品搜索,如果某個關(guān)鍵字的商品經(jīng)常被搜,那就可以考慮這部分商品列表存放到緩存(內(nèi)存中
去),這樣不用每次訪問數(shù)據(jù)庫,性能大大增加。
簡單的緩存大家可以理解為自己做一個hashmap,把常訪問的數(shù)據(jù)做一個key,value是第一次從數(shù)據(jù)庫搜索出來的值,下次訪問就可以從map里讀取,而不
讀數(shù)據(jù)庫;專業(yè)些的目前有獨立的緩存框架比如memcached 等,可獨立部署成一個緩存服務(wù)器。
常見的提高高并發(fā)下訪問的效率的手段
首先要了解高并發(fā)的的瓶頸在哪里?
1、可能是服務(wù)器網(wǎng)絡(luò)帶寬不夠
2.可能web線程連接數(shù)不夠
3.可能數(shù)據(jù)庫連接查詢上不去。
根據(jù)不同的情況,解決思路也不同。
像第一種情況可以增加網(wǎng)絡(luò)帶寬,DNS域名解析分發(fā)多臺服務(wù)器。
負(fù)載均衡,前置代理服務(wù)器nginx、apache等等
數(shù)據(jù)庫查詢優(yōu)化,讀寫分離,分表等等
最后復(fù)制一些在高并發(fā)下面需要常常需要處理的內(nèi)容:
盡量使用緩存,包括用戶緩存,信息緩存等,多花點內(nèi)存來做緩存,可以大量減少與數(shù)據(jù)庫的交互,提高性能。
用jprofiler等工具找出性能瓶頸,減少額外的開銷。
優(yōu)化數(shù)據(jù)庫查詢語句,減少直接使用hibernate等工具的直接生成語句(僅耗時較長的查詢做優(yōu)化)。
優(yōu)化數(shù)據(jù)庫結(jié)構(gòu),多做索引,提高查詢效率。
統(tǒng)計的功能盡量做緩存,或按每天一統(tǒng)計或定時統(tǒng)計相關(guān)報表,避免需要時進(jìn)行統(tǒng)計的功能。
能使用靜態(tài)頁面的地方盡量使用,減少容器的解析(盡量將動態(tài)內(nèi)容生成靜態(tài)html來顯示)。
解決以上問題后,使用服務(wù)器集群來解決單臺的瓶頸問題。
總 結(jié):其實我們在開發(fā)中都很少考慮這種問題,直接都是先將功能實現(xiàn),當(dāng)一個程序員在干到1到2年,就會感覺光實現(xiàn)功能不是最主要的,安全性能、質(zhì)量等等才是 一個開發(fā)人員最該關(guān)心的。今天我所說的是高并發(fā)。
我的解決思路是:
1、采用分布式應(yīng)用設(shè)計
2、分布式緩存數(shù)據(jù)庫
3、代碼優(yōu)化
---------------------