自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Sqlite事務(wù)模型、性能優(yōu)化Tips、常見誤區(qū)

開發(fā) 前端
本文主要介紹sqlite的事務(wù)模型,以及基于事務(wù)模型的一些性能優(yōu)化tips,包括事務(wù)封裝、WAL+讀寫分離、分庫分表、page size優(yōu)化等。

[[277940]]

 0.前言

本文主要介紹sqlite的事務(wù)模型,以及基于事務(wù)模型的一些性能優(yōu)化tips,包括事務(wù)封裝、WAL+讀寫分離、分庫分表、page size優(yōu)化等。并基于手淘sqlite的使用現(xiàn)狀總結(jié)了部分常見問題及誤區(qū),主要集中在多線程的設(shè)置、多線程下性能優(yōu)化的誤區(qū)等。本文先提出以下幾個(gè)問題(作者在進(jìn)行統(tǒng)一存儲(chǔ)的關(guān)系存儲(chǔ)框架優(yōu)化過程中一直困惑的問題,同時(shí)也是客戶端開發(fā)者經(jīng)常搞錯(cuò)的問題)并在正文中進(jìn)行解答:

  • 1,sqlite的多進(jìn)程安全是怎么實(shí)現(xiàn)的?性能如何?
  • 2,sqlite的數(shù)據(jù)庫連接是什么?
  • 3,言sqlite必提的讀寫分離,具體指什么?能不能提升數(shù)據(jù)讀寫的性能?為什么
  • 4,sqlite提供的WAL特性解決了什么問題?
  • 5,sqlite的多線程設(shè)置是為了解決什么問題?與讀寫分離有什么關(guān)系?
  • 6,什么情況下數(shù)據(jù)庫會(huì)發(fā)生死鎖?
  • 7,有哪些性能優(yōu)化的方案?

1,sqlite主要數(shù)據(jù)結(jié)構(gòu)

在深入了解sqlite之前,最好先對sqlite的主要數(shù)據(jù)結(jié)構(gòu)有個(gè)概要的理解,sqlite是一個(gè)非常完備的關(guān)系數(shù)據(jù)庫系統(tǒng),由很多部分組成(parser,tokenize,virtual machine等等),同時(shí)sqlite的事務(wù)模型相對簡化,是入門學(xué)習(xí)關(guān)系數(shù)據(jù)庫方法論的一個(gè)不錯(cuò)的選擇;下文對事務(wù)模型的分析也基于這些核心數(shù)據(jù)結(jié)構(gòu)。下面這張圖比較準(zhǔn)確的描述了sqlite的幾個(gè)核心數(shù)據(jù)結(jié)構(gòu): 

sqlite事務(wù)模型、性能優(yōu)化tips、常見誤區(qū)

1.1 Connection

connection通過sqlite3_open函數(shù)打開,代表一個(gè)獨(dú)立的事務(wù)環(huán)境(這里及下文提到的事務(wù),包括顯式聲明的事務(wù),也包括隱式的事務(wù),即每條獨(dú)立的sql語句)。

1.2 B-Tree

B-Tree負(fù)責(zé)請求pager從disk讀取數(shù)據(jù),然后把頁面(page)加載到頁面緩沖區(qū)(page cache)。

1.3 Pager

Pager負(fù)責(zé)讀寫數(shù)據(jù)庫,管理內(nèi)存緩存和頁面(即下文提到的page caches),以及管理事務(wù),鎖和崩潰恢復(fù)。

2,sqlite事務(wù)模型及鎖

2.1 sqlite多進(jìn)程安全及Linux & windows文件鎖

  • 關(guān)于建議鎖(advisory lock)和強(qiáng)制鎖(mandatory lock)

建議鎖并不由內(nèi)核強(qiáng)制實(shí)行,如果有進(jìn)程不檢查目標(biāo)文件是否已經(jīng)由別的進(jìn)程加了鎖就往其中寫入數(shù)據(jù),內(nèi)核也不會(huì)加以阻攔。因此,建議鎖并不能阻止進(jìn)程對文件的訪問,而是需要進(jìn)程事先對鎖的狀態(tài)做一個(gè)約定,并根據(jù)鎖的當(dāng)前狀態(tài)和相互關(guān)系來確定其他進(jìn)程是否能對文件執(zhí)行指定的操作。

