當前位置:首頁 » 數據倉庫 » 資料庫並發解決方案
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

資料庫並發解決方案

發布時間: 2022-04-30 06:02:37

⑴ C#怎麼處理資料庫並發問題

比如我們在sql Server中的表中定義一個欄位為timestamp類型的欄位ts,這個欄位的值不需要我們進行控制的。
在Insert與Update時,資料庫會自己進行ts值的更新,因此我們只要在Update時使用:
Update xxx where key=@key and ts=@ts 就可以了,根本不用考慮ts里的值更新。
Delete時也最好進行一下判斷,用這種方式是可以控制數據並發操作的。
只需要在Update與Delete時,判斷"影響條數"就可以知道更新是否成功。
這一點我想非常方便,但不是所有的資料庫都支持timestampr的,如在Access里沒有timestamp欄位,我也不知道其他的資料庫是否都有類似的timestamp類型,不管怎麼樣,我覺得我們不能完全信賴於資料庫的控制,而應該採用自設的控制機制,這樣可以適應系統的資料庫移值,下面我就介紹一下,在.NET下如何實現,自設的時間戳控制。
我們也同樣建一個欄位ts,定義為Varchar,長度在20以上即可,而且不允許為null,這樣比較合適。
我們應該採用什麼機制來生成隨時的或者說不可能會產生一樣的值,我推薦的是DateTime.Now.Ticks,這是一個12位的數字,由於在Update等更新時,資料庫會自動進行鎖定,所以不可能會在同一時間會有兩個一樣的操作執行,因此這就可以避免Ticks產生相同的值了。
或者也可以採用Guid值,也可以產生唯一值,但我覺得Guid值太大,可能會影響效率。
那好,在我們Insert時:Insert xxxx ts='221283747584' where key='1'

⑵ 資料庫有幾十萬條數據,求高手指點如何處理並發

如果有20萬條數據,5分鍾內處理完成,那麼每秒最少要處理600多條數據。你這個瓶頸在三個方面:

  1. 本地資料庫讀寫,你這個需要資料庫表不人導致死鎖的,這個可以通過堆硬體性能(IO和CPU主頻),資料庫做好對應的索引提升查詢速度,返回的結果先緩存到redis里,然後再寫回去,應該是可以解決的

  2. 提交速度,每秒提交600條,這個靠單線程是肯定解決不了的,這需要多任務多線程處理。。。最好的方式是第三方網站可以批量查詢

  3. 你以這么快的速度提交過去,第三方介面不一定能處理完成,可能有QPS限速的。這就不是你解決的了,需要第三方提升性能。

⑶ 資料庫並發控制的主要方法

MVCC:多版本並發控制。

⑷ 如何處理資料庫並發問題

想要知道如何處理數據並發,自然需要先了解數據並發。

什麼是數據並發操作呢?
就是同一時間內,不同的線程同時對一條數據進行讀寫操作。

在互聯網時代,一個系統常常有很多人在使用,因此就可能出現高並發的現象,也就是不同的用戶同時對一條數據進行操作,如果沒有有效的處理,自然就會出現數據的異常。而最常見的一種數據並發的場景就是電商中的秒殺,成千上萬個用戶對在極端的時間內,搶購一個商品。針對這種場景,商品的庫存就是一個需要控制的數據,而多個用戶對在同一時間對庫存進行重寫,一個不小心就可能出現超賣的情況。

針對這種情況,我們如何有效的處理數據並發呢?

第一種方案、資料庫鎖
從鎖的基本屬性來說,可以分為兩種:一種是共享鎖(S),一種是排它鎖(X)。在MySQL的資料庫中,是有四種隔離級別的,會在讀寫的時候,自動的使用這兩種鎖,防止數據出現混亂。

這四種隔離級別分別是:

讀未提交(Read Uncommitted)
讀提交(Read Committed)
可重復讀(Repeated Read)
串列化(Serializable)
當然,不同的隔離級別,效率也是不同的,對於數據的一致性保證也就有不同的結果。而這些可能出現的又有哪些呢?

臟讀(dirty read)

當事務與事務之間沒有任何隔離的時候,就可能會出現臟讀。例如:商家想看看所有的訂單有哪些,這時,用戶A提交了一個訂單,但事務還沒提交,商家卻看到了這個訂單。而這時就會出現一種問題,當商家去操作這個訂單時,可能用戶A的訂單由於部分問題,導致數據回滾,事務沒有提交,這時商家的操作就會失去目標。

