• <tr id="yyy80"></tr>
  • <sup id="yyy80"></sup>
  • <tfoot id="yyy80"><noscript id="yyy80"></noscript></tfoot>
  • 99热精品在线国产_美女午夜性视频免费_国产精品国产高清国产av_av欧美777_自拍偷自拍亚洲精品老妇_亚洲熟女精品中文字幕_www日本黄色视频网_国产精品野战在线观看 ?

    嵌入式數(shù)據(jù)庫(kù)SQLite上多版本并發(fā)控制的設(shè)計(jì)與實(shí)現(xiàn)

    2022-02-26 06:58:14景子奇鄒兆年
    計(jì)算機(jī)應(yīng)用 2022年1期
    關(guān)鍵詞:元組線程事務(wù)

    景子奇,鄒兆年

    (哈爾濱工業(yè)大學(xué)計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,哈爾濱 150001)

    0 引言

    SQLite[1]是當(dāng)今非常流行的輕量級(jí)開源關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)(Relational DataBase Management System,RDBMS)。與Oracle、MySQL、PostgreSQL 等主流RDMBS 不同,SQLite 是一個(gè)嵌入式RDBMS。嵌入式數(shù)據(jù)庫(kù)體積非常小,系統(tǒng)資源占用非常低,可以運(yùn)行于手機(jī)等終端設(shè)備,使用時(shí)不需要運(yùn)行獨(dú)立的數(shù)據(jù)庫(kù)引擎,可與應(yīng)用一同編譯[2]。SQLite 支持大部分SQL92 標(biāo)準(zhǔn),支持滿足ACID(Atomicity-Consistency-Isolation-Durability)特性(即原子性、一致性、隔離性和持久性)的事務(wù),可以運(yùn)行在Unix、Linux、Windows、MacOS 等諸多主流操作系統(tǒng)上,并支持C、Java、PHP、Tcl、C#等眾多計(jì)算機(jī)程序設(shè)計(jì)語(yǔ)言[3]。SQLite 是用ANSI C 語(yǔ)言編寫的,自身完全獨(dú)立,無(wú)需依賴任何外部函數(shù)庫(kù)。SQLite 的整個(gè)數(shù)據(jù)庫(kù)(包括數(shù)據(jù)庫(kù)模式定義、表和索引)都存儲(chǔ)在宿主機(jī)上的單一文件中,便于數(shù)據(jù)庫(kù)和應(yīng)用程序的遷移。由于上述優(yōu)點(diǎn),SQLite 目前已在移動(dòng)應(yīng)用開發(fā)中占據(jù)了相當(dāng)大的市場(chǎng)份額[4],并在氣象觀測(cè)[5]、礦業(yè)[6]、航空航天[7]等領(lǐng)域得到了廣泛應(yīng)用。

    SQLite 提供了對(duì)ACID 事務(wù)的完整支持。由于SQLite 的嵌入式特點(diǎn),SQLite 僅支持?jǐn)?shù)據(jù)庫(kù)級(jí)別的事務(wù)并發(fā),允許同一時(shí)刻有多個(gè)讀事務(wù)并發(fā)執(zhí)行,但任何時(shí)刻最多只允許一個(gè)寫事務(wù)執(zhí)行,并且讀事務(wù)和寫事務(wù)之間可能存在沖突。在事務(wù)并發(fā)控制方面,SQLite 采用了兩階段鎖(two-Phase Locking,2PL)并發(fā)控制協(xié)議,通過對(duì)操作系統(tǒng)提供的文件鎖進(jìn)行包裝,實(shí)現(xiàn)了數(shù)據(jù)庫(kù)粒度的鎖。此外,SQLite 提供了journal 日志機(jī)制,可用于故障恢復(fù),無(wú)需額外編寫故障恢復(fù)模塊[8]。SQLite 的并發(fā)控制方法任意時(shí)刻只允許有一個(gè)正在執(zhí)行的寫事務(wù),因此無(wú)法實(shí)現(xiàn)多個(gè)寫事務(wù)的并發(fā)執(zhí)行。另一方面,由于鎖的粒度為數(shù)據(jù)庫(kù)級(jí),因此即使兩個(gè)并發(fā)執(zhí)行的讀寫事務(wù)分別讀寫不同的數(shù)據(jù)表,它們?nèi)匀粫?huì)發(fā)生沖突。

    多版本并發(fā)控制(Multi-Version Concurrency Control,MVCC)[9]是當(dāng)今RDBMS 中非常流行的事務(wù)管理方法。在使用MVCC 的RDBMS 中,關(guān)系的每個(gè)元組都有多個(gè)版本(version),每個(gè)版本帶有一個(gè)時(shí)間戳(timestamp),版本越新,其時(shí)間戳越大。MVCC 實(shí)現(xiàn)了并發(fā)事務(wù)之間的快照隔離(snapshot isolation),保證每個(gè)事務(wù)在讀數(shù)據(jù)庫(kù)時(shí)讀到的是事務(wù)開始時(shí)的數(shù)據(jù)庫(kù)實(shí)例(即快照)。因此,在支持MVCC 的RDBMS 中不存在讀寫沖突(read-write conflict),即當(dāng)一個(gè)寫事務(wù)和一個(gè)讀事務(wù)并發(fā)執(zhí)行時(shí),讀事務(wù)會(huì)讀取它應(yīng)該讀的數(shù)據(jù)庫(kù)版本,寫事務(wù)會(huì)寫入新版本的元組,因此兩個(gè)讀寫事務(wù)之間不存在沖突。盡管在支持MVCC 的RDBMS 中不存在讀寫沖突,但寫寫沖突(write-wirte conflict)仍然存在。

    為了提高SQLite 處理并發(fā)讀事務(wù)和寫事務(wù)的能力,本文設(shè)計(jì)并實(shí)現(xiàn)了SQLite 上的MVCC。該設(shè)計(jì)利用SQLite 現(xiàn)有的鎖機(jī)制與執(zhí)行結(jié)構(gòu),通過修改數(shù)據(jù)庫(kù)文件頭與記錄格式來(lái)確定數(shù)據(jù)的可見性,并改寫SQLite 內(nèi)置虛擬機(jī)的操作序列完成MVCC 的執(zhí)行。在大量數(shù)據(jù)上進(jìn)行了實(shí)驗(yàn),驗(yàn)證了在SQLite 上實(shí)現(xiàn)MVCC 可以有效提高數(shù)據(jù)庫(kù)上讀寫事務(wù)的并發(fā)性。

    1 預(yù)備知識(shí)

    1.1 SQLite的體系結(jié)構(gòu)

    與MySQL、PostgreSQL 等主流RDMBS 不同,SQLite 具有一個(gè)精致的、模塊化的體系結(jié)構(gòu),并采用了一些獨(dú)特的方法來(lái)管理關(guān)系數(shù)據(jù)庫(kù)。SQLite 包含3 個(gè)子系統(tǒng)和8 個(gè)獨(dú)立的模塊,這些模塊可以分為兩部分:前端解析系統(tǒng)和后端處理引擎。SQLite 的體系結(jié)構(gòu)如圖1 所示。

    圖1 SQLite的體系結(jié)構(gòu)Fig.1 Architecture of SQLite

    SQLite 的前端部分由分詞器、語(yǔ)法分析器和代碼生成器構(gòu)成,作用是將SQL 語(yǔ)句轉(zhuǎn)換為SQLite 虛擬機(jī)上可以執(zhí)行的字節(jié)碼(opcode)序列。SQLite 的后端主要負(fù)責(zé)處理SQLite 數(shù)據(jù)庫(kù)文件的存儲(chǔ)與讀寫,主要由B-樹、頁(yè)緩存(pager)和操作系統(tǒng)接口三部分構(gòu)成,其中B-樹的作用是對(duì)文件頁(yè)面中的元組進(jìn)行索引;頁(yè)緩存的作用是通過操作系統(tǒng)接口在B-樹和磁盤之間傳遞頁(yè)面。

    SQLite 的核心是虛擬機(jī)VDBE(Virtual DataBase Engine)。在SQLite 執(zhí) 行SQL 語(yǔ)句時(shí)SQLite 的前端 首先將SQL 語(yǔ)句轉(zhuǎn)換為虛擬機(jī)上可以執(zhí)行的字節(jié)碼序列,然后啟動(dòng)虛擬機(jī)執(zhí)行字節(jié)碼,完成SQL 語(yǔ)句的執(zhí)行。在執(zhí)行字節(jié)碼的過程中,虛擬機(jī)通過SQLite 后端的B-樹模塊讀寫數(shù)據(jù)庫(kù)中的元組。

    SQLite 支持ACID 事務(wù),它使用庫(kù)級(jí)鎖來(lái)協(xié)調(diào)并發(fā)事務(wù)。SQLite 支 持4 種類型的鎖:SHARED 鎖、RESERVED 鎖、PENDING 鎖和EXCLUSIVE 鎖。表1 展示了這4 種類型鎖的相容性。在事務(wù)執(zhí)行過程中,讀操作在讀數(shù)據(jù)庫(kù)對(duì)象時(shí)須獲得SHARED 鎖,寫操作在修改未提交時(shí)須獲得RESERVED 鎖來(lái)阻止其他寫事務(wù)啟動(dòng)。寫事務(wù)在內(nèi)存中完成寫操作時(shí),必須先獲取PENDING 鎖來(lái)阻止新的讀事務(wù)啟動(dòng),并在所有已啟動(dòng)的讀事務(wù)提交后獲取EXCLUSIVE鎖,將修改寫入磁盤。

    表1 SQLite中不同類型鎖之間的相容性Tab.1 Compatibility of mutex in SQLite

    1.2 多版本并發(fā)控制

    多版本并發(fā)控制(MVCC)提出于20 世紀(jì)70 年代末,盡管時(shí)間久遠(yuǎn),但仍是當(dāng)前最流行的事務(wù)管理方案,幾乎被當(dāng)今所有RDBMS 采用。

    1.2.1 MVCC協(xié)議

    MVCC 協(xié)議保證事務(wù)訪問到的數(shù)據(jù)庫(kù)內(nèi)容等同于該事務(wù)啟動(dòng)時(shí)已提交的數(shù)據(jù)庫(kù)內(nèi)容(即一致性快照)。因此,MVCC 允許一個(gè)事務(wù)T在另一個(gè)并發(fā)事務(wù)T′更新對(duì)象X 時(shí)讀取X 的舊版本,使這兩個(gè)X 上的讀寫操作不沖突。盡管MVCC 避免了讀寫沖突,但并發(fā)事務(wù)的寫寫沖突仍然存在,需要采取特殊的手段來(lái)解決。根據(jù)解決手段的不同,MVCC可分為使用事務(wù)標(biāo)識(shí)符(TID)標(biāo)注元組可用性的MVTO(Multi-Version Timestamp Ordering)、使用樂觀鎖協(xié)議的MVOCC(Multi-Version Optimistic Concurrency Control)[10]和使用兩階段鎖協(xié)議的MV2PL(Multi-Version Two-Phase Locking)。圖2 展示了三種協(xié)議下元組的格式。

    圖2 三種MVCC協(xié)議下元組的格式Fig.2 Tuple formats under three MVCC protocols

    MVTO 是最初的MVCC,它使用事務(wù)的TID 來(lái)預(yù)計(jì)算事務(wù)的序列化順序。當(dāng)事務(wù)T 對(duì)一條邏輯元組執(zhí)行讀操作時(shí),DBMS 將搜索TID 位于元組begin-ts 和end-ts 字段范圍之間的版本,MVTO 從不允許事務(wù)讀取未提交的版本。在MVTO中,事務(wù)執(zhí)行寫操作先需要持有寫鎖(鎖有版本號(hào)),并將元組的txn-id 設(shè)置為事務(wù)的TID,且總是在元組的最新版本上進(jìn)行寫操作。

    在MVOCC 中,事務(wù)在執(zhí)行讀寫操作時(shí)無(wú)需獲得鎖,而是在提交前進(jìn)行驗(yàn)證,驗(yàn)證通過后將更新過的對(duì)象寫入磁盤。在MVOCC 中,事務(wù)的執(zhí)行過程分為讀、驗(yàn)證和寫入3 個(gè)階段,僅在寫入時(shí)短暫持有鎖。

    MVP2L 使用2PL 來(lái)解決寫寫沖突,同時(shí)用read-cnt 表示同時(shí)在讀該版本的事務(wù)數(shù)量,將read-cnt 視為共享鎖,txn-id視為排他鎖。

    MVTO 和MV2PL 由于在讀元組時(shí)修改了元組的頭部,可能會(huì)使其他事務(wù)中止,而MVOCC 不會(huì)使事務(wù)中止,但是驗(yàn)證和回滾代價(jià)較大。為了解決這個(gè)問題,有研究將事務(wù)使用鏈表串聯(lián)起來(lái),并根據(jù)時(shí)間戳進(jìn)行快速驗(yàn)證[11]。

    1.2.2 數(shù)據(jù)版本的存儲(chǔ)與檢索

    MVCC 設(shè)計(jì)中另一個(gè)重要的問題是數(shù)據(jù)版本的存儲(chǔ)與檢索。在版本存儲(chǔ)方面,主要有Append-only、Time-travel 和Delta 三種方法[12]。

    Append-only 方法將元組的所有版本都存儲(chǔ)在同一個(gè)存儲(chǔ)空間中,并用單向鏈表將元組的不同版本連接起來(lái)。根據(jù)單向鏈表的方向是從新版本到舊版本,還是從舊版本到新版本,又可將該方法分為N2O(Newest-To-Oldest)方法和O2N(Oldest-To-Newest)方 法。Append-only 方法已用于PostgresSQL 等數(shù)據(jù)庫(kù)管理系統(tǒng)[13]中。

    Time-Travel 方法將舊版本存放在單獨(dú)的表中,主表中只存儲(chǔ)最新版本[14]。該方法好處是索引始終指向元組的最新版本,省去了事務(wù)在更新元組時(shí)維護(hù)數(shù)據(jù)庫(kù)索引的開銷,并且非常適合于那些需要訪問元組最新版本的查詢。

    Delta 方法僅在額外的存儲(chǔ)空間中存儲(chǔ)元組版本的變化,這樣做能夠降低寫事務(wù)的開銷,但讀事務(wù)需要通過計(jì)算才能得到所需版本的元組,開銷較大,不適合于讀密集型事務(wù)。作為MySQL 默認(rèn)存儲(chǔ)引擎的InnoDB 便采取這種做法[15]。

    1.2.3 垃圾回收

    存儲(chǔ)各個(gè)版本的數(shù)據(jù)需要消耗大量存儲(chǔ)空間,必須在存儲(chǔ)資源耗盡前將無(wú)用的版本刪除,以回收存儲(chǔ)空間。垃圾回收的過程分為3 步:1)檢測(cè)無(wú)用的過期版本,其中過期版本是指無(wú)效的版本(即由中止事務(wù)創(chuàng)建的版本)或?qū)θ魏位钴S事務(wù)都不可見的版本。2)刪除過期的版本以及和它們有關(guān)的關(guān)聯(lián)鏈和索引鏈接。3)回收存儲(chǔ)空間。

    垃圾回收又分為元組級(jí)垃圾回收和事務(wù)級(jí)垃圾回收[12]。元組級(jí)垃圾回收是在執(zhí)行查詢時(shí)會(huì)定期掃描過期的元組版本,刪除這些版本,從而實(shí)現(xiàn)垃圾回收。事務(wù)級(jí)垃圾回收則是按照事務(wù)的維度,要求記錄r/w set,這樣可以把不用的事務(wù)所創(chuàng)建的版本都過濾掉[16]。

    1.2.4 索引

    索引可以使用邏輯指針和物理指針兩種方式實(shí)現(xiàn)。物理指針只適用于append-only 版本存儲(chǔ)方法,因?yàn)樵摲椒▽⑷堪姹敬鎯?chǔ)在同一個(gè)表中,所有索引都可以指向這些版本。當(dāng)更新表中的元組時(shí),DBMS 會(huì)將新創(chuàng)建的版本插入到所有二級(jí)索引中。在這種方式下,DBMS 可以在二級(jí)索引上查找元組,而無(wú)需將次關(guān)鍵字與所有索引版本進(jìn)行比較。MemSQL 和Hekaton 等多個(gè)支持MVCC 的DBMS 都采用了這種方案。使用邏輯指針的主要思想是使用固定的邏輯標(biāo)識(shí)符來(lái)標(biāo)識(shí)元組,該標(biāo)識(shí)符對(duì)于索引中的相同元組是不變的。DBMS 只需使用一個(gè)間接層,將元組的標(biāo)識(shí)符映射到其版本鏈的頭部。該方法的優(yōu)點(diǎn)是當(dāng)元組被修改時(shí),不必更新所有索引,使索引項(xiàng)指向新的物理位置。該方法只需修改間接層中的映射條目即可。但是由于索引沒有指向確切的版本,DBMS 必須從元組版本鏈的頭部開始遍歷版本鏈從而找到可見的版本,查找效率較低,但比較適合于寫密集型事務(wù)。

    1.3 事務(wù)的隔離級(jí)別

    不同的事務(wù)同時(shí)對(duì)一個(gè)數(shù)據(jù)庫(kù)進(jìn)行操作便構(gòu)成了并發(fā),并發(fā)事務(wù)的操作交叉執(zhí)行,可能會(huì)相互干擾,影響事務(wù)之間的隔離性。根據(jù)事務(wù)中的操作是否修改了數(shù)據(jù)庫(kù)的內(nèi)容,事務(wù)被分為讀事務(wù)與寫事務(wù)。如果數(shù)據(jù)庫(kù)上僅執(zhí)行讀事務(wù),則讀事務(wù)之間不會(huì)相互干擾;然而,當(dāng)寫事務(wù)與其他事務(wù)同時(shí)執(zhí)行時(shí),則可能會(huì)出現(xiàn)一些異常問題。解決這些異常問題的方法稱為并發(fā)控制。人們總結(jié)出了事務(wù)并發(fā)執(zhí)行時(shí)產(chǎn)生的幾種常見的異常問題:臟讀、不可重復(fù)讀和幻讀。

    臟讀(dirty read)是指事務(wù)讀到了其他未提交事務(wù)寫的數(shù)據(jù)。若未提交的事務(wù)進(jìn)行了回滾,則引發(fā)了臟讀。不可重復(fù)讀(unrepeatable read)是指如果事務(wù)已讀過的數(shù)據(jù)被其他事務(wù)修改了,該事務(wù)再次讀該數(shù)據(jù)時(shí)與前一次讀到的內(nèi)容不一致?;米x(phantom read)是指當(dāng)事務(wù)多次執(zhí)行相同的查詢時(shí),后面操作因涉及其他事務(wù)寫入的新數(shù)據(jù),導(dǎo)致相同的查詢返回不同的結(jié)果。

    根據(jù)上述3 種異常問題是否會(huì)在事務(wù)并發(fā)執(zhí)行過程中出現(xiàn),可將事務(wù)的隔離性劃分成如表2 所示的不同級(jí)別。并發(fā)控制的設(shè)計(jì)目標(biāo)就是在保證一定的隔離級(jí)別的前提下盡可能提高系統(tǒng)的并發(fā)性能[17]。

    表2 不同隔離級(jí)別的劃分依據(jù)Tab.2 Division rules of different isolation levels

    2 SQLite多版本并發(fā)控制的設(shè)計(jì)

    本章介紹本文提出的SQLite 上的MVCC 設(shè)計(jì)方案。

    2.1 設(shè)計(jì)目標(biāo)

    本節(jié)從多個(gè)方面分析SQLite 的設(shè)計(jì)方案,并提出SQLite上MVCC 的設(shè)計(jì)目標(biāo)。

    1)在數(shù)據(jù)庫(kù)存儲(chǔ)方面,為了便于數(shù)據(jù)庫(kù)和應(yīng)用程序的遷移,SQLite 將整個(gè)數(shù)據(jù)庫(kù)存儲(chǔ)在單個(gè)文件中。在設(shè)計(jì)SQLite的MVCC 時(shí)仍將數(shù)據(jù)庫(kù)存儲(chǔ)在單個(gè)文件中,并沿用SQLite 的文件組織方式,即一切數(shù)據(jù)頁(yè)均為B 樹節(jié)點(diǎn)。MVCC 中,需要存儲(chǔ)一個(gè)元組的不同版本。在本設(shè)計(jì)中將一個(gè)表中的新舊版本的元組存儲(chǔ)在該表中。

    2)在并發(fā)控制方面,出于移植性的考慮,SQLite 的鎖機(jī)制基于文件鎖實(shí)現(xiàn),其粒度為庫(kù)級(jí)。一旦數(shù)據(jù)庫(kù)被某個(gè)事務(wù)加上了排他鎖,則整個(gè)數(shù)據(jù)庫(kù)無(wú)法被任何其他事務(wù)并發(fā)訪問。為保證可移植性,在進(jìn)行設(shè)計(jì)MVCC 時(shí)應(yīng)沿用SQLite 的鎖機(jī)制,故多個(gè)寫事務(wù)無(wú)法并發(fā)執(zhí)行。因此,本文的MVCC設(shè)計(jì)方案只支持讀事務(wù)與讀事務(wù)的并發(fā)執(zhí)行以及讀事務(wù)與寫事務(wù)的并發(fā)執(zhí)行,而不支持多個(gè)寫事務(wù)的并發(fā)執(zhí)行。

    3)與其他RDBMS 不同,SQLite 并不記錄系統(tǒng)內(nèi)的活躍(active)事務(wù),因此無(wú)法從數(shù)據(jù)庫(kù)的角度知道系統(tǒng)內(nèi)有哪些事務(wù)正在執(zhí)行。這使得無(wú)法根據(jù)活躍事務(wù)來(lái)劃分?jǐn)?shù)據(jù)庫(kù)版本,也無(wú)法通過為事務(wù)分配自增ID 的方式進(jìn)行版本控制。由于本文的MVCC 設(shè)計(jì)中不考慮寫-寫事務(wù)的并發(fā),因此數(shù)據(jù)庫(kù)在邏輯上被已提交的寫事務(wù)劃分為不同的獨(dú)立狀態(tài),可以使用已提交的寫事務(wù)將數(shù)據(jù)庫(kù)劃分為不同版本,并用單調(diào)遞增的版本號(hào)進(jìn)行標(biāo)記。下文中提到的版本號(hào)均指以該方法指定的版本號(hào)。

    4)在隔離性方面,SQLite 支持的隔離級(jí)別為可串行化(serializable)與讀未提交(read uncommitted),其中讀未提交隔離級(jí)別僅提供給同一進(jìn)程內(nèi)不同線程間的并發(fā)事務(wù)。在本文的MVCC 設(shè)計(jì)中,只考慮可串行化隔離級(jí)別。

    2.2 MVCC協(xié)議

    本節(jié)介紹SQLite 上MVCC 協(xié)議的具體設(shè)計(jì)。

    數(shù)據(jù)庫(kù)版本的表示 根據(jù)2.1 節(jié)介紹的設(shè)計(jì)思路,本設(shè)計(jì)中根據(jù)已提交的寫事務(wù)將數(shù)據(jù)庫(kù)劃分成不同的版本。數(shù)據(jù)庫(kù)的初始版本號(hào)為0。每當(dāng)一個(gè)寫事務(wù)提交,數(shù)據(jù)庫(kù)的版本號(hào)增加1。在實(shí)現(xiàn)中在SQLite 數(shù)據(jù)庫(kù)文件的頭部記錄數(shù)據(jù)庫(kù)當(dāng)前的版本號(hào)。每個(gè)版本的數(shù)據(jù)庫(kù)由該版本的全體元組構(gòu)成。為了記錄元組的版本,在每條元組的頭部增加了3 個(gè)字段:T_START、T_END 和CID,其中T_START 代表該元組創(chuàng)建時(shí)數(shù)據(jù)庫(kù)的版本號(hào),T_END 代表該元組結(jié)束時(shí)數(shù)據(jù)庫(kù)的版本號(hào),CID 代表該元組版本在同一事務(wù)中被創(chuàng)建的順序。

    數(shù)據(jù)可見性規(guī)則 由于數(shù)據(jù)庫(kù)存在多個(gè)版本,MVCC 規(guī)定一個(gè)事務(wù)只能看見該事務(wù)啟動(dòng)前最后一個(gè)寫事務(wù)提交后數(shù)據(jù)庫(kù)的狀態(tài)(即一致性快照)。為了實(shí)現(xiàn)上述可見性,基于上面介紹的數(shù)據(jù)庫(kù)版本表示方法定義了如下數(shù)據(jù)可見性規(guī)則。設(shè)t是數(shù)據(jù)庫(kù)中的一條元組,使用T_START(t)、T_END(t)和CID(t)分別表示t的T_START、T_END 和CID 字段的值。設(shè)T是一個(gè)事務(wù),使用TID(T)表示T啟動(dòng)時(shí)數(shù)據(jù)庫(kù)的版本號(hào)。對(duì)于T中的第i個(gè)操作,根據(jù)以下3 條規(guī)則來(lái)判定元組t是否對(duì)該操作可見。

    規(guī)則1 如果T是讀事務(wù)且T_START(t)≤TID(T)≤T_END(t),則t對(duì)T的第i個(gè)操作可見。

    規(guī)則2 如果T是寫事務(wù)且T_START(t)≤TID(T)<T_END(t),則t對(duì)T的第i個(gè)操作可見。

    規(guī)則3 如果T是寫事務(wù)且T_START(t)=TID(T)+1,TID(T)<T_END(t),CID(t)<i,則t對(duì)T的第i個(gè)操作可見。

    只要上述規(guī)則之一滿足,則t對(duì)T的第i個(gè)操作可見。

    2.3 數(shù)據(jù)更新操作

    在MVCC 協(xié)議下,數(shù)據(jù)更新不再是SQLite 采用的原地更新(in-place update),而是維護(hù)數(shù)據(jù)的版本。下面介紹MVCC協(xié)議下的數(shù)據(jù)更新操作。

    插入(INSERT)操作 INSERT 操作用于向數(shù)據(jù)庫(kù)中插入新的元組。設(shè)事務(wù)T的第i個(gè)操作準(zhǔn)備插入一條新元組,該元組的全部屬性值為val,T的TID 為TID(T)。該操作將在表中插入一條新的元組t,其中T_START(t)=TID(T)+1,T_END(t)=∞,CID=i,t的屬性值為val。根據(jù)2.2 節(jié)介紹的可見性規(guī)則,新插入的元組t對(duì)事務(wù)T后續(xù)操作可見。圖3 中展示了INSERT 操作的過程,其中執(zhí)行INSERT 操作的事務(wù)的TID 為1,該INSERT 操作是該事務(wù)的第0 個(gè)操作,新插入元組的屬性值為b。

    圖3 INSERT操作的執(zhí)行過程Fig.3 Execution process of INSERT operation

    刪除(DELETE)操作 DELETE 操作用于刪除數(shù)據(jù)庫(kù)中的元組。在MVCC 下,該操作不會(huì)在物理上刪除元組,而是修改該元組可見版本的T_END 字段,設(shè)置其可見的結(jié)束時(shí)間。設(shè)事務(wù)T準(zhǔn)備刪除一條元組,t是T當(dāng)前可見的元組版本,T的TID 為TID(T)。DELETE 操作將T_END(t)設(shè)置為TID(T)。根據(jù)2.2 節(jié)介紹的可見性規(guī)則,t對(duì)T不再可見,從而在邏輯上刪除了t,然而對(duì)于某些早于T啟動(dòng)的事務(wù),t仍然可見。圖4 展示了DELETE 操作的過程,其中執(zhí)行DELETE 操作的事務(wù)的TID 為2,rowid=0 的元組是該事務(wù)可見的待刪除元組版本。

    圖4 DELETE操作的執(zhí)行過程Fig.4 Execution process of DELETE operation

    更新(UPDATE)操作 UPDATE 操作用于修改數(shù)據(jù)庫(kù)中的元組。在MVCC 下,該操作不會(huì)原地修改元組,而是先執(zhí)行DELETE 操作,刪除該元組;再執(zhí)行INSERT 操作,插入更新后的元組。圖5 展示了UPDATE 操作的過程。設(shè)執(zhí)行UPDATE 操作的事務(wù)的TID 為2,該INSERT 操作是該事務(wù)的第1 個(gè)操作,該事務(wù)準(zhǔn)備將rowid=0 的元組的值a修改為b。該事務(wù)首先執(zhí)行DELETE 操作,將rowid=0 的元組的T_END字段設(shè)置為2;然后執(zhí)行INSERT 操作,插入rowid=2 的元組。

    圖5 UPDATE操作的執(zhí)行過程Fig.5 Execution process of UPDATE operation

    2.4 事務(wù)的執(zhí)行流程

    由于本文的MVCC 設(shè)計(jì)允許讀-寫事務(wù)并發(fā),但不允許寫-寫事務(wù)并發(fā),因此需要對(duì)SQLite 的事務(wù)執(zhí)行流程進(jìn)行重新設(shè)計(jì)。圖6 展示了MVCC 下一個(gè)事務(wù)的執(zhí)行流程(這里未使用檢查點(diǎn))。在事務(wù)執(zhí)行過程中,系統(tǒng)使用SHARED 與RESERVED 兩種類型的鎖來(lái)控制并發(fā)。寫事務(wù)通過持有RESERVED 鎖來(lái)阻止其他寫事務(wù)的執(zhí)行,但并不會(huì)影響讀事務(wù)的啟動(dòng)和提交。

    圖6 事務(wù)的執(zhí)行流程Fig.6 Flowchart of transaction execution

    事務(wù)的提交發(fā)生在commit 或end 語(yǔ)句執(zhí)行時(shí)。如果提交的事務(wù)為寫事務(wù),則SQLite 會(huì)將該事務(wù)修改過的數(shù)據(jù)寫入數(shù)據(jù)庫(kù)文件中。如2.3 節(jié)所述,寫事務(wù)在執(zhí)行寫操作時(shí)需要用到該事務(wù)的TID。在寫數(shù)據(jù)庫(kù)文件時(shí)需要判斷當(dāng)前寫事務(wù)的TID 是否已經(jīng)被其他已提交的事務(wù)使用過。進(jìn)行該檢查的理由如下:即使當(dāng)前寫事務(wù)已經(jīng)獲得了RESERVED 鎖,使得其他事務(wù)無(wú)法升級(jí)到寫事務(wù),然而在該寫事務(wù)提交后,其他訪問該版本數(shù)據(jù)的事務(wù)仍可以獲得RESERVED 鎖并嘗試提交。如果這種情況發(fā)生,兩個(gè)提交的寫事務(wù)提供的TID可能相同,因而無(wú)法正確劃分?jǐn)?shù)據(jù)版本。由于上述原因,在寫事務(wù)提交前必須進(jìn)行版本檢查,只有寫事務(wù)的TID 大于當(dāng)前已提交的數(shù)據(jù)庫(kù)版本,寫事務(wù)才能成功提交,否則寫事務(wù)回滾(rollback)。該方法可以保證數(shù)據(jù)庫(kù)版本的唯一性。

    2.5 并發(fā)事務(wù)的隔離級(jí)別

    基于2.2~2.4 節(jié)的內(nèi)容,給出本文提出的MVCC 設(shè)計(jì)方案下并發(fā)事務(wù)的隔離級(jí)別的分析。

    定理1在本文提出的MVCC 設(shè)計(jì)方案下并發(fā)事務(wù)的隔離級(jí)別為可串行化(serializable)。

    證明 根據(jù)可串行化隔離級(jí)別的定義,只需證明基于本文的MVCC 設(shè)計(jì),并發(fā)事務(wù)不會(huì)產(chǎn)生臟讀(dirty read)、不可重復(fù)讀(unrepeatable read)和幻讀(phantom read)。

    1)無(wú)臟讀。由SQLite 事務(wù)模型可知,未提交的事務(wù)不會(huì)將任何修改過的數(shù)據(jù)寫入數(shù)據(jù)庫(kù)文件中,因此任何事務(wù)讀到的數(shù)據(jù)一定是已提交事務(wù)寫入的版本,故不會(huì)產(chǎn)生臟讀。

    2)無(wú)不可重復(fù)讀。設(shè)t是事務(wù)T1 可見的元組版本,根據(jù)可見性規(guī)則,有T_START(t)≤TID(T1)≤T_END(t)。設(shè)事務(wù)T2 對(duì)t進(jìn)行了修改,則根據(jù)2.3 節(jié)介紹的UPDATE 操作,T2首先將T_END(t)設(shè)置為TID(T2),其中TID(T1)≤TID(T2)。根據(jù)2.2 節(jié)中的可見性規(guī)則,t仍然對(duì)T1 可見。然后,T2 插入一個(gè)新的元組版本t′,且T_START(t′)=TID(T2)+1。根據(jù)2.2 節(jié)中的可見性規(guī)則,t′對(duì)T1 不可見。T1 可見的仍然是t,故不會(huì)產(chǎn)生不可重復(fù)讀。

    3)無(wú)幻讀。由于系統(tǒng)使用了庫(kù)級(jí)鎖,同一時(shí)刻只允許一個(gè)寫事務(wù)執(zhí)行,因此寫事務(wù)插入的新元組的版本號(hào)一定大于當(dāng)先所有讀事務(wù)的TID。由可見性規(guī)則1 可知,這些新元組對(duì)任何讀事務(wù)均不可見,因此不會(huì)發(fā)生幻讀。

    2.6 索引

    索引用于對(duì)表中的元組進(jìn)行快速查找。如果表被更新,則該表上的索引也要被更新。SQLite 中索引與表的格式相同,因此在MVCC 下,B+樹索引葉節(jié)點(diǎn)中的索引項(xiàng)也記錄著對(duì)應(yīng)元組的T_START、T_END 和CID 字段的值。下面介紹如何在MVCC 下對(duì)索引進(jìn)行查找和更新。

    查找索引項(xiàng) 在索引上進(jìn)行查找時(shí),除了根據(jù)給定的索引鍵值進(jìn)行查找外,還需要對(duì)查找到的索引項(xiàng)進(jìn)行可見性檢查,確保事務(wù)可見的索引與事務(wù)可見的數(shù)據(jù)庫(kù)一致性快照是一致的。值得注意的是,在一個(gè)事務(wù)查找索引項(xiàng)的過程中,另一個(gè)事務(wù)可能同時(shí)更新了索引,導(dǎo)致B 樹葉節(jié)點(diǎn)發(fā)生分裂。這有可能導(dǎo)致葉節(jié)點(diǎn)中信息與緩存中的父節(jié)點(diǎn)中的信息不一致,從而查不到索引項(xiàng)。為了解決該問題,需要保證在查找索引項(xiàng)的過程中B 樹頁(yè)面的版本沒有發(fā)生變化。如果B 樹頁(yè)面的版本發(fā)生了變化,則需要終止本次索引查找并重新啟動(dòng)索引查找操作。

    插入索引項(xiàng) 當(dāng)一條新元組被插入表中時(shí),需要向該表上每個(gè)索引中插入相應(yīng)的索引項(xiàng)。索引項(xiàng)的T_START、T_END 和CID 字段的值分別與新元組這3 個(gè)字段的值相同。

    刪除索引項(xiàng) 當(dāng)表中元組被刪除時(shí),索引中與該元組對(duì)應(yīng)的索引項(xiàng)并不會(huì)被物理上刪除,而是修改該索引項(xiàng)的T_END 字段,以改變?cè)撍饕?xiàng)的可見性。修改方法與2.3 節(jié)介紹的DELETE 操作修改元組T_END 字段的方法相同。

    更新索引項(xiàng) 如2.3 節(jié)所述,當(dāng)更新表中元組時(shí),采取先執(zhí)行DELETE 操作再執(zhí)行INSERT 操作的方式。相應(yīng)地,還要在每個(gè)索引上先刪除索引項(xiàng),再插入更新后的索引項(xiàng)。刪除和插入索引項(xiàng)的方法均已在前面介紹過。

    2.7 舊版本數(shù)據(jù)回收

    當(dāng)數(shù)據(jù)庫(kù)長(zhǎng)時(shí)間運(yùn)行后,會(huì)積累大量舊版本數(shù)據(jù)。舊版本數(shù)據(jù)對(duì)新啟動(dòng)的事務(wù)不可見,因此不會(huì)被新事務(wù)訪問。然而,所有版本的數(shù)據(jù)均存儲(chǔ)于同一個(gè)SQLite 數(shù)據(jù)庫(kù)文件中,這會(huì)導(dǎo)致文件過大,嚴(yán)重降低系統(tǒng)性能。因此,MVCC 設(shè)計(jì)必須為SQLite 設(shè)計(jì)舊版本數(shù)據(jù)回收方法。

    為SQLite 設(shè)計(jì)舊版本數(shù)據(jù)回收方法主要面臨兩個(gè)困難:1)現(xiàn)有基于MVCC 的RDBMS 采用多種方法(如版本鏈、undo日志)來(lái)記錄元組版本之間的關(guān)系,但受限于SQLite 的現(xiàn)有設(shè)計(jì),無(wú)法在不大量改變SQLite 的前提下采取類似的方法來(lái)加速查詢符合當(dāng)前事務(wù)可見性的數(shù)據(jù)。2)SQLite 并不記錄系統(tǒng)內(nèi)的活躍事務(wù),因此無(wú)法從數(shù)據(jù)庫(kù)的角度判斷元組的某個(gè)版本是否還在被某個(gè)活躍的讀事務(wù)讀取,因而無(wú)法主動(dòng)清除數(shù)據(jù)庫(kù)中的舊版本數(shù)據(jù)。

    設(shè)計(jì)提供了下面的舊版本數(shù)據(jù)回收方法。首先,SQLite是一種嵌入式數(shù)據(jù)庫(kù),可以和應(yīng)用程序的代碼一起編譯,用戶可以在應(yīng)用程序中記錄系統(tǒng)內(nèi)的活躍事務(wù)。然后,當(dāng)系統(tǒng)內(nèi)沒有活躍事務(wù)時(shí),SQLite 可以啟動(dòng)舊版本數(shù)據(jù)清理。具體過程如下:首先,阻止任何新事務(wù)啟動(dòng)。然后,打開數(shù)據(jù)庫(kù)中所有的表,逐條掃描表中每條元組,使用所有最新版本的可見數(shù)據(jù)創(chuàng)建新的數(shù)據(jù)庫(kù)。當(dāng)新數(shù)據(jù)庫(kù)創(chuàng)建完成后,用新數(shù)據(jù)庫(kù)替換原有的數(shù)據(jù)庫(kù)。

    3 實(shí)驗(yàn)與結(jié)果分析

    3.1 實(shí)驗(yàn)設(shè)置

    本文在SQLite 3.34.0 上實(shí)現(xiàn)了MVCC,并在Visual Studio 2019 中使用nmake 指 令執(zhí)行SQLite 的Makefile.msc 文件,將實(shí)現(xiàn)了MVCC 的SQLite 編譯為動(dòng)態(tài)鏈接庫(kù)。后文將實(shí)現(xiàn)了MVCC 的SQLite 稱為SQLite-MVCC。實(shí)驗(yàn)測(cè)試程序用C++語(yǔ)言編寫,并用Visual Studio 2019 編譯。實(shí)驗(yàn)測(cè)試程序在多個(gè)線程內(nèi)打開SQLite-MVCC,并在事務(wù)中使用SQL 語(yǔ)句進(jìn)行一系列操作。實(shí)驗(yàn)所使用的計(jì)算機(jī)的配置如下:Intel Core i7-7700 處理器(4 核,2.8 GHz),16 GB 內(nèi)存,120 GB 硬盤(讀取速度為546 MB/s),運(yùn)行Windows 10 操作系統(tǒng)。

    實(shí)驗(yàn)在1 個(gè)隨機(jī)生成的表上進(jìn)行。表中含有INT、TEXT、BLOB 和DOUBLE 類型的 屬性各1 個(gè),其 中INT 和DOUBLE 類型的屬性值為隨機(jī)數(shù),TEXT 和BLOB 類型的屬性值由隨機(jī)生成的短序列拼接而成。元組長(zhǎng)度在設(shè)定值的±2 B 內(nèi)變動(dòng)。實(shí)驗(yàn)中預(yù)設(shè)的元組長(zhǎng)度分別為10 B、50 B 和20 B,用于反映不同應(yīng)用情境下的數(shù)據(jù)。為使實(shí)驗(yàn)數(shù)據(jù)在圖表中能合理顯示,將上述3 種元組長(zhǎng)度下的表中元組數(shù)分別設(shè)置為50 萬(wàn)條、20 萬(wàn)條和10 萬(wàn)條。

    3.2 非并發(fā)情況下數(shù)據(jù)寫操作的性能

    本文首先在非并發(fā)情況下對(duì)SQLite-MVCC 上INSERT、DELETE 和UPDATE 操作與SQLite 上對(duì)應(yīng)操作的執(zhí)行時(shí)間進(jìn)行了實(shí)驗(yàn)對(duì)比。圖7 展示了實(shí)驗(yàn)結(jié)果。

    圖7(a)對(duì)比了SQLite 和SQLite-MVCC 上INSERT 操作的執(zhí)行時(shí)間。實(shí)驗(yàn)中使用INSERT 語(yǔ)句分別在SQLite 和SQLite-MVCC 上依次插入50 萬(wàn)條元組,每條語(yǔ)句插入1 條元組,然后測(cè)試插入操作的平均執(zhí)行時(shí)間。實(shí)驗(yàn)結(jié)果顯示,SQLite 和SQLite-MVCC 上插入1 條元組的平均執(zhí)行時(shí)間接近。但由于SQLite-MVCC 中的元組增加了T_START、T_END和CID 字段,比SQLite 中的元組略長(zhǎng),故SQLite-MVCC 上的插入操作略慢于SQLite 上的插入操作,但總體差異不大。

    圖7(b)對(duì)比了SQLite 和SQLite-MVCC 上DELETE 操作的執(zhí)行時(shí)間。實(shí)驗(yàn)中使用DELETE 語(yǔ)句分別在SQLite 和SQLite-MVCC 上刪除50 萬(wàn)條元組,并測(cè)試刪除1 條元組的平均執(zhí)行時(shí)間。實(shí)驗(yàn)結(jié)果顯示,SQLite 和SQLite-MVCC 上刪除操作的平均執(zhí)行時(shí)間接近,因?yàn)镾QLite 和SQLite-MVCC 均采用修改元組頭部的方式實(shí)現(xiàn)數(shù)據(jù)刪除,區(qū)別僅在于SQLite 修改元組的前4 個(gè)字節(jié),而SQLite-MVCC 修改3 個(gè)字節(jié)長(zhǎng)的T_END 字段,因此二者性能接近。

    圖7(c)對(duì)比了SQLite 和SQLite-MVCC 上UPDATE 操作的執(zhí)行時(shí)間。實(shí)驗(yàn)方法與測(cè)試DELETE 操作時(shí)類似。實(shí)驗(yàn)結(jié)果顯示,SQLite-MVCC 上UPDATE 操作的平均執(zhí)行時(shí)間顯著高于SQLite 上UPDATE 操作。這是因?yàn)镾QLite 進(jìn)行原地更新,只需修改元組的屬性值即可,而SQLite-MVCC 需要先執(zhí)行1 次DELETE 操作,再執(zhí)行1 次INSERT 操作。因此,SQLite-MVCC 上UPDATE 操作在理論上要比SQLite 上UPDATE 操作多花費(fèi)1 倍以上的時(shí)間,實(shí)驗(yàn)結(jié)果驗(yàn)證了這一點(diǎn)。另外,SQLite-MVCC 執(zhí)行過程中沿著B+樹葉節(jié)點(diǎn)遍歷新插入的元組,即使元組不可見,也要被讀取1 次并進(jìn)行可見性判斷,從而花費(fèi)更多額外的時(shí)間。

    圖7 非并發(fā)情況下寫操作性能的對(duì)比Fig.7 Performance comparison of non-concurrent write operations

    綜上所述,除UPDATE 外,SQLite-MVCC 上各項(xiàng)操作的時(shí)間性能比SQLite 上略有下降,但在可接受的范圍內(nèi)。受實(shí)現(xiàn)方法的影響,SQLite-MVCC 上更新操作的時(shí)間性能下降較多,因此更新元組數(shù)量較多的事務(wù)不適于使用SQLite-MVCC。

    3.3 非并發(fā)情況下數(shù)據(jù)讀操作的性能

    本文還在非并發(fā)情況下對(duì)SQLite和SQLite-MVCC 的數(shù)據(jù)讀取性能進(jìn)行了實(shí)驗(yàn)對(duì)比。實(shí)驗(yàn)分別在元組長(zhǎng)度為10 B 和200 B 的2 個(gè)隨機(jī)生成的表上進(jìn)行,查詢語(yǔ)句的選擇謂詞的選擇度約為0.1%,并在2 個(gè)表上各執(zhí)行了100 次查詢,測(cè)試100次查詢的總體執(zhí)行時(shí)間。圖8展示了實(shí)驗(yàn)結(jié)果。如圖8(a)所示,當(dāng)元組長(zhǎng)度為10 B 時(shí),SQLite-MVCC 處理1 個(gè)查詢的時(shí)間要比SQLite 多15%~20%。這是因?yàn)樽x取元組中的版本信息并進(jìn)行元組的可見性判斷耗費(fèi)了額外的時(shí)間。如圖8(b)所示,當(dāng)元組長(zhǎng)度為200 B 時(shí),SQLite 和SQLite-MVCC 的查詢執(zhí)行時(shí)間的差異在5%以內(nèi),基本可以忽略。

    圖8 非并發(fā)情況下讀操作性能的對(duì)比Fig.8 Performance comparison of non-concurrent read operations

    3.4 并發(fā)情況下數(shù)據(jù)庫(kù)的性能

    接下來(lái),在并發(fā)情況下對(duì)SQLite 和SQLite-MVCC 的性能進(jìn)行了實(shí)驗(yàn)比較。實(shí)驗(yàn)中通過設(shè)置系統(tǒng)內(nèi)的并發(fā)線程數(shù)來(lái)調(diào)整系統(tǒng)的并發(fā)度。

    該部分實(shí)驗(yàn)首先測(cè)試了SQLite 和SQLite-MVCC 在不同并發(fā)線程數(shù)下的吞吐量,其中吞吐量使用系統(tǒng)每秒鐘處理的事務(wù)數(shù)來(lái)衡量。圖9 展示了實(shí)驗(yàn)結(jié)果。在該實(shí)驗(yàn)中,同類事務(wù)的執(zhí)行時(shí)間基本相同,不存在因隨機(jī)性而導(dǎo)致的執(zhí)行時(shí)間偏差。實(shí)驗(yàn)結(jié)果表明,在相同線程數(shù)下,SQLite-MVCC 的吞吐量高于SQLite。這表明SQLite-MVCC 的讀-寫事務(wù)并發(fā)設(shè)計(jì)提高了系統(tǒng)的并發(fā)性。然而,隨著線程數(shù)的增加,SQLite-MVCC 相較SQLite 的并發(fā)性能優(yōu)勢(shì)逐漸下降,主要原因如下:

    圖9 不同線程數(shù)下系統(tǒng)的吞吐量Fig.9 System throughput under different numbers of threads

    1)線程數(shù)的增加導(dǎo)致了并發(fā)事務(wù)發(fā)生沖突的概率下降。當(dāng)線程數(shù)為2 時(shí),約有55%的事務(wù)發(fā)生沖突;而當(dāng)線程數(shù)為6時(shí),僅有5%左右的事務(wù)發(fā)生沖突。這是因?yàn)镾QLite-MVCC不持支寫-寫事務(wù)并發(fā),系統(tǒng)中最多只有1 個(gè)線程能夠進(jìn)行寫操作。隨著線程數(shù)的增加,讀事務(wù)在全體并發(fā)事務(wù)中的占比提高,寫事務(wù)的CPU 資源占用率下降,從而導(dǎo)致寫事務(wù)的提交變得緩慢,致使并發(fā)事務(wù)發(fā)生沖突的概率下降。

    2)3.3 節(jié)的實(shí)驗(yàn)表明,SQLite-MVCC 執(zhí)行讀事務(wù)的效率略低于SQLite,因此當(dāng)線程數(shù)增加,讀事務(wù)占比提高時(shí),吞吐量提升的速度會(huì)下降。此外,SQLite-MVCC 更容易使CPU 到達(dá)性能瓶頸,從而導(dǎo)致吞吐量隨線程數(shù)的增加其增長(zhǎng)的速度放緩。

    本文對(duì)并發(fā)狀態(tài)下SQLite-MVCC 和SQLite 執(zhí)行相同工作量的事務(wù)時(shí)所用的時(shí)間進(jìn)行了實(shí)驗(yàn)對(duì)比。圖10 展示了實(shí)驗(yàn)結(jié)果。圖10 結(jié)果反映在各種線程數(shù)下,SQLite-MVCC 的執(zhí)行時(shí)間均少于SQLite。這是由于SQLite-MVCC 支持讀-寫事務(wù)并發(fā),在實(shí)際執(zhí)行事務(wù)時(shí)總是可以提前完成寫事務(wù),使整體效率變高。該實(shí)驗(yàn)結(jié)果表明,在實(shí)際的有限任務(wù)工作負(fù)載下,SQLite-MVCC 的并發(fā)性顯著優(yōu)于SQLite。

    圖10 不同線程數(shù)下完成固定事務(wù)所需的時(shí)間Fig.10 Time cost for handling same transactions using different numbers of threads

    數(shù)據(jù)集的大小對(duì)數(shù)據(jù)庫(kù)系統(tǒng)的性能有內(nèi)在影響,本文通過實(shí)驗(yàn)對(duì)此進(jìn)行了測(cè)試。圖11 展示了元組長(zhǎng)度對(duì)數(shù)據(jù)庫(kù)大小的影響。從實(shí)驗(yàn)結(jié)果來(lái)看,在相同的元組長(zhǎng)度下,SQLite-MVCC 數(shù)據(jù)庫(kù)比SQLite 數(shù)據(jù)庫(kù)大,這是因?yàn)镾QLite-MVCC 元組頭部包含額外的版本信息,占用了更多的空間。但是隨著元組長(zhǎng)度的增大,版本信息所占的空間比例越來(lái)越低,對(duì)數(shù)據(jù)庫(kù)大小的影響越來(lái)越不明顯。圖12 展示了在不同的元組長(zhǎng)度下SQLite-MVCC 相較SQLite 的并發(fā)性能提升比例??梢姡S著元組長(zhǎng)度的增加,SQLite-MVCC 的并發(fā)性能提升越來(lái)越大,因?yàn)镾QLite-MVCC 處理版本信息的開銷在總開銷中所占的比例越來(lái)越低,總性能提升。在實(shí)際使用數(shù)據(jù)庫(kù)時(shí),URL、TEXT、BLOB 等類型數(shù)據(jù)的長(zhǎng)度一般在50 B 以上,此時(shí)SQLite-MVCC 有著不錯(cuò)的表現(xiàn),并且此時(shí)存儲(chǔ)元組頭部的版本信息所消耗的存儲(chǔ)空間基本可以忽略不計(jì)。

    圖11 元組長(zhǎng)度對(duì)數(shù)據(jù)庫(kù)大小的影響Fig.11 Influence of tuple length on database size

    圖12 不同元組長(zhǎng)度下SQLite-MVCC并發(fā)性能的提升Fig.12 Concurrent performance improvement of SQLite-MVCC under different tuple lengths

    基于以上實(shí)驗(yàn)結(jié)果可知,SQLite-MVCC 相較SQLite 并發(fā)性能有所提高,并具有一定實(shí)際意義,且在一定范圍內(nèi)并發(fā)事務(wù)中寫事務(wù)比例越高性能提升越大,該方法更適合寫事務(wù)占比高的并發(fā)場(chǎng)合使用。此外,當(dāng)有長(zhǎng)讀取事務(wù)執(zhí)行時(shí)也可考慮使用該方法提高數(shù)據(jù)庫(kù)性能。

    4 結(jié)語(yǔ)

    本文為SQLite 設(shè)計(jì)了支持可串行化隔離級(jí)別的MVCC協(xié)議,并實(shí)現(xiàn)了支持MVCC 的SQLite,使SQLite 上并發(fā)的讀事務(wù)和寫事務(wù)不會(huì)沖突,有效提高了SQLite 處理并發(fā)事務(wù)的能力,為研究如何提高SQLite 的并發(fā)性能提供了一種新的技術(shù)思路。后續(xù)還可以從以下幾個(gè)方面進(jìn)行深入研究:1)將MVCC 與其他方法(如多粒度鎖)相結(jié)合,進(jìn)一步提高系統(tǒng)的并發(fā)性能,并解決目前寫事務(wù)和寫事務(wù)無(wú)法并發(fā)執(zhí)行的問題。2)設(shè)計(jì)更符合MVCC 的編譯器與字節(jié)碼,使系統(tǒng)可以跳過不可見的元組,進(jìn)一步提高系統(tǒng)性能。3)設(shè)計(jì)更加高效的舊版本數(shù)據(jù)回收機(jī)制。

    猜你喜歡
    元組線程事務(wù)
    “事物”與“事務(wù)”
    基于分布式事務(wù)的門架數(shù)據(jù)處理系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)
    Python核心語(yǔ)法
    河湖事務(wù)
    海量數(shù)據(jù)上有效的top-kSkyline查詢算法*
    基于減少檢索的負(fù)表約束優(yōu)化算法
    淺談linux多線程協(xié)作
    面向數(shù)據(jù)流處理的元組跟蹤方法
    SQLServer自治事務(wù)實(shí)現(xiàn)方案探析
    Linux線程實(shí)現(xiàn)技術(shù)研究
    亚洲欧美日韩另类电影网站| 国产日韩欧美视频二区| 久久精品aⅴ一区二区三区四区| 国产成人精品无人区| 日韩 欧美 亚洲 中文字幕| 免费观看性生交大片5| 日本爱情动作片www.在线观看| 亚洲av日韩精品久久久久久密 | 观看av在线不卡| 亚洲熟女精品中文字幕| 91aial.com中文字幕在线观看| 国产深夜福利视频在线观看| 在线观看国产h片| 国产女主播在线喷水免费视频网站| 赤兔流量卡办理| 久久午夜综合久久蜜桃| 亚洲激情五月婷婷啪啪| 最近最新中文字幕大全免费视频 | 国产在线免费精品| 看免费成人av毛片| 免费黄频网站在线观看国产| 亚洲美女搞黄在线观看| 伦理电影大哥的女人| 电影成人av| 啦啦啦在线观看免费高清www| 啦啦啦在线免费观看视频4| 一边摸一边抽搐一进一出视频| 成人18禁高潮啪啪吃奶动态图| 99九九在线精品视频| 在线观看www视频免费| 久久久精品区二区三区| 不卡av一区二区三区| 中文字幕最新亚洲高清| 国产高清国产精品国产三级| 热re99久久国产66热| 激情视频va一区二区三区| 久久人人爽人人片av| 亚洲av成人不卡在线观看播放网 | 啦啦啦 在线观看视频| 亚洲av电影在线观看一区二区三区| 久久这里只有精品19| 日韩精品有码人妻一区| 久久精品国产亚洲av涩爱| 中文字幕av电影在线播放| 黄色 视频免费看| 午夜福利,免费看| 肉色欧美久久久久久久蜜桃| 2018国产大陆天天弄谢| 99九九在线精品视频| 另类精品久久| 婷婷色综合大香蕉| 狂野欧美激情性bbbbbb| 亚洲三区欧美一区| 欧美 日韩 精品 国产| 女人被躁到高潮嗷嗷叫费观| 黄网站色视频无遮挡免费观看| 欧美日韩国产mv在线观看视频| 久久久久久人妻| av有码第一页| 亚洲国产av新网站| 精品卡一卡二卡四卡免费| 国产爽快片一区二区三区| 国产一区二区激情短视频 | 51午夜福利影视在线观看| av又黄又爽大尺度在线免费看| 99久国产av精品国产电影| videos熟女内射| 丝袜美腿诱惑在线| 中国三级夫妇交换| 免费在线观看视频国产中文字幕亚洲 | 又大又黄又爽视频免费| 国产成人欧美在线观看 | 又大又爽又粗| 制服人妻中文乱码| 婷婷色av中文字幕| 90打野战视频偷拍视频| 国产一区亚洲一区在线观看| 日韩 亚洲 欧美在线| 国产午夜精品一二区理论片| 亚洲天堂av无毛| av国产精品久久久久影院| 久久久久久久大尺度免费视频| 久久久久精品人妻al黑| 青春草亚洲视频在线观看| 亚洲精品国产av成人精品| 热re99久久精品国产66热6| 秋霞在线观看毛片| 伦理电影免费视频| 99国产综合亚洲精品| 午夜老司机福利片| 女性被躁到高潮视频| 波多野结衣av一区二区av| 人人妻人人澡人人爽人人夜夜| 中文欧美无线码| 日韩人妻精品一区2区三区| 老鸭窝网址在线观看| 亚洲成人av在线免费| 精品国产一区二区三区久久久樱花| 亚洲美女视频黄频| 又黄又粗又硬又大视频| videos熟女内射| 国产免费现黄频在线看| 久久天躁狠狠躁夜夜2o2o | 90打野战视频偷拍视频| 不卡视频在线观看欧美| 两个人免费观看高清视频| 亚洲中文av在线| 亚洲七黄色美女视频| 69精品国产乱码久久久| 性少妇av在线| 一本久久精品| 国产精品久久久av美女十八| 欧美日韩综合久久久久久| av有码第一页| 国产免费福利视频在线观看| av免费观看日本| 久久ye,这里只有精品| 2021少妇久久久久久久久久久| 婷婷色av中文字幕| 在线观看一区二区三区激情| 尾随美女入室| 国产精品.久久久| 久久免费观看电影| 菩萨蛮人人尽说江南好唐韦庄| 久久精品国产综合久久久| 午夜福利视频在线观看免费| 免费高清在线观看日韩| 国产熟女午夜一区二区三区| 久久久久视频综合| 在线免费观看不下载黄p国产| 亚洲国产精品999| 制服诱惑二区| 在现免费观看毛片| 欧美黑人精品巨大| 国产女主播在线喷水免费视频网站| 日韩大片免费观看网站| 久久精品久久精品一区二区三区| a 毛片基地| 久久狼人影院| 69精品国产乱码久久久| 曰老女人黄片| 欧美激情极品国产一区二区三区| 中国三级夫妇交换| 一级a爱视频在线免费观看| 亚洲精品一二三| 久久精品久久久久久噜噜老黄| 精品久久久精品久久久| 久久精品aⅴ一区二区三区四区| 成人手机av| 热re99久久国产66热| 天天影视国产精品| 亚洲美女黄色视频免费看| 丝袜美足系列| 久久久久久久国产电影| 黄色视频不卡| 国产男女内射视频| 2021少妇久久久久久久久久久| 新久久久久国产一级毛片| 母亲3免费完整高清在线观看| 亚洲国产精品一区三区| 国产精品久久久av美女十八| 国产精品无大码| 嫩草影院入口| 欧美黑人精品巨大| 菩萨蛮人人尽说江南好唐韦庄| 国产精品久久久久久精品电影小说| 久久毛片免费看一区二区三区| 欧美日韩福利视频一区二区| 国产精品嫩草影院av在线观看| 午夜免费男女啪啪视频观看| 又大又黄又爽视频免费| 精品少妇一区二区三区视频日本电影 | 亚洲欧美一区二区三区黑人| 国产精品女同一区二区软件| 欧美av亚洲av综合av国产av | 天天躁夜夜躁狠狠躁躁| 亚洲精品自拍成人| 99九九在线精品视频| 一区二区日韩欧美中文字幕| 菩萨蛮人人尽说江南好唐韦庄| 97人妻天天添夜夜摸| 国产探花极品一区二区| 国产亚洲午夜精品一区二区久久| 极品少妇高潮喷水抽搐| 亚洲人成电影观看| 五月天丁香电影| 精品人妻熟女毛片av久久网站| 国产成人91sexporn| 国产片特级美女逼逼视频| 欧美黑人欧美精品刺激| 国产精品免费大片| 97在线人人人人妻| 亚洲色图 男人天堂 中文字幕| 最近手机中文字幕大全| 日韩,欧美,国产一区二区三区| 国产精品人妻久久久影院| 热re99久久精品国产66热6| 国产熟女欧美一区二区| 国产成人av激情在线播放| 十八禁高潮呻吟视频| 1024视频免费在线观看| 久久精品亚洲熟妇少妇任你| 大香蕉久久网| 亚洲精品国产一区二区精华液| 久久久久久人人人人人| 午夜日本视频在线| 亚洲精品国产av成人精品| 人妻一区二区av| 国产97色在线日韩免费| 国产成人欧美| 高清黄色对白视频在线免费看| 黄色怎么调成土黄色| tube8黄色片| 国产黄频视频在线观看| 曰老女人黄片| 免费高清在线观看日韩| 亚洲 欧美一区二区三区| 天天影视国产精品| 免费观看人在逋| 国产片内射在线| 99久久人妻综合| 日韩一本色道免费dvd| 午夜日韩欧美国产| 亚洲精品美女久久久久99蜜臀 | 咕卡用的链子| 久久久久精品人妻al黑| 亚洲免费av在线视频| 久久国产亚洲av麻豆专区| 韩国精品一区二区三区| 亚洲精品美女久久av网站| 免费在线观看黄色视频的| 亚洲色图综合在线观看| 日韩免费高清中文字幕av| 青春草国产在线视频| 日本91视频免费播放| 午夜免费男女啪啪视频观看| 美女大奶头黄色视频| e午夜精品久久久久久久| av国产久精品久网站免费入址| 曰老女人黄片| 在线观看www视频免费| 亚洲第一av免费看| 国产免费现黄频在线看| 中国三级夫妇交换| 亚洲伊人色综图| 又大又爽又粗| 国产精品99久久99久久久不卡 | 老司机在亚洲福利影院| 男女之事视频高清在线观看 | 中文字幕精品免费在线观看视频| 成人免费观看视频高清| 国产高清国产精品国产三级| 久久精品熟女亚洲av麻豆精品| 国产精品秋霞免费鲁丝片| 韩国高清视频一区二区三区| 久久99一区二区三区| 国产1区2区3区精品| 午夜免费观看性视频| 国产精品秋霞免费鲁丝片| 亚洲三区欧美一区| 亚洲av中文av极速乱| 又黄又粗又硬又大视频| 成人亚洲欧美一区二区av| 观看美女的网站| 美女中出高潮动态图| 国产欧美日韩综合在线一区二区| 亚洲精品在线美女| 免费高清在线观看视频在线观看| 最近的中文字幕免费完整| 香蕉丝袜av| 啦啦啦中文免费视频观看日本| 一区二区日韩欧美中文字幕| 成人国产av品久久久| 青草久久国产| 国产精品免费视频内射| 男女下面插进去视频免费观看| 99久久99久久久精品蜜桃| a级片在线免费高清观看视频| 国产精品亚洲av一区麻豆 | 又大又黄又爽视频免费| 美女国产高潮福利片在线看| 热99久久久久精品小说推荐| 欧美日韩视频精品一区| 男男h啪啪无遮挡| kizo精华| 免费av中文字幕在线| 97在线人人人人妻| 日韩一卡2卡3卡4卡2021年| 老司机靠b影院| 这个男人来自地球电影免费观看 | 香蕉国产在线看| 久久久久精品性色| 最近最新中文字幕大全免费视频 | 久久久国产欧美日韩av| 亚洲国产av新网站| 黑人巨大精品欧美一区二区蜜桃| 久久久久精品久久久久真实原创| 久久久久久人人人人人| 波多野结衣一区麻豆| 精品亚洲成a人片在线观看| 伊人亚洲综合成人网| 精品国产国语对白av| 黄色视频不卡| 成人18禁高潮啪啪吃奶动态图| 一级爰片在线观看| 18禁裸乳无遮挡动漫免费视频| 三上悠亚av全集在线观看| 国产精品国产三级专区第一集| 成年人午夜在线观看视频| 国产男女超爽视频在线观看| 精品少妇内射三级| 欧美最新免费一区二区三区| 精品少妇内射三级| 国产男女超爽视频在线观看| 久久精品国产综合久久久| 国产在线免费精品| 久久 成人 亚洲| 久久亚洲国产成人精品v| 菩萨蛮人人尽说江南好唐韦庄| 99久国产av精品国产电影| 亚洲成av片中文字幕在线观看| 免费av中文字幕在线| 美女午夜性视频免费| 99热国产这里只有精品6| 无限看片的www在线观看| av在线app专区| 最近最新中文字幕大全免费视频 | 三上悠亚av全集在线观看| 国产一区二区三区av在线| 国产精品99久久99久久久不卡 | 国产一级毛片在线| 欧美精品一区二区免费开放| 蜜桃国产av成人99| av免费观看日本| 免费看av在线观看网站| 亚洲色图 男人天堂 中文字幕| 建设人人有责人人尽责人人享有的| 亚洲av欧美aⅴ国产| 老司机深夜福利视频在线观看 | av有码第一页| 丝袜美足系列| 90打野战视频偷拍视频| 亚洲国产最新在线播放| 肉色欧美久久久久久久蜜桃| 老司机亚洲免费影院| 大香蕉久久网| 国产1区2区3区精品| 九草在线视频观看| 午夜福利视频精品| 国产精品一区二区在线观看99| 丁香六月欧美| 岛国毛片在线播放| 久久国产亚洲av麻豆专区| 国产一区二区激情短视频 | 久久综合国产亚洲精品| 国产不卡av网站在线观看| 高清av免费在线| 女人高潮潮喷娇喘18禁视频| 久久性视频一级片| 最近手机中文字幕大全| 18禁国产床啪视频网站| 国产亚洲欧美精品永久| 午夜福利乱码中文字幕| 欧美精品av麻豆av| 少妇精品久久久久久久| 日本色播在线视频| 不卡av一区二区三区| 亚洲精华国产精华液的使用体验| 成年动漫av网址| 观看av在线不卡| 亚洲精品成人av观看孕妇| 性高湖久久久久久久久免费观看| av又黄又爽大尺度在线免费看| 建设人人有责人人尽责人人享有的| 伦理电影大哥的女人| 亚洲美女视频黄频| 黑人巨大精品欧美一区二区蜜桃| 别揉我奶头~嗯~啊~动态视频 | 最新的欧美精品一区二区| 高清不卡的av网站| 波多野结衣av一区二区av| 午夜日本视频在线| 免费日韩欧美在线观看| 色94色欧美一区二区| 亚洲国产av新网站| 国产日韩欧美视频二区| 精品国产一区二区三区久久久樱花| 国产成人啪精品午夜网站| 欧美成人精品欧美一级黄| 精品免费久久久久久久清纯 | 日本av免费视频播放| 下体分泌物呈黄色| 精品福利永久在线观看| 精品久久久精品久久久| 日韩,欧美,国产一区二区三区| 啦啦啦在线观看免费高清www| 国产在线一区二区三区精| 国产欧美日韩综合在线一区二区| 精品国产超薄肉色丝袜足j| 最近最新中文字幕大全免费视频 | 日韩成人av中文字幕在线观看| 日日啪夜夜爽| 免费高清在线观看视频在线观看| 久久久久网色| 久久久久久久久久久免费av| 午夜福利影视在线免费观看| 国产成人精品无人区| 亚洲欧美一区二区三区国产| 黄片无遮挡物在线观看| 伊人久久大香线蕉亚洲五| 日本vs欧美在线观看视频| 日本91视频免费播放| 七月丁香在线播放| 伊人久久大香线蕉亚洲五| 只有这里有精品99| 国产精品久久久久久精品电影小说| 你懂的网址亚洲精品在线观看| 免费日韩欧美在线观看| 午夜日本视频在线| 晚上一个人看的免费电影| 日本一区二区免费在线视频| 91成人精品电影| 大码成人一级视频| 国产一卡二卡三卡精品 | 精品一品国产午夜福利视频| 制服人妻中文乱码| 五月开心婷婷网| 国产av国产精品国产| 中文字幕人妻丝袜一区二区 | 国产av一区二区精品久久| 午夜久久久在线观看| 国产午夜精品一二区理论片| 免费观看性生交大片5| 亚洲中文av在线| av电影中文网址| 亚洲一码二码三码区别大吗| 欧美成人午夜精品| 国产极品粉嫩免费观看在线| 国产精品麻豆人妻色哟哟久久| 国产亚洲午夜精品一区二区久久| 99国产精品免费福利视频| 国产1区2区3区精品| 你懂的网址亚洲精品在线观看| 曰老女人黄片| 蜜桃在线观看..| 国产亚洲欧美精品永久| 久久av网站| 一区二区三区四区激情视频| 精品国产乱码久久久久久男人| 久久久久精品性色| 男人操女人黄网站| 极品少妇高潮喷水抽搐| 男女边吃奶边做爰视频| 国产成人一区二区在线| av国产久精品久网站免费入址| 亚洲国产精品一区二区三区在线| 在线免费观看不下载黄p国产| 欧美日韩视频精品一区| 精品亚洲成国产av| 亚洲欧洲精品一区二区精品久久久 | 日韩av在线免费看完整版不卡| 国产精品一二三区在线看| 又大又黄又爽视频免费| 精品国产一区二区三区四区第35| 久久久久久久久免费视频了| 亚洲成色77777| 久久精品亚洲熟妇少妇任你| 国产黄频视频在线观看| 伊人久久国产一区二区| 欧美国产精品一级二级三级| 精品少妇黑人巨大在线播放| 在线观看免费高清a一片| 黄色一级大片看看| 黄色怎么调成土黄色| 宅男免费午夜| √禁漫天堂资源中文www| 国产xxxxx性猛交| 国产精品亚洲av一区麻豆 | 亚洲av男天堂| 三上悠亚av全集在线观看| 国产 一区精品| 777久久人妻少妇嫩草av网站| 最近最新中文字幕大全免费视频 | 人人妻人人爽人人添夜夜欢视频| netflix在线观看网站| 欧美精品一区二区大全| 国产精品蜜桃在线观看| 国产精品亚洲av一区麻豆 | 亚洲成人手机| 一级片免费观看大全| 超碰97精品在线观看| 一级毛片 在线播放| 国产精品久久久人人做人人爽| 久久97久久精品| 国产免费福利视频在线观看| 免费看av在线观看网站| 一本—道久久a久久精品蜜桃钙片| 久久毛片免费看一区二区三区| 黄色毛片三级朝国网站| 亚洲精品aⅴ在线观看| 亚洲精品中文字幕在线视频| 最近最新中文字幕免费大全7| 日韩欧美一区视频在线观看| 久久青草综合色| 国产熟女欧美一区二区| 亚洲熟女毛片儿| 精品国产一区二区久久| av视频免费观看在线观看| 一区二区三区四区激情视频| 国产成人精品久久二区二区91 | 麻豆av在线久日| 欧美成人精品欧美一级黄| 狂野欧美激情性xxxx| 亚洲男人天堂网一区| 午夜福利影视在线免费观看| 亚洲精品,欧美精品| 新久久久久国产一级毛片| 亚洲欧洲精品一区二区精品久久久 | 看非洲黑人一级黄片| 久久国产精品男人的天堂亚洲| 我的亚洲天堂| 精品国产乱码久久久久久男人| 国产乱来视频区| 亚洲av男天堂| 国产毛片在线视频| 最近手机中文字幕大全| 日本av免费视频播放| 欧美av亚洲av综合av国产av | 久久av网站| 啦啦啦在线免费观看视频4| 国产精品一区二区在线观看99| 天天躁日日躁夜夜躁夜夜| 在线天堂最新版资源| 亚洲精品第二区| 国产成人91sexporn| 亚洲精品一二三| 男男h啪啪无遮挡| 肉色欧美久久久久久久蜜桃| 在线 av 中文字幕| 国产成人欧美| 欧美在线一区亚洲| 老司机深夜福利视频在线观看 | 午夜福利视频在线观看免费| 黄片播放在线免费| 黄频高清免费视频| 国产精品久久久久久久久免| 熟女少妇亚洲综合色aaa.| 国产在线免费精品| 亚洲欧美精品综合一区二区三区| 午夜老司机福利片| 18禁国产床啪视频网站| 又黄又粗又硬又大视频| 一区在线观看完整版| 国产亚洲精品第一综合不卡| 精品第一国产精品| 亚洲欧美精品自产自拍| 蜜桃国产av成人99| 日韩av免费高清视频| 在线天堂中文资源库| 欧美日韩av久久| 亚洲国产av影院在线观看| 国产又色又爽无遮挡免| 婷婷色综合www| 91aial.com中文字幕在线观看| 国产又爽黄色视频| 精品一区二区三区av网在线观看 | 国产成人免费无遮挡视频| 欧美在线一区亚洲| 久久久久久久国产电影| 亚洲欧洲日产国产| 成人亚洲欧美一区二区av| 韩国高清视频一区二区三区| av又黄又爽大尺度在线免费看| 看免费成人av毛片| 久热爱精品视频在线9| 色婷婷av一区二区三区视频| 欧美黑人欧美精品刺激| 成年人午夜在线观看视频| 一区二区av电影网| 亚洲国产精品999| 国产精品嫩草影院av在线观看| 国产欧美日韩综合在线一区二区| 成人午夜精彩视频在线观看| 精品少妇内射三级| av在线观看视频网站免费| 天天影视国产精品| 久久韩国三级中文字幕| 欧美日韩成人在线一区二区| 亚洲第一av免费看| 男男h啪啪无遮挡| 大香蕉久久成人网| 日本欧美视频一区| 日日摸夜夜添夜夜爱| 亚洲精品视频女| 国产精品麻豆人妻色哟哟久久| 日日爽夜夜爽网站| 亚洲人成电影观看| 亚洲欧美色中文字幕在线| 久久97久久精品| 汤姆久久久久久久影院中文字幕| 亚洲欧美色中文字幕在线| 国产成人免费无遮挡视频| 免费高清在线观看视频在线观看| 巨乳人妻的诱惑在线观看| av卡一久久| 国产一区二区三区av在线| 日韩欧美一区视频在线观看| 一本久久精品| 操出白浆在线播放| 尾随美女入室| 亚洲四区av| 久久久精品国产亚洲av高清涩受| 深夜精品福利| 九色亚洲精品在线播放| 狂野欧美激情性xxxx|