強(qiáng)制鎖是由內(nèi)核強(qiáng)制采用的文件鎖——由于內(nèi)核對每個(gè)read()和write()操作都會(huì)檢查相應(yīng)的鎖,會(huì)降低系統(tǒng)性能。

  • 典型的建議鎖

鎖文件;鎖文件是最簡單的對文件加鎖的方法,每個(gè)需要加鎖的數(shù)據(jù)文件都有一個(gè)鎖文件(lock file)。但這種方式存在比較大的問題是無法強(qiáng)制保護(hù)需要加鎖的文件,并且當(dāng)加鎖進(jìn)程非正常退出之后,會(huì)造成其他進(jìn)程的死鎖。

記錄鎖;System V和BSD4.3引入了記錄鎖,相應(yīng)的系統(tǒng)調(diào)用為lockf()和flock()。而POSIX對于記錄鎖提供了另外一種機(jī)制,其系統(tǒng)調(diào)用為fcntl()。記錄鎖和鎖文件有兩個(gè)很重要的區(qū)別:1)記錄鎖可以對文件的任何一部分加鎖,這對DBMS有極大的幫助,2)記錄鎖的另一個(gè)優(yōu)點(diǎn)就是它由進(jìn)程持有,而不是文件系統(tǒng)持有,當(dāng)進(jìn)程結(jié)束時(shí),所有的鎖也隨之釋放。對于一個(gè)進(jìn)程本身而言,多個(gè)鎖絕不會(huì)沖突。(Windows中的鎖都是強(qiáng)制鎖,具體不是很熟,只知道在由于windows上文鎖的限制,sqlite多進(jìn)程下的并發(fā)性會(huì)受影響)。

2.1.1 結(jié)論

sqlite的文件鎖在linux/posix上基于記錄鎖實(shí)現(xiàn),也就是說sqlite在文件鎖上會(huì)有以下幾個(gè)特點(diǎn):

  • 多進(jìn)程使用安全,且不會(huì)因?yàn)檫M(jìn)程異常退出引發(fā)死鎖
  • 單進(jìn)程使用性能幾乎不會(huì)受損,多進(jìn)程使用的性能損耗會(huì)受一定的影響

2.2 事務(wù)模型(Without WAL)

sqlite對每個(gè)連接設(shè)計(jì)了五鐘鎖的狀態(tài)(UNLOCKED, PENDING, SHARED, RESERVED, EXCLUSIVE), sqlite的事務(wù)模型中通過鎖的狀態(tài)保證讀寫事務(wù)(包括顯式的事務(wù)和隱式的事務(wù))的一致性和讀寫安全。sqlite官方提供的事務(wù)生命周期如下圖所示,我在這里稍微加了一些個(gè)人的理解: 

sqlite事務(wù)模型、性能優(yōu)化tips、常見誤區(qū)

這里有幾點(diǎn)需要注意:

UNLOCKED、PENDING、SHARED、RESERVED狀態(tài)是非獨(dú)占的,也就是說同一個(gè)連接中多個(gè)線程并發(fā)只讀不會(huì)被阻塞。

寫操作的數(shù)據(jù)修改會(huì)先寫入page cache,內(nèi)容包括journal日志、b-tree的修改等;正是由于page cache的存在,很多耗時(shí)的“重”操作都可以不干擾其他連接和當(dāng)前連接的讀操作,真正意義上保證了sqlite可以同時(shí)處理一個(gè)寫連接和多個(gè)讀連接。

連接由RESERVED狀態(tài)進(jìn)入EXCLUSIVE狀態(tài),需要等待讀線程釋放SHARED鎖,也即寫操作會(huì)被讀操作阻塞

連接由RESERVED狀態(tài)進(jìn)入EXCLUSIVE狀態(tài)后(顯式或隱式的調(diào)用commit),數(shù)據(jù)庫進(jìn)入獨(dú)占狀態(tài),其他任何連接都無法由UNLOCK狀態(tài)進(jìn)入SHARED狀態(tài);也即寫操作會(huì)阻塞所有連接的讀操作(不包括已經(jīng)進(jìn)入SHARED狀態(tài)的操作),直到page caches寫入數(shù)據(jù)庫文件(成功或失敗)。