不可重復讀(unrepeatable read)

一個事務中,兩次讀操作出來的同一條數據值不同,就是不可重復讀。

例如:我們有一個事務A,需要去查詢一下商品庫存,然後做扣減,這時,事務B操作了這個商品,扣減了一部分庫存,當事務A再次去查詢商品庫存的時候,發現這一次的結果和上次不同了,這就是不可重復讀。

幻讀(phantom problem)

一個事務中,兩次讀操作出來的結果集不同,就是幻讀。

例如:一個事務A,去查詢現在已經支付的訂單有哪些,得到了一個結果集。這時,事務B新提交了一個訂單,當事務A再次去查詢時,就會出現,兩次得到的結果集不同的情況,也就是幻讀了。

那針對這些結果,不同的隔離級別可以干什麼呢?

「讀未提(Read Uncommitted)」能預防啥?啥都預防不了。

「讀提交(Read Committed)」能預防啥?使用「快照讀(Snapshot Read)」方式,避免「臟讀」,但是可能出現「不可重復讀」和「幻讀」。

「可重復讀(Repeated Red)」能預防啥?使用「快照讀(Snapshot Read)」方式,鎖住被讀取記錄,避免出現「臟讀」、「不可重復讀」,但是可能出現「幻讀」。

「串列化(Serializable)」能預防啥?有效避免「臟讀」、「不可重復讀」、「幻讀」,不過運行效率奇差。

好了,鎖說完了,但是,我們的資料庫鎖,並不能有效的解決並發的問題,只是盡可能保證數據的一致性,當並發量特別大時,資料庫還是容易扛不住。那解決數據並發的另一個手段就是,盡可能的提高處理的速度。

因為數據的IO要提升難度比較大,那麼通過其他的方式,對數據進行處理,減少資料庫的IO,就是提高並發能力的有效手段了。

最有效的一種方式就是:緩存
想要減少並發出現的概率,那麼讀寫的效率越高,讀寫的執行時間越短,自然數據並發的可能性就變小了,並發性能也有提高了。

還是用剛才的秒殺舉例,我們為的就是保證庫存的數據不出錯,賣出一個商品,減一個庫存,那麼,我們就可以將庫存放在內存中進行處理。這樣,就能夠保證庫存有序的及時扣減,並且不出現問題。這樣,我們的資料庫的寫操作也變少了,執行效率也就大大提高了。

當然,常用的分布式緩存方式有:Redis和Memcache,Redis可以持久化到硬碟,而Memcache不行,應該怎麼選擇,就看具體的使用場景了。

當然,緩存畢竟使用的范圍有限,很多的數據我們還是必須持久化到硬碟中,那我們就需要提高資料庫的IO能力,這樣避免一個線程執行時間太長,造成線程的阻塞。

那麼,讀寫分離就是另一種有效的方式了
當我們的寫成為了瓶頸的時候,讀寫分離就是一種可以選擇的方式了。

我們的讀庫就只需要執行讀,寫庫就只需要執行寫,把讀的壓力從主庫中分離出去,讓主庫的資源只是用來保證寫的效率,從而提高寫操作的性能。

⑸ 高並發下,資料庫成最大問題怎麼辦

一、資料庫結構的設計

為了保證資料庫的一致性和完整性,在邏輯設計的時候往往會設計過多的表間關聯,盡可能的降低數據的冗餘。(例如用戶表的地區,我們可以把地區另外存放到一個地區表中)如果數據冗餘低,數據的完整性容易得到保證,提高了數據吞吐速度,保證了數據的完整性,清楚地表達數據元素之間的關系。不要用自增屬性欄位作為主鍵與子表關聯。不便於系統的遷移和數據恢復。對外統計系統映射關系丟失。
表的設計具體注意的問題:
1、數據行的長度不要超過8020位元組,如果超過這個長度的話在物理頁中這條數據會佔用兩行從而造成存儲碎片,降低查詢效率。
2、能夠用數字類型的欄位盡量選擇數字類型而不用字元串類型的(電話號碼),這會降低查詢和連接的性能,並會增加存儲開銷。這是因為引擎在處理查詢和連接回逐個比較字元串中每一個字元,而對於數字型而言只需要比較一次就夠了。
3、對於不可變字元類型char和可變字元類型varchar都是8000位元組,char查詢快,但是耗存儲空間,varchar查詢相對慢一些但是節省存儲空間。在設計欄位的時候可以靈活選擇,例如用戶名、密碼等長度變化不大的欄位可以選擇CHAR,對於評論等長度變化大的欄位可以選擇VARCHAR。
4、欄位的長度在最大限度的滿足可能的需要的前提下,應該盡可能的設得短一些,這樣可以提高查詢的效率,而且在建立索引的時候也可以減少資源的消耗。

