WAL 是 write-ahead log 系統(tǒng),其核心思想是將用戶的所有修改操作(插入、刪除)寫入日志,然后再應(yīng)用到系統(tǒng)狀態(tài)。一旦日志寫入成功,就可以通知用戶操作成功。由于日志采用尾部追加方式寫入,耗時(shí)較短,因此不會(huì)長時(shí)間阻塞用戶線程。另外,為防止意外退出導(dǎo)致數(shù)據(jù)丟失,系統(tǒng)重啟時(shí)會(huì)根據(jù)日志重做用戶操作,保證數(shù)據(jù)可靠性。
創(chuàng)新互聯(lián)建站從2013年開始,先為云南等服務(wù)建站,云南等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為云南企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
WAL 一直是傳統(tǒng) RDBMS 系統(tǒng)中的一個(gè)共識(shí),用于幫助保證原子性和持久性(ACID 的 A 和 D)。對(duì)表的所有更新首先寫入 WA),然后異步的方式使用。
示例 WAL 和 WALEntry 結(jié)構(gòu):
為什么不將更改直接刷新到實(shí)際數(shù)據(jù)文件?
它有2個(gè)方面——
WAL 是一個(gè)僅附加日志,它將數(shù)據(jù)存儲(chǔ)中的每個(gè)狀態(tài)更改存儲(chǔ)為日志。
一個(gè)單獨(dú)的異步進(jìn)程可以從 WAL 讀取操作,然后按照正常流程通過不同的緩存將數(shù)據(jù)更新應(yīng)用于磁盤上的實(shí)際數(shù)據(jù)文件,有助于提高數(shù)據(jù)存儲(chǔ)的寫入吞吐量。
此外,如果發(fā)生故障,可能會(huì)有未應(yīng)用的更新,由于我們?cè)?WAL 文件中存在操作,我們可以從 WAL 重放操作并應(yīng)用它們以使數(shù)據(jù)存儲(chǔ)恢復(fù)到一致狀態(tài)。因此,WAL 幫助我們確保數(shù)據(jù)的完整性和可靠性,同時(shí)仍然允許我們的數(shù)據(jù)存儲(chǔ)具有高寫入吞吐量。
如前所述,對(duì)磁盤的寫入可能不會(huì)直接刷新,考慮到寫入系統(tǒng)中導(dǎo)致性能的問題,需要進(jìn)行權(quán)衡刷新頻率或微批處理或兩者來將更改刷新到磁盤,以幫助提高性能。請(qǐng)注意,此處存在數(shù)據(jù)丟失的風(fēng)險(xiǎn)。
需要確保任何刷新到磁盤的操作都不會(huì)損壞, WAL 記錄還包含一個(gè) CRC 值,該值可用于驗(yàn)證何時(shí)從 WAL 讀取記錄并確保沒有損壞。
由于 WAL 是一個(gè)附加追尾的文件,因此如果客戶端由于通信故障而重試,可能會(huì)遇到在 WAL 上寫入重復(fù)操作的情況。因此,每當(dāng)讀取 WAL 時(shí),要確保忽略重復(fù)項(xiàng),或者對(duì)應(yīng)用數(shù)據(jù)的動(dòng)作具有冪等性的。
1)所有數(shù)據(jù)庫,包括像Cassandra這樣的NoSQL數(shù)據(jù)庫都使用WAL來保證持久性。
2) Kafka 使用了與 WAL(Commit Log) 類似的結(jié)構(gòu)。
3) 像 Rocks DB、Level DB 這樣的 KV 存儲(chǔ)和像 Apache Ignite 這樣的分布式緩存也使用 WAL。
總而言之,WAL 提供一下價(jià)值
1) 更快的性能和吞吐量,避免了所有更改的數(shù)據(jù)刷新/磁盤寫入。
2) 重啟時(shí)的可恢復(fù)性,操作可以從 WAL 應(yīng)用到實(shí)際的數(shù)據(jù)存儲(chǔ)。
3)能夠恢復(fù)到時(shí)間點(diǎn)快照,我們?cè)?WAL 中存在所有操作。
分布式緩存主要用于在高并發(fā)環(huán)境下,減輕數(shù)據(jù)庫的壓力,提高系統(tǒng)的響應(yīng)速度和并發(fā)吞吐。當(dāng)大量的讀、寫請(qǐng)求涌向數(shù)據(jù)庫時(shí),磁盤的處理速度與內(nèi)存顯然不在一個(gè)量級(jí),因此,在數(shù)據(jù)庫之前加一層緩存,能夠顯著提高系統(tǒng)的響應(yīng)速度,并降低數(shù)據(jù)庫的壓力。作為傳統(tǒng)的關(guān)系型數(shù)據(jù)庫,MySQL提供完整的ACID操作,支持豐富的數(shù)據(jù)類型、強(qiáng)大的關(guān)聯(lián)查詢、where語句等,能夠非??鸵椎亟⒉樵兯饕?,執(zhí)行復(fù)雜的內(nèi)連接、外連接、求和、排序、分組等操作,并且支持存儲(chǔ)過程、函數(shù)等功能,產(chǎn)品成熟度高,功能強(qiáng)大。但是,對(duì)于需要應(yīng)對(duì)高并發(fā)訪問并且存儲(chǔ)海量數(shù)據(jù)的場景來說,出于對(duì)性能的考慮,不得不放棄很多傳統(tǒng)關(guān)系型數(shù)據(jù)庫原本強(qiáng)大的功能,犧牲了系統(tǒng)的易用性,并且使得系統(tǒng)的設(shè)計(jì)和管理變得更為復(fù)雜。這也使得在過去幾年中,流行著另一種新的存儲(chǔ)解決方案——NoSQL,它與傳統(tǒng)的關(guān)系型數(shù)據(jù)庫最大的差別在于,它不使用SQL作為查詢語言來查找數(shù)據(jù),而采用key-value形式進(jìn)行查找,提供了更高的查詢效率及吞吐,并且能夠更加方便地進(jìn)行擴(kuò)展,存儲(chǔ)海量數(shù)據(jù),在數(shù)千個(gè)節(jié)點(diǎn)上進(jìn)行分區(qū),自動(dòng)進(jìn)行數(shù)據(jù)的復(fù)制和備份。在分布式系統(tǒng)中,消息作為應(yīng)用間通信的一種方式,得到了十分廣泛的應(yīng)用。消息可以被保存在隊(duì)列中,直到被接收者取出,由于消息發(fā)送者不需要同步等待消息接收者的響應(yīng),消息的異步接收降低了系統(tǒng)集成的耦合度,提升了分布式系統(tǒng)協(xié)作的效率,使得系統(tǒng)能夠更快地響應(yīng)用戶,提供更高的吞吐。
當(dāng)系統(tǒng)處于峰值壓力時(shí),分布式消息隊(duì)列還能夠作為緩沖,削峰填谷,緩解集群的壓力,避免整個(gè)系統(tǒng)被壓垮。垂直化的搜索引擎在分布式系統(tǒng)中是一個(gè)非常重要的角色,它既能夠滿足用戶對(duì)于全文檢索、模糊匹配的需求,解決數(shù)據(jù)庫like查詢效率低下的問題,又能夠解決分布式環(huán)境下,由于采用分庫分表,或者使用NoSQL數(shù)據(jù)庫,導(dǎo)致無法進(jìn)行多表關(guān)聯(lián)或者進(jìn)行復(fù)雜查詢的問題。
下文例子中演示了如何插入、獲取、刪除一條記錄
LevelDB 簡介
一、LevelDB入門
LevelDB是Google開源的持久化KV單機(jī)數(shù)據(jù)庫,具有很高的隨機(jī)寫,順序讀/寫性能,但是隨機(jī)讀的性能很一般,也就是說,LevelDB很適合應(yīng)用在查詢較少,而寫很多的場景。LevelDB應(yīng)用了LSM (Log Structured Merge) 策略,lsm_tree對(duì)索引變更進(jìn)行延遲及批量處理,并通過一種類似于歸并排序的方式高效地將更新遷移到磁盤,降低索引插入開銷,關(guān)于LSM,本文在后面也會(huì)簡單提及。
根據(jù)LevelDB官方網(wǎng)站的描述,LevelDB的特點(diǎn)和限制如下:
特點(diǎn):
1、key和value都是任意長度的字節(jié)數(shù)組;
2、entry(即一條K-V記錄)默認(rèn)是按照key的字典順序存儲(chǔ)的,當(dāng)然開發(fā)者也可以重載這個(gè)排序函數(shù);
3、提供的基本操作接口:Put()、Delete()、Get()、Batch();
4、支持批量操作以原子操作進(jìn)行;
5、可以創(chuàng)建數(shù)據(jù)全景的snapshot(快照),并允許在快照中查找數(shù)據(jù);
6、可以通過前向(或后向)迭代器遍歷數(shù)據(jù)(迭代器會(huì)隱含的創(chuàng)建一個(gè)snapshot);
7、自動(dòng)使用Snappy壓縮數(shù)據(jù);
8、可移植性;
限制:
1、非關(guān)系型數(shù)據(jù)模型(NoSQL),不支持sql語句,也不支持索引;
2、一次只允許一個(gè)進(jìn)程訪問一個(gè)特定的數(shù)據(jù)庫;
3、沒有內(nèi)置的C/S架構(gòu),但開發(fā)者可以使用LevelDB庫自己封裝一個(gè)server;
LevelDB本身只是一個(gè)lib庫,在源碼目錄make編譯即可,然后在我們的應(yīng)用程序里面可以直接include leveldb/include/db.h頭文件,該頭文件有幾個(gè)基本的數(shù)據(jù)庫操作接口,下面是一個(gè)測試?yán)樱?/p>
#include iostream
#include string
#include assert.h
#include "leveldb/db.h"
using namespace std;
int main(void)
{
leveldb::DB *db;
leveldb::Options options;
options.create_if_missing = true;
// open
leveldb::Status status = leveldb::DB::Open(options,"/tmp/testdb", db);
assert(status.ok());
string key = "name";
string value = "chenqi";
// write
status = db-Put(leveldb::WriteOptions(), key, value);
assert(status.ok());
// read
status = db-Get(leveldb::ReadOptions(), key, value);
assert(status.ok());
coutvalueendl;
// delete
status = db-Delete(leveldb::WriteOptions(), key);
assert(status.ok());
status = db-Get(leveldb::ReadOptions(),key, value);
if(!status.ok()) {
cerrkey" "status.ToString()endl;
} else {
coutkey"==="valueendl;
}
// close
delete db;
return 0;
}
上面的例子演示了如何插入、獲取、刪除一條記錄,編譯代碼:
g++ -o test test.cpp libleveldb.a -lpthread -Iinclude
執(zhí)行./test后,會(huì)在/tmp下面生成一個(gè)目錄testdb,里面包含若干文件:
------------------------------------------------------------
LevelDB是google開源的一個(gè)key-value存儲(chǔ)引擎庫,類似于開源的Lucene索引庫一樣。其他的軟件開發(fā)者可以利用該庫做二次開發(fā),來滿足定制需求。LevelDB采用日志式的寫方式來提高寫性能,但是犧牲了部分讀性能。為了彌補(bǔ)犧牲了的讀性能,一些人提議使用SSD作為存儲(chǔ)介質(zhì)。
對(duì)于本地化的Key-value存儲(chǔ)引擎來說,簡單的使用一般都分成三個(gè)基本的步驟:(1)打開一個(gè)數(shù)據(jù)庫實(shí)例;(2)對(duì)這個(gè)數(shù)據(jù)庫實(shí)例進(jìn)行插入,修改和查詢操作;(3)最后在使用完成之后,關(guān)閉該數(shù)據(jù)庫。下面將詳細(xì)討論該三個(gè)步驟:
一、打開一個(gè)數(shù)據(jù)庫實(shí)例
一個(gè)leveldb數(shù)據(jù)庫有一個(gè)對(duì)應(yīng)一個(gè)文件系統(tǒng)目錄的名字。該數(shù)據(jù)庫的所有內(nèi)容都存儲(chǔ)在這個(gè)目錄下。下面的代碼描述了怎樣打開一個(gè)數(shù)據(jù)庫或者建立一個(gè)新的數(shù)據(jù)庫。
#include assert.h
#include "leveldb/db.h"
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(options,"/tmp/testdb", db);
assert(status.ok());
如果打開已存在數(shù)據(jù)庫的時(shí)候,需要拋出錯(cuò)誤。將以下代碼插在leveldb::DB::Open方法前面:
options.error_if_exists = true;
二、對(duì)數(shù)據(jù)庫的簡單讀、寫操作
LevelDB提供了Put,Delete和Get三個(gè)方法對(duì)數(shù)據(jù)庫進(jìn)行修改和查詢。例如,下面的代碼片段描述了怎樣將key1對(duì)應(yīng)的value值,移到key2對(duì)應(yīng)的值。
std::string value;
leveldb::Status s = db-Get(leveldb::ReadOptions(), key1, value);
if(s.ok()) s = db-Put(leveldb::WriteOptions(), key2, value);
if(s.ok()) s = db-Delete(leveldb::WriteOptions(), key1);
三、關(guān)閉數(shù)據(jù)庫
在對(duì)數(shù)據(jù)庫進(jìn)行了一系列的操作之后,需要對(duì)數(shù)據(jù)庫進(jìn)行關(guān)閉。該操作比較簡單:
... open the db as described above...
... do something with db ...
delete db;
上面對(duì)levelDB的簡單使用做了基本的介紹,接下來就是如何自己寫一個(gè)完成并且能運(yùn)行的例子。
1、下載源碼 git clone
2、編譯源碼 cd leveldb make all
3、編寫test.cpp
#include assert.h
#include string.h
#include leveldb/db.h
#include iostream
int main(){
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(options,"/tmp/testdb", db);
assert(status.ok());
//write key1,value1
std::string key="key";
std::string value = "value";
status = db-Put(leveldb::WriteOptions(), key,value);
assert(status.ok());
status = db-Get(leveldb::ReadOptions(), key, value);
assert(status.ok());
std::coutvaluestd::endl;
std::string key2 = "key2";
//move the value under key to key2
status = db-Put(leveldb::WriteOptions(),key2,value);
assert(status.ok());
status = db-Delete(leveldb::WriteOptions(), key);
assert(status.ok());
status = db-Get(leveldb::ReadOptions(),key2, value);
assert(status.ok());
std::coutkey2"==="valuestd::endl;
status = db-Get(leveldb::ReadOptions(),key, value);
if(!status.ok()) std::cerrkey" "status.ToString()std::endl;
else std::coutkey"==="valuestd::endl;
delete db;
return 0;
}
4、編譯鏈接 g++ -o test test.cpp ../leveldb/libleveldb.a -lpthread -I../leveldb/include
注意libleveldb.a 和leveldb include的路徑。
5、運(yùn)行結(jié)果./test:
value
key2===value
key NotFound:
思路一,使用獨(dú)立的商品類表, 構(gòu)造商品屬性信息,1、N個(gè)商品類屬性值表,2、商品基本信息表,3、商品屬性表
思路二,使用key-value模型,使用動(dòng)態(tài)行列轉(zhuǎn)換模型,將商品屬性信息碎片化存儲(chǔ),整合型只讀輸出快照,1、公共鍵值表,2、公共類表,3、公共鍵類表,4、屬性值物化表,5、商品基本信息表,6、商品屬性表,6、商品屬性快照表或模型
總體來說,主流數(shù)據(jù)庫并不存在明確的好壞之分,每一種數(shù)據(jù)庫都有各自的優(yōu)缺點(diǎn),最主要還是看它是否能夠滿足您的需求。
總的來說,選擇數(shù)據(jù)庫可以從以下角度考慮:
從個(gè)人角度出發(fā)的話,如果是以學(xué)習(xí)和小型業(yè)務(wù)需求為主,推薦使用MySQL,它的優(yōu)勢在于:
成本(免費(fèi))
自由(完全開源,適用多個(gè)場景)
性能(體積小但速度快)
這三點(diǎn)決定了MySQL數(shù)據(jù)庫的超高性價(jià)比。并且目前有不少主流公司仍然青睞MySQL,大名鼎鼎的Fackbook就依然在延續(xù)MySQL的使用。
2. 如果是企業(yè)角度出發(fā),主流的大型數(shù)據(jù)庫如Oracle、Sql Server...以及近些年來大數(shù)據(jù)領(lǐng)域十分火熱的非關(guān)系型數(shù)據(jù)庫,例如Redis、HBse等等,都可以作為考慮的對(duì)象。
接下來具體列舉一些常用數(shù)據(jù)庫的優(yōu)缺點(diǎn),希望能為大家提供參考:
MySQL:
優(yōu)勢:
MySQL是開放源代碼的數(shù)據(jù)庫,任何人都可以獲得該數(shù)據(jù)庫的源代碼。
MySQL能夠?qū)崿F(xiàn)跨平臺(tái)操作,可以在Windows、UNIX、Linux和Mac OS等操作系統(tǒng)上運(yùn)行。
MySQL數(shù)據(jù)庫是一款自由軟件,大部分應(yīng)用場景下都是免費(fèi)使用。
MySQL功能強(qiáng)大且使用方便,社區(qū)生態(tài)繁榮,有諸多學(xué)習(xí)資料。
缺點(diǎn):規(guī)模小,功能有限。
SQL Server
高度可擴(kuò)展:可以從單一的筆記本電腦上運(yùn)行任何東西或以高倍云服務(wù)器網(wǎng)絡(luò)運(yùn)行,或在兩者之間任何東西。
“雖然說是“任何東西”,但是仍然要滿足相關(guān)的軟件和硬件的要求“
生態(tài)鏈廣:具有內(nèi)置的商務(wù)智能工具,以及一系列的分析和報(bào)告工具,可以創(chuàng)建數(shù)據(jù)庫、備份、復(fù)制,帶來了更好的安全性。
Oracle
Oracle數(shù)據(jù)庫系統(tǒng)是目前世界上流行的關(guān)系數(shù)據(jù)庫管理系統(tǒng),具有以下特點(diǎn):
可移植性好(在各類大、中、小、微機(jī)環(huán)境中都適用)
使用方便、
功能強(qiáng)
因此,Oracle是一種高效率、可靠性好的、適應(yīng)高吞吐量的數(shù)據(jù)庫解決方案。
DB2
DB2是IBM開發(fā)的一種大型關(guān)系型數(shù)據(jù)庫平臺(tái)。它支持多用戶或應(yīng)用程序在同一條SQL 語句中查詢不同database甚至不同DBMS中的數(shù)據(jù)。它的應(yīng)用特點(diǎn)如下:
支持面向?qū)ο蟮木幊蹋篸b2支持復(fù)雜的數(shù)據(jù)結(jié)構(gòu),如無結(jié)構(gòu)文本對(duì)象,可以對(duì)無結(jié)構(gòu)文本對(duì)象進(jìn)行布爾匹配、最接近匹配和任意匹配等搜索。可以建立用戶數(shù)據(jù)類型和用戶自定義函數(shù)。
支持多媒體應(yīng)用程序:db2支持大二分對(duì)象(blob),允許在數(shù)據(jù)庫中存取二進(jìn)制大對(duì)象和文本大對(duì)象。其中,二進(jìn)制大對(duì)象可以用來存儲(chǔ)多媒體對(duì)象。
具有良好的備份和恢復(fù)能力
支持存儲(chǔ)過程和觸發(fā)器,用戶可以在建表時(shí)顯示的定義復(fù)雜的完整性規(guī)則
支持異構(gòu)分布式數(shù)據(jù)庫訪問,支持?jǐn)?shù)據(jù)復(fù)制
PostgreSQL
PostgreSQL 是一個(gè)免費(fèi)的對(duì)象-關(guān)系數(shù)據(jù)庫服務(wù)器(ORDBMS),它的 Slogan 是 “世界上最先進(jìn)的開源關(guān)系型數(shù)據(jù)庫”。
PostgreSQL具有如下特征:
函數(shù):通過函數(shù),可以在數(shù)據(jù)庫服務(wù)器端執(zhí)行指令程序。
索引:用戶可以自定義索引方法,或使用內(nèi)置的 B 樹,哈希表與 GiST 索引。
觸發(fā)器:觸發(fā)器是由SQL語句查詢所觸發(fā)的事件。如:一個(gè)INSERT語句可能觸發(fā)一個(gè)檢查數(shù)據(jù)完整性的觸發(fā)器。觸發(fā)器通常由INSERT或UPDATE語句觸發(fā)。 多版本并發(fā)控制:PostgreSQL使用多版本并發(fā)控制(MVCC,Multiversion concurrency control)系統(tǒng)進(jìn)行并發(fā)控制,該系統(tǒng)向每個(gè)用戶提供了一個(gè)數(shù)據(jù)庫的”快照”,用戶在事務(wù)內(nèi)所作的每個(gè)修改,對(duì)于其他的用戶都不可見,直到該事務(wù)成功提交。
規(guī)則:規(guī)則(RULE)允許一個(gè)查詢能被重寫,通常用來實(shí)現(xiàn)對(duì)視圖(VIEW)的操作,如插入(INSERT)、更新(UPDATE)、刪除(DELETE)。
數(shù)據(jù)類型:包括文本、任意精度的數(shù)值數(shù)組、JSON 數(shù)據(jù)、枚舉類型、XML 數(shù)據(jù)等。
全文檢索:通過 Tsearch2 或 OpenFTS,8.3版本中內(nèi)嵌 Tsearch2。
NoSQL:JSON,JSONB,XML,HStore 原生支持,至 NoSQL 數(shù)據(jù)庫的外部數(shù)據(jù)包裝器。
數(shù)據(jù)倉庫:能平滑遷移至同屬 PostgreSQL 生態(tài)的 GreenPlum,DeepGreen,HAWK 等,使用 FDW 進(jìn)行 ETL
postgreSQL是一款先進(jìn)的開源數(shù)據(jù)庫,擁有非常齊全的自由軟件的對(duì)象-關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(ORDBMS),可面向企業(yè)復(fù)雜SQL的OLTP業(yè)務(wù)場景,支持多項(xiàng)企業(yè)級(jí)功能,能解決使用數(shù)據(jù)庫的各種難題。
PostgreSQL的優(yōu)勢有很多。它是一個(gè)免費(fèi)的對(duì)象-關(guān)系數(shù)據(jù)庫服務(wù)器(ORDBMS),在靈活的BSD許可證下發(fā)行。
postgreSQL的特征
函數(shù):通過函數(shù),可以在數(shù)據(jù)庫服務(wù)器端執(zhí)行指令程序。
索引:用戶可以自定義索引方法,或使用內(nèi)置的 B 樹,哈希表與 GiST 索引。
觸發(fā)器:觸發(fā)器是由SQL語句查詢所觸發(fā)的事件。如:一個(gè)INSERT語句可能觸發(fā)一個(gè)檢查數(shù)據(jù)完整性的觸發(fā)器。觸發(fā)器通常由INSERT或UPDATE語句觸發(fā)。 多版本并發(fā)控制:PostgreSQL使用多版本并發(fā)控制(MVCC,Multiversion concurrency control)系統(tǒng)進(jìn)行并發(fā)控制,該系統(tǒng)向每個(gè)用戶提供了一個(gè)數(shù)據(jù)庫的"快照",用戶在事務(wù)內(nèi)所作的每個(gè)修改,對(duì)于其他的用戶都不可見,直到該事務(wù)成功提交。
規(guī)則:規(guī)則(RULE)允許一個(gè)查詢能被重寫,通常用來實(shí)現(xiàn)對(duì)視圖(VIEW)的操作,如插入(INSERT)、更新(UPDATE)、刪除(DELETE)。
數(shù)據(jù)類型:包括文本、任意精度的數(shù)值數(shù)組、JSON 數(shù)據(jù)、枚舉類型、XML 數(shù)據(jù)等。全文檢索:通過 Tsearch2 或 OpenFTS,8.3版本中內(nèi)嵌 Tsearch2。
NoSQL:JSON,JSONB,XML,HStore 原生支持,至 NoSQL 數(shù)據(jù)庫的外部數(shù)據(jù)包裝器。
數(shù)據(jù)倉庫:能平滑遷移至同屬postgreSQL生態(tài)的GreenPlum,DeepGreen,HAWK 等,使用 FDW 進(jìn)行 ETL。