數(shù)據(jù)庫獨(dú)占狀態(tài)越久,其他操作的等待時(shí)間越久,即SQLITE_BUSY產(chǎn)生的一個(gè)原因。

2.2.1 結(jié)論

對于常規(guī)的事務(wù)模型(without WAL),讀寫(連接)分離,不同連接或同一個(gè)連接上的讀和寫操作仍互相阻塞,對性能提升沒有明顯幫助。

寫事務(wù)在拿到reserve鎖之前在page cache里的操作不會(huì)影響其他連接的讀寫,所以使用事務(wù)進(jìn)行批量數(shù)據(jù)的更新操作有非常大的性能優(yōu)勢。

事務(wù)模型存在死鎖的場景,如下圖所示: 

sqlite事務(wù)模型、性能優(yōu)化tips、常見誤區(qū)

2.3 WAL對事務(wù)模型的影響

按照官方文檔,WAL的原理如下:

對數(shù)據(jù)庫修改是是寫入到WAL文件里的,這些寫是可以并發(fā)的(WAL文件鎖)。所以并不會(huì)阻塞其語句讀原始的數(shù)據(jù)庫文件。當(dāng)WAL文件到達(dá)一定的量級時(shí)(CheckPoint),自動(dòng)把WAL文件的內(nèi)容寫入到數(shù)據(jù)庫文件中。當(dāng)一個(gè)連接嘗試讀數(shù)據(jù)庫的時(shí)候,首先記錄下來當(dāng)前WAL文件的末尾 end mark,然后,先嘗試在WAL文件里查找對應(yīng)的Page,通過WAL-Index來對查找加速(放在共享內(nèi)存里,.shm文件),如果找不到再查找數(shù)據(jù)庫文件。

這里結(jié)合源碼,有下面幾個(gè)理解:

  • 數(shù)據(jù)的寫操作寫入WAL的過程不再需要SHARED鎖、EXCLUSIVE鎖,而是需要WAL文件鎖。
  • 數(shù)據(jù)的寫操作不會(huì)被讀操作阻塞(寫操作不再需要SHARED鎖)。
  • 數(shù)據(jù)的讀操作不會(huì)被寫操作阻塞(寫操作不再需要獨(dú)占數(shù)據(jù)庫)。
  • WAL文件寫入數(shù)據(jù)庫文件的過程,依然會(huì)被讀操作阻塞,也會(huì)阻塞讀操作。
  • WAL文件的大小設(shè)置很關(guān)鍵,過大的WAL文件,會(huì)讓查找操作從B-Tree查找退化成線性查找(WAL中page連續(xù)存儲(chǔ));但大的WAL文件對寫操作較友好。

2.3.1 結(jié)論

  • 只有開了WAL,再使用讀寫(連接)分離才能有較大的性能提升。
  • WAL本質(zhì)上是將部分隨機(jī)寫操作(數(shù)據(jù)庫文件和journal日志)變成了串行寫WAL文件,并進(jìn)行了鎖分離。
  • WAL文件的大小設(shè)置很關(guān)鍵,過大的WAL文件,會(huì)讓查找操作從B-Tree查找退化成線性查找(WAL中page連續(xù)存儲(chǔ));但大的WAL文件對寫操作較友好。