二、查詢的優化

在數據窗口使用SQL時,盡量把使用的索引放在選擇的首列;演算法的結構盡量簡單;
在查詢時,不要過多地使用通配符如SELECT* FROM T1語句,要用到幾列就選擇幾列如:SELECT COL1,COL2 FROMT1;在可能的情況下盡量限制盡量結果集行數如:SELECT TOP 300 COL1,COL2,COL3 FROMT1,因為某些情況下用戶是不需要那麼多的數據的。
在沒有建索引的情況下,資料庫查找某一條數據,就必須進行全表掃描了,對所有數據進行一次遍歷,查找出符合條件的記錄。在數據量比較小的情況下,也許看不出明顯的差別,但是當數據量大的情況下,這種情況就是極為糟糕的了。
SQL語句在SQL SERVER中是如何執行的,他們擔心自己所寫的SQL語句會被SQLSERVER誤解。比如:
select * from table1 where name='zhangsan' and tID >10000和執行:
select * from table1 where tID > 10000 andname='zhangsan'
一些人不知道以上兩條語句的執行效率是否一樣,因為如果簡單的從語句先後上看,這兩個語句的確是不一樣,如果tID是一個聚合索引,那麼後一句僅僅從表的10000條以後的記錄中查找就行了;而前一句則要先從全表中查找看有幾個name='zhangsan'的,而後再根據限制條件條件tID>10000來提出查詢結果。
事實上,這樣的擔心是不必要的。SQLSERVER中有一個「查詢分析優化器」,它可以計算出where子句中的搜索條件並確定哪個索引能縮小表掃描的搜索空間,也就是說,它能實現自動優化。雖然查詢優化器可以根據where子句自動的進行查詢優化,但有時查詢優化器就會不按照您的本意進行快速查詢。

所以,優化查詢最重要的就是,盡量使語句符合查詢優化器的規則避免全表掃描而使用索引查詢。
具體要注意的:
1.應盡量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num is null

可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:

select id from t where num=0
2.應盡量避免在 where子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。優化器將無法通過索引來確定將要命中的行數,因此需要搜索該表的所有行。
3.應盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num=10 or num=20

可以這樣查詢:

select id from t where num=10
union all
select id from t where num=20s
4.in 和 not in 也要慎用,因為IN會使系統無法使用索引,而只能直接搜索表中的數據。如:

select id from t where num in(1,2,3)
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.必要時強制查詢優化器使用某個索引,如在 where子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變數,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:

select id from t where num=@num
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
7.應盡量避免在 where 子句中對欄位進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:

SELECT * FROM T1 WHERE F1/2=100
應改為:
SELECT * FROM T1 WHERE F1=100*2
SELECT * FROM RECORD WHERESUBSTRING(CARD_NO,1,4)=』5378』
應改為:
SELECT * FROM RECORD WHERE CARD_NO LIKE 『5378%』
SELECT member_number, first_name, last_name FROMmembers
WHERE DATEDIFF(yy,datofbirth,GETDATE()) >21
應改為:
SELECT member_number, first_name, last_name FROMmembers
WHERE dateofbirth <DATEADD(yy,-21,GETDATE())
即:任何對列的操作都將導致表掃描,它包括資料庫函數、計算表達式等等,查詢時要盡可能將操作移至等號右邊。
8.應盡量避免在where子句中對欄位進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:

select id from t wheresubstring(name,1,3)='abc'--name以abc開頭的id
select id from t wheredatediff(day,createdate,'2005-11-30')=0--『2005-11-30』生成的id
應改為:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' andcreatedate<'2005-12-1'
9.不要在 where 子句中的「=」左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。
10.在使用索引欄位作為條件時,如果該索引是復合索引,那麼必須使用到該索引中的第一個欄位作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應盡可能的讓欄位順序與索引順序相一致。
11.很多時候用 exists是一個好的選擇:

elect num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
但是後者的效率顯然要高於前者。因為後者不會產生大量鎖定的表掃描或是索引掃描。
如果你想校驗表裡是否存在某條紀錄,不要用count(*)那樣效率很低,而且浪費伺服器資源。可以用EXISTS代替。如:
IF (SELECT COUNT(*) FROM table_name WHERE column_name ='xxx')
可以寫成:
IF EXISTS (SELECT * FROM table_name WHERE column_name = 'xxx')
12.盡量使用表變數來代替臨時表。如果表變數包含大量數據,請注意索引非常有限(只有主鍵索引)。
13.避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。
14.臨時表並不是不可使用,適當地使用它們可以使某些常式更有效,例如,當需要重復引用大型表或常用表中的某個數據集時。但是,對於一次性事件,最好使用導出表。
15.在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量log ,以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然後insert。
16.如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table,這樣可以避免系統表的較長時間鎖定。
17.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF。無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。
18.盡量避免大事務操作,提高系統並發能力。
19.盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

20.避免使用不兼容的數據類型。例如float和int、char和varchar、binary和varbinary是不兼容的。數據類型的不兼容可能使優化器無法執行一些本來可以進行的優化操作。例如:

SELECT name FROM employee WHERE salary >60000
在這條語句中,如salary欄位是money型的,則優化器很難對其進行優化,因為60000是個整型數。我們應當在編程時將整型轉化成為錢幣型,而不要等到運行時轉化。

23、能用DISTINCT的就不用GROUP BY

SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BYOrderID
可改為:
SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10
24.能用UNION ALL就不要用UNION

UNION ALL不執行SELECTDISTINCT函數,這樣就會減少很多不必要的資源

35.盡量不要用SELECT INTO語句。

SELECT INOT 語句會導致表鎖定,阻止其他用戶訪問該表。

四、建立高效的索引
創建索引一般有以下兩個目的:維護被索引列的唯一性和提供快速訪問表中數據的策略。
大型資料庫有兩種索引即簇索引和非簇索引,一個沒有簇索引的表是按堆結構存儲數據,所有的數據均添加在表的尾部,而建立了簇索引的表,其數據在物理上會按照簇索引鍵的順序存儲,一個表只允許有一個簇索引,因此,根據B樹結構,可以理解添加任何一種索引均能提高按索引列查詢的速度,但會降低插入、更新、刪除操作的性能,尤其是當填充因子(FillFactor)較大時。所以對索引較多的表進行頻繁的插入、更新、刪除操作,建表和索引時因設置較小的填充因子,以便在各數據頁中留下較多的自由空間,減少頁分割及重新組織的工作。
索引是從資料庫中獲取數據的最高效方式之一。95%的資料庫性能問題都可以採用索引技術得到解決。作為一條規則,我通常對邏輯主鍵使用唯一的成組索引,對系統鍵(作為存儲過程)採用唯一的非成組索引,對任何外鍵列[欄位]採用非成組索引。不過,索引就象是鹽,太多了菜就咸了。你得考慮資料庫的空間有多大,表如何進行訪問,還有這些訪問是否主要用作讀寫。
實際上,您可以把索引理解為一種特殊的目錄。微軟的SQL SERVER提供了兩種索引:聚集索引(clusteredindex,也稱聚類索引、簇集索引)和非聚集索引(nonclusteredindex,也稱非聚類索引、非簇集索引)。
聚集索引和非聚集索引的區別:
其實,我們的漢語字典的正文本身就是一個聚集索引。比如,我們要查「安」字,就會很自然地翻開字典的前幾頁,因為「安」的拼音是「an」,而按照拼音排序漢字的字典是以英文字母「a」開頭並以「z」結尾的,那麼「安」字就自然地排在字典的前部。如果您翻完了所有以「a」開頭的部分仍然找不到這個字,那麼就說明您的字典中沒有這個字;同樣的,如果查「張」字,那您也會將您的字典翻到最後部分,因為「張」的拼音是「zhang」。也就是說,字典的正文部分本身就是一個目錄,您不需要再去查其他目錄來找到您需要找的內容。
我們把這種正文內容本身就是一種按照一定規則排列的目錄稱為「聚集索引」。
如果您認識某個字,您可以快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛才的方法找到您要查的字,而需要去根據「偏旁部首」查到您要找的字,然後根據這個字後的頁碼直接翻到某頁來找到您要找的字。但您結合「部首目錄」和「檢字表」而查到的字的排序並不是真正的正文的排序方法,比如您查「張」字,我們可以看到在查部首之後的檢字表中「張」的頁碼是672頁,檢字表中「張」的上面是「馳」字,但頁碼卻是63頁,「張」的下面是「弩」字,頁面是390頁。很顯然,這些字並不是真正的分別位於「張」字的上下方,現在您看到的連續的「馳、張、弩」三字實際上就是他們在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我們可以通過這種方式來找到您所需要的字,但它需要兩個過程,先找到目錄中的結果,然後再翻到您所需要的頁碼。