2.4 多線程設(shè)置

  • 多線程是sqlite使用過程中比較容易誤解的一個(gè)概念,帶來的問題要么是產(chǎn)生各種線程安全問題,要么是無法充分發(fā)掘sqlite的性能,這里結(jié)合代碼我們簡單分析一下并給出幾個(gè)重要結(jié)論。
  • 線程安全設(shè)置主要在設(shè)置bCoreMutex和bFullMutex,啟用bFullMutex之后數(shù)據(jù)庫連接和prepared statement都已加鎖(社區(qū)各種文檔都到此為止);但還是感覺不夠清晰:這兩個(gè)鎖會(huì)對我們使用sqlite有怎樣的影響?best practice又是什么?
  1. // 多線程的設(shè)置的實(shí)現(xiàn):設(shè)置bCoreMutex和bFullMutex 
  2.  
  3. #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0  /* IMP: R-54466-46756 */ 
  4.     case SQLITE_CONFIG_SINGLETHREAD: { 
  5.       /* EVIDENCE-OF: R-02748-19096 This option sets the threading mode to 
  6.       ** Single-thread. */ 
  7.       sqlite3GlobalConfig.bCoreMutex = 0;  /* Disable mutex on core */ 
  8.       sqlite3GlobalConfig.bFullMutex = 0;  /* Disable mutex on connections */ 
  9.       break; 
  10.     } 
  11. #endif 
  12. #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-20520-54086 */ 
  13.     case SQLITE_CONFIG_MULTITHREAD: { 
  14.       /* EVIDENCE-OF: R-14374-42468 This option sets the threading mode to 
  15.       ** Multi-thread. */ 
  16.       sqlite3GlobalConfig.bCoreMutex = 1;  /* Enable mutex on core */ 
  17.       sqlite3GlobalConfig.bFullMutex = 0;  /* Disable mutex on connections */ 
  18.       break; 
  19.     } 
  20. #endif 
  21. #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-59593-21810 */ 
  22.     case SQLITE_CONFIG_SERIALIZED: { 
  23.       /* EVIDENCE-OF: R-41220-51800 This option sets the threading mode to 
  24.       ** Serialized. */ 
  25.       sqlite3GlobalConfig.bCoreMutex = 1;  /* Enable mutex on core */ 
  26.       sqlite3GlobalConfig.bFullMutex = 1;  /* Enable mutex on connections */ 
  27.       break; 
  28.     } 
  29. #endif 
  • 如果FullMutex打開,則每個(gè)數(shù)據(jù)庫連接會(huì)初始化一個(gè)互斥量成員(db->mutex),也就是社區(qū)各種文檔上所說的“bFullMutex是對連接的線程保護(hù)”。
  1. if( isThreadsafe ){    // bFullMutex = 1 
  2.     db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);    // 每個(gè)數(shù)據(jù)庫連接會(huì)初始化一個(gè)成員鎖 
  3.     if( db->mutex==0 ){ 
  4.       sqlite3_free(db); 
  5.       db = 0; 
  6.       goto opendb_out; 
  7.     } 
  8.   } 