⑹ mysql資料庫怎麼解決高並發問題

通常情況下在PHP中MySQL查詢是串列的,如果能實現MySQL查詢的非同步化,就能實現多條SQL語句同時執行,這樣就能大大地縮短MySQL查詢的耗時,提高資料庫查詢的效率。目前MySQL的非同步查詢只在MySQLi擴展提供,查詢方法分別是:
1、使用MYSQLI_ASYNC模式執行mysqli::query
2、獲取非同步查詢結果:mysqli::reap_async_query
使用mysql非同步查詢,需要使用mysqlnd作為PHP的MySQL資料庫驅動。
使用MySQL非同步查詢,因為需要給所有查詢都創建一個新的連接,而MySQL服務端會為每個連接創建一個單獨的線程進行處理,如果創建的線程過多,則會造成線程切換引起系統負載過高。Swoole中的非同步MySQL其原理是通過MYSQLI_ASYNC模式查詢,然後獲取mysql連接的socket,加入到epoll事件循環中,當資料庫返回結果時會回調指定函數,這個過程是完全非同步非阻塞的。

⑺ sqlite資料庫並發怎麼解決

使用SQLite經常會遇到並發處理,要處理好多線程或多進程之間的並發,就得搞清楚SQLite的機制,尤其是Sqlite的鎖機制。
因為SQLite是文件資料庫,所以它的鎖也基本是和文件一致,也即:寫獨占,讀共享。這意味是在讀取資料庫的時候,是可以多個線程共享的,而如果有增刪改的操作,則會獨占此文件,其他線程會進程都會被阻塞。
在移動設備上,比較常見的情況是App的UI進程和Service進程同時訪問資料庫,這個時候就要對其訪問做好並發的處理,否則會出現很多意想不到的後果。

⑻ 資料庫並發訪問會出現哪些問題可以通過哪些方法解決呀

1.資料庫並發訪問會出現哪些問題?
---------記錄鎖死
2.可以通過哪些方法解決么?
------------減少並發數,做一個消息隊列,採用非同步方式操作資料庫

⑼ 如何處理mysql資料庫並發更新問題

現象

Sysbench對MySQL進行壓測, 並發數過大(>5k)時, Sysbench建立連接的步驟會超時.

猜想

猜想: 直覺上這很簡單, Sysbench每建立一個連接, 都要消耗一個線程, 資源消耗過大導致超時.

驗證: 修改Sysbench源碼, 調大超時時間, 仍然會發生超時.

檢查環境

猜想失敗, 回到常規的環境檢查:

  • MySQL error log 未見異常.

  • syslog 未見異常.

  • tcpmp 觀察網路包未見異常, 連接能完成正常的三次握手; 只觀察到在出問題的連接中, 有一部分的TCP握手的第一個SYN包發生了重傳, 另一部分沒有發生重傳.

  • 自己寫一個簡單的並發發生器, 替換sysbench, 可重現場景. 排除sysbench的影響

  • 猜想2

    懷疑 MySQL 在應用層因為某種原因, 沒有發送握手包, 比如卡在某一個流程上:

  • 檢查MySQL堆棧未見異常, 彷彿MySQL在應用層沒有看到新連接進入.

  • 通過strace檢查MySQL, 發現accept()調用確實沒有感知到新連接.

  • 懷疑是OS的原因, Google之, 得到參考文檔:A TCP 「stuck」 connection mystery【http://www.evanjones.ca/tcp-stuck-connection-mystery.html】

    分析

    參考文檔中的現象跟目前的狀況很類似, 簡述如下:

    正常的TCP連接流程:

  • Client 向 Server 發起連接請求, 發送SYN.

  • Server 預留連接資源, 向 Client 回復SYN-ACK.

  • Client 向 Server 回復ACK.

  • Server 收到 ACK, 連接建立.

  • 在業務層上, Client和Server間進行通訊.

  • 當發生類似SYN-flood的現象時, TCP連接的流程會使用SYN-cookie, 變為:

  • Client 向 Server 發起連接請求, 發送SYN.

  • Server 不預留連接資源, 向 Client 回復SYN-ACK, 包中附帶有簽名A.

  • Client 向 Server 回復ACK, 附帶 f(簽名A) (對簽名進行運算的結果).

  • Server 驗證簽名, 分配連接資源, 連接建立.

  • 在業務層上, Client和Server間進行通訊.

  • 當啟用SYN-cookie時, 第3步的ACK包因為某種原因丟失, 那麼:

  • 從Client的視角, 連接已經建立.

  • 從Server的視角, 連接並不存在, 既沒有建立, 也沒有」即將建立」 (若不啟用SYN-cookie, Server會知道某個連接」即將建立」)

  • 發生這種情況時:

  • 若業務層的第一個包應是從 Client 發往 Server, 則會進行重發或拋出連接錯誤

  • 若業務層的第一個包應是從 Server 發往 Client的, Server不會發出第一個包. MySQL的故障就屬於這種情況.

  • TCP握手的第三步ACK包為什麼丟失

    參考文檔中, 對於TCP握手的第三步ACK包的丟失原因, 描述為:

  • Some of these packets get lost because some buffer somewhere overflows.

  • 我們可以通過Systemtap進一步探究原因.通過一個簡單的腳本:

  • probe kernel.function("cookie_v4_check").return

  • {

  • source_port = @cast($skb->head + $skb->transport_header, "struct tcphdr")->source

  • printf("source=%d, return=%d ",readable_port(source_port), $return)

  • }

  • function readable_port(port) {

  • return (port & ((1<<9)-1)) << 8 | (port >> 8)

  • }

  • 觀察結果, 可以確認cookie_v4_check(syn cookie機制進行包簽名檢查的函數)會返回 NULL(0). 即驗證是由於syn cookie驗證不通過, 導致TCP握手的第三步ACK包不被接受.

    之後就是對其中不同條件進行觀察, 看看是哪個條件不通過. 最終原因是accept隊列滿(sk_acceptq_is_full):

  • static inline bool sk_acceptq_is_full(const struct sock *sk){ return sk->sk_ack_backlog > sk- >sk_max_ack_backlog;}

  • 恢復故障與日誌的正關聯

    在故障處理的一開始, 我們就檢查了syslog, 結論是未見異常.

    當整個故障分析完成, 得知了故障與syn cookie有關, 回頭看syslog, 裡面是有相關的信息, 只是和故障發生的時間不匹配, 沒有正關聯, 因此被忽略.

    檢查Linux源碼:

  • if (!queue->synflood_warned &&

  • sysctl_tcp_syncookies != 2 &&

  • xchg(&queue->synflood_warned, 1) == 0)

  • pr_info("%s: Possible SYN flooding on port %d. %s.

  • Check SNMP counters. ",

  • proto, ntohs(tcp_hdr(skb)->dest), msg);

  • 可以看到日誌受到了抑制, 因此日誌與故障的正關聯被破壞.

    粗看源碼, 每個listen socket只會發送一次告警日誌, 要獲得日誌與故障的正關聯, 必須每次測試重啟MySQL.

    解決方案

    這種故障一旦形成, 難以檢測; 系統日誌中只會出現一次, 在下次重啟MySQL之前就不會再出現了; Client如果沒有合適的超時機制, 萬劫不復.

    解決方案:
    1. 修改MySQL的協議, 讓Client先發握手包. 顯然不現實.
    2. 關閉syn_cookie. 有安全的人又要跳出來了.
    3. 或者調高syn_cookie的觸發條件 (syn backlog長度). 降低系統對syn flood的敏感度, 使之可以容忍業務的syn波動.

    有多個系統參數混合影響syn backlog長度, 參看【http://blog.bbelboer.com/2012/04/09/syn-cookies.html】

    下圖為精華總結

⑽ JAVA解決資料庫並發問題

幾百人報名就叫並發嗎?
概念錯了!
幾百人操作幾百條互不相關的數據那個不叫並發,只有同時操作同一條數據的情況才叫並發。
如果兩人同時修改同一條數據的話,那個要不就使用行鎖(悲觀鎖),要不就用version(樂觀鎖)控制。
其它情況無需考慮的。