如果CoreMutex打開,則會(huì)設(shè)置全局的鎖控制函數(shù)。

  1. /* If the xMutexAlloc method has not been setthen the user did not 
  2.     ** install a mutex implementation via sqlite3_config() prior to  
  3.     ** sqlite3_initialize() being called. This block copies pointers to 
  4.     ** the default implementation into the sqlite3GlobalConfig structure. 
  5.     */ 
  6.     sqlite3_mutex_methods const *pFrom; 
  7.     sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex; 
  8.  
  9.     if( sqlite3GlobalConfig.bCoreMutex ){ 
  10.       pFrom = sqlite3DefaultMutex(); 
  11.     }else
  12.       pFrom = sqlite3NoopMutex(); 
  13.     } 
  14.     pTo->xMutexInit = pFrom->xMutexInit; 
  15.     pTo->xMutexEnd = pFrom->xMutexEnd; 
  16.     pTo->xMutexFree = pFrom->xMutexFree; 
  17.     pTo->xMutexEnter = pFrom->xMutexEnter; 
  18.     pTo->xMutexTry = pFrom->xMutexTry; 
  19.     pTo->xMutexLeave = pFrom->xMutexLeave; 
  20.     pTo->xMutexHeld = pFrom->xMutexHeld; 
  21.     pTo->xMutexNotheld = pFrom->xMutexNotheld; 
  22.     sqlite3MemoryBarrier(); 
  23.     pTo->xMutexAlloc = pFrom->xMutexAlloc; 
  • 而CoreMutext未打開的話,sqlite3NoopMutex()的實(shí)現(xiàn)如下(CoreMutext未打開的話,對應(yīng)使用的鎖函數(shù)均為空實(shí)現(xiàn)):
  1. sqlite3_mutex_methods const *sqlite3NoopMutex(void){ 
  2.   static const sqlite3_mutex_methods sMutex = { 
  3.     noopMutexInit, 
  4.     noopMutexEnd, 
  5.     noopMutexAlloc, 
  6.     noopMutexFree, 
  7.     noopMutexEnter, 
  8.     noopMutexTry, 
  9.     noopMutexLeave, 
  10.     0, 
  11.     0, 
  12.   }; 
  13.  
  14.   return &sMutex; 
  15.  
  16. // CoreMutext未打開的話,對應(yīng)使用的鎖函數(shù)均為空實(shí)現(xiàn) 
  17. static int noopMutexInit(void){ return SQLITE_OK; } 
  18. static int noopMutexEnd(void){ return SQLITE_OK; } 
  19. static sqlite3_mutex *noopMutexAlloc(int id){  
  20.   UNUSED_PARAMETER(id); 
  21.   return (sqlite3_mutex*)8;  
  22. static void noopMutexFree(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } 
  23. static void noopMutexEnter(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } 
  24. static int noopMutexTry(sqlite3_mutex *p){ 
  25.   UNUSED_PARAMETER(p); 
  26.   return SQLITE_OK; 
  27. static void noopMutexLeave(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } 
  • FullMutex保護(hù)了什么?

粗略看了一下,通過db->mutex(sqlite3_mutex_enter(db->mutex);)保護(hù)的邏輯塊和函數(shù)主要如下列表:

  1. sqlite3_db_status、sqlite3_finalize、sqlite3_reset、sqlite3_step、sqlite3_exec、 
  2. sqlite3_preppare_v2、column_name、blob操作、sqlite3Close、sqlite3_errmsg... 

基本覆蓋了所有的讀、寫、DDL、DML,也包括prepared statement操作;也就是說,在未打開FullMutex的情況下,在一個(gè)連接上的所有DB操作必須嚴(yán)格串行執(zhí)行,包括只讀操作。

  • CoreMutex保護(hù)了什么?

sqlite3中的mutex操作函數(shù),除了用于操作db->mutex這個(gè)成員之外,還主要用于以下邏輯塊(主要是影響數(shù)據(jù)庫所有連接的邏輯):

shm操作(index for wal)、內(nèi)存池操作、內(nèi)存緩存操作等。

2.4.1 結(jié)論

  • 多線程設(shè)置是決定DDL、DML、WAL(包括SHM)操作是否線程安全的設(shè)置。
  • 多線程設(shè)置與讀寫(連接)分離沒有任何關(guān)系,并不是實(shí)現(xiàn)讀寫(連接)分離的必要條件(很多人對這一點(diǎn)有誤解)。

3,性能優(yōu)化tips

3.1 合理使用事務(wù)

由#2.2的分析可知,寫操作會(huì)在RESERVED狀態(tài)下將數(shù)據(jù)更改、b-tree的更改、日志等寫入page cache,并最終flush到數(shù)據(jù)庫文件中;使用事務(wù)的話,只需要一次對DB文件的flush操作,同時(shí)也不會(huì)對其他連接的讀寫操作阻塞;對比以下兩種數(shù)據(jù)寫入方式(這里以統(tǒng)一存儲(chǔ)提供的API為例),實(shí)測耗時(shí)有十幾倍的差距(當(dāng)然對于頻繁的讀操作,使用事務(wù)可以減事務(wù)狀態(tài)的切換,也會(huì)有一點(diǎn)點(diǎn)性能提升):

  1. // batch insert in transaction with 1000000 records 
  2. // 
  3. AliDBExecResult* execResult = NULL
  4. _database->InTransaction([&]() -> bool {    // in transaction 
  5.   auto statement = _database->PrepareStatement("INSERT INTO table VALUES(?, ?)"); 
  6.   for (auto record : records) {    // bind 1000000 records 
  7.     // bind record 
  8.     ... 
  9.     ... 
  10.     statement->AddBatch(); 
  11.   } 
  12.   auto result = statement->ExecuteUpdate(); 
  13.   return result->is_success_; 
  14. }); 
  15.  
  16.  
  17. // batch insert with 1000000 records, no transaction 
  18. // 
  19. auto statement = _database->PrepareStatement("INSERT INTO table VALUES(?, ?)"); 
  20. for (auto record : records) {    // bind 1000000 records 
  21.   // bind record 
  22.   ... 
  23.   ... 
  24.   statement->ExecuteUpdate(); 

3.2 啟用WAL + 讀寫(連接)分離

啟用WAL之后,數(shù)據(jù)庫大部分寫操作變成了串行寫(對WAL文件的串行操作),對寫入性能提升有非常大的幫助;同時(shí)讀寫操作可以互相完全不阻塞(如#2.3所述)。上述兩點(diǎn)比較好的解釋了啟用WAL帶來的提升;同時(shí)推薦一個(gè)寫連接 + 多個(gè)讀連接的模型,如下圖所示: 

sqlite事務(wù)模型、性能優(yōu)化tips、常見誤區(qū)

3.2.1 讀寫連接分離的細(xì)節(jié)

  • 讀操作使用不同的連接并發(fā)執(zhí)行,可以完全避免由于顯式事務(wù)、寫操作之間的鎖競爭帶來的死鎖。
  • 所有的寫操作、顯式事務(wù)操作都使用同一個(gè)連接,且所有的寫操作、顯式事務(wù)操作都串行執(zhí)行。

可以完全避免由于顯式事務(wù)、寫操作之間的鎖競爭帶來的死鎖,如#2.2.1提到的死鎖的例子。

并發(fā)寫并不能有效的提高寫入效率,參考如下偽代碼,哪段執(zhí)行更快?

  1. // two transactions:  
  2. void Transaction_1() { 
  3.         connection_->Exec("BEGIN"); 
  4.       connection_->Exec("insert into table(value) values('xxxx')"); 
  5.       connection_->Exec("COMMIT"); 
  6.  
  7. void Transaction_2() { 
  8.         connection_->Exec("BEGIN"); 
  9.       connection_->Exec("insert into table(value) values('xxxx')"); 
  10.       connection_->Exec("COMMIT"); 
  11.  
  12. // code fragment 1: concurrent transaction 
  13. thread1.RunBlock([]() -> void { 
  14.       for (int i=0; i< 100000; i++) { 
  15.             Transaction_1(); 
  16.     } 
  17. }); 
  18.  
  19. thread2.RunBlock([]() -> void { 
  20.       for (int i=0; i< 100000; i++) { 
  21.             Transaction_2(); 
  22.     } 
  23. }); 
  24.  
  25. thread1.Join(); thread2.join(); 
  26.  
  27. // code fragment 2: serial transaction 
  28. for (int i=0; i< 100000; i++) { 
  29.   Transaction_1(); 
  30. for (int i=0; i< 100000; i++) { 
  31.   Transaction_2(); 

3.3 針對具體業(yè)務(wù)場景,設(shè)置合適的WAL SIZE

如#2.3提到,過大的WAL文件,會(huì)讓查找操作從B-Tree查找退化成線性查找(WAL中page連續(xù)存儲(chǔ));但大的WAL文件對寫操作較友好。對于大記錄的寫入操作,較大的wal size會(huì)有效提高寫入效率,同時(shí)不會(huì)影響查詢效率。

3.4 針對業(yè)務(wù)場景分庫分表

分庫分表可以有效提高數(shù)據(jù)操作的并發(fā)度;但同時(shí)過多的表會(huì)影響數(shù)據(jù)庫文件的加載速度。現(xiàn)在數(shù)據(jù)庫方向的很多研究包括Auto sharding, paxos consensus, 存儲(chǔ)和計(jì)算的分離等;Auto

application-awared optimization,Auto hardware-awared optimization,machine

learning based optimization也是不錯(cuò)的方向。

3.5 其他

包括WAL checkpoint策略、WAL size優(yōu)化、page size優(yōu)化等,均需要根據(jù)具體的業(yè)務(wù)場景設(shè)置。

4,常見問題 & 誤區(qū)

4.1 線程安全設(shè)置及誤區(qū)

sqlites configuration options: https://sqlite.org/c3ref/c_config_getmalloc.html

按照sqlite文檔,sqlite線程安全模式有以下三種:

SQLITE_CONFIG_SINGLETHREAD(單線程模式)

This option sets the threading mode to Single-thread. In other words, it disables all mutexing and puts SQLite into a mode where it can only be used by a single thread.

SQLITE_CONFIG_MULTITHREAD(多線程模式)

This option sets the threading mode to Multi-thread. In other words, it disables mutexing on database connection and prepared statement objects. The application is responsible for serializing access to database connections and prepared statements. But other mutexes are enabled so that SQLite will be safe to use in a multi-threaded environment as long as no two threads attempt to use the same database connection at the same time.

SQLITE_CONFIG_SERIALIZED(串行模式)

This option sets the threading mode to Serialized. In other words, this option enables all mutexes including the recursive mutexes on database connection and prepared statement objects. In this mode (which is the default when SQLite is compiled with SQLITE_THREADSAFE=1) the SQLite library will itself serialize access to database connections and prepared statements so that the application is free to use the same database connection or the same prepared statement in different threads at the same time.

4.1.1 誤區(qū)一:多線程模式是線程安全的

產(chǎn)生這個(gè)誤區(qū)主的主要原因是官方文檔里的最后一句話:

SQLite will be safe to use in a multi-threaded environment as long as no two threads attempt to use the same database connection at the same time.

但大家往往忽略了前面的一句話:

it disables mutexing on database connection and prepared statement objects

即對于單個(gè)連接的讀、寫操作,包括創(chuàng)建出來的prepared statement操作,都沒有線程安全的保護(hù)。也即在多線程模式下,對單個(gè)連接的操作,仍需要在業(yè)務(wù)層進(jìn)行鎖保護(hù)。

4.1.2 誤區(qū)二:多線程模式下,并發(fā)讀操作是安全的

關(guān)于這一點(diǎn),#2.4給出了具體的解釋;多線程模式下(SQLITE_CONFIG_MULTITHREAD)對prepared statement、connection的操作都不是線程安全的

4.1.3 誤區(qū)三:串行模式下,所有數(shù)據(jù)庫操作都是串行執(zhí)行

這個(gè)問題比較籠統(tǒng);即使在串行模式下,所有的數(shù)據(jù)庫操作仍需遵循事務(wù)模型;而事務(wù)模型已經(jīng)將數(shù)據(jù)庫操作的鎖進(jìn)行了非常細(xì)粒度的分離,串行模式的鎖也是在上層保證了事務(wù)模型的完整性。

4.1.4 誤區(qū)四:多線程模式性能最好,串行模式性能差

多線程模式下,仍需要業(yè)務(wù)上層進(jìn)行鎖保護(hù),串行模式則是在sqlite內(nèi)部進(jìn)行了鎖保護(hù);認(rèn)為多線程模式性能好的兄弟哪來的自信認(rèn)為業(yè)務(wù)層的鎖實(shí)現(xiàn)比sqlite內(nèi)部鎖實(shí)現(xiàn)性能更高?

 

責(zé)任編輯:武曉燕 來源: 云棲社區(qū)
相關(guān)推薦

2010-08-12 11:12:27

Flex誤區(qū)

2020-12-26 15:19:00

DevOps誤區(qū)開發(fā)

2024-03-01 12:19:00

接口性能優(yōu)化

2019-03-21 14:18:38

iOS開發(fā)優(yōu)化原因

2017-06-07 15:37:51

MySQLSQL性能優(yōu)化

2018-08-17 08:26:25

2009-11-10 14:18:46

2015-03-20 10:00:53

2011-03-21 11:14:21

Oracle性能調(diào)整

2019-09-04 08:13:53

MySQLInnodb事務(wù)系統(tǒng)

2014-10-08 10:37:41

SQLite

2012-05-16 09:29:25

JavaRailsJVM

2023-10-24 06:59:17

2009-02-27 13:33:47

性能優(yōu)化網(wǎng)絡(luò)性能分析工具

2021-11-15 10:50:52

Java線程池代碼

2020-07-10 17:40:01

人工智能網(wǎng)絡(luò)技術(shù)

2023-08-03 14:45:00

數(shù)字孿生

2021-06-27 17:35:54

DevSecOps網(wǎng)絡(luò)安全數(shù)據(jù)泄露

2025-02-20 09:27:46

2022-08-04 11:10:03

日志優(yōu)化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)