① 內存條存儲數據的原理
內存的存儲原理
內存,英文名為RAM(Random Access Memory),全稱是隨機存取存儲器。主要的作用就是存儲代碼和數據供CPU在需要的時候調用。但是這些數據並不是像用木桶盛水那麼簡單,而是類似圖書館中用有格子的書架存放書籍一樣,不但要放進去還要能夠在需要的時候准確的調用出來,雖然都是書但是每本書是不同的。對於內存等存儲器來說也是一樣的,雖然存儲的都是代表0和1的代碼,但是不同的組合就是不同的數據。讓我們重新回到書和書架上來。
如果有一個書架上有10行和10列格子(每行和每列都有0~9編號),有100本書要存放在裡面,那麼我們使用一個行的編號和一個列的編號就能確定某一本書的位置。如果已知這本書的編號36,那麼我們首先鎖定第3行,然後找到第6列就能准確的找到這本書了。
在內存中也是利用了相似的原理現在讓我們回到內存上,對於它而言數據匯流排是用來傳入數據或者傳出數據的。因為存儲器中的存儲空間是如果前面提到的存放圖書的書架一樣通過一定的規則定義的,所以我們可以通過這個規則來把數據存放到存儲器上相應的位置,而進行這種定位的工作就要依靠地址匯流排來實現了。
對於CPU來說,內存就像是一條長長的有很多空格的「線」,每個空格都有一個唯一的地址與之相對應。如果CPU想要從內存中調用數據,它首先需要給地址匯流排發送地址數據定位要存取的數據,然後等待若干個時鍾周期之後,數據匯流排就會把數據傳輸給CPU。當地址解碼器接收到地址匯流排送來的地址數據之後,它會根據這個數據定位CPU想要調用的數據所在的位置,然後數據匯流排就會把其中的數據傳送到CPU。
CPU在一行數據中每次知識存取一個位元組的數據。會到實際中,通常CPU每次需要調用64bit或者是128bit的數據(單通道內存控制器為64bit,雙通道為128bit)。如果數據匯流排是64bit的話,CPU就會在一個時間中存取8個位元組的數據,因為每次還是存取1個位元組的數據,64bit匯流排將不會顯示出來任何的優勢,工作的效率將會降低很多。這也就是現在的主板和CPU都使用雙通道內存控制器的原因。
② 分析大文本與圖像數據在資料庫內部的存儲原理。
圖像數據在資料庫內部的存儲原理:
XML 是文本型的數據交換結構,對於字元類型的文本交換非常的方便,實際工作中我們往往需要通過 XML 將二進制格式的圖形圖像信息數據進行數據交換。本文從介紹 BASE64 編碼的原理入手,通過採用 C 語言編寫 DB2 的嵌入存儲過程,實現了在資料庫內存中將文本格式的圖片文件到二進制 BLOB 欄位之間的轉換,並且就性能優化等提出若干建議,該設計思路和程序可以廣泛的應用到圖像圖形數據在 XML 的存儲和轉換。
--------------------------------------------------------------------------------
回頁首
XML 存儲圖形圖像的基本原理
XML 作為一種非常廣泛的數據交換的載體被廣泛的應用到了各行各業的數據交換中。對於圖形圖像數據的轉換,需要採用 Base64 編碼將二進制格式的圖形圖像信息轉換成文本格式再進行傳輸。
Base64 編碼轉換的思想是通過 64 個 ASCII 字元碼對二進制數據進行重新編碼組合,即將需要轉換的數據每三個位元組(24 位)為一組,再將這 24 位數據按每組 6 位進行重新劃分,在每組的最高 2 位填充 0 最終成一個完整的 8 位位元組。如果所要編碼的數據的位元組數不是 3 的整數倍,需要在最後一組數據填充 1 到 2 個位元組的 0 位元組。例如:我們對 ABC 進行 BASE64 的編碼,ABC 的編碼值:A(65), B(66), C(67)。再取二進制 A(01000001)B(01000010)C(01000011)連接起來構成 010000010100001001000011,然後按 6 位為單位分成 4 個數據塊並在最高位填充兩個 0 後形成 4 個位元組的編碼後的值(00010000)(00010100)(00001001)(00000011)。再將 4 個位元組的數據轉換成十進制數為(16)(20)(19)(3)。最後根據 BASE64 給出的 64 個基本字元表,查出對應的 ASCII 碼字元(Q)(U)(J)(D)。這里的值實際就是數據在字元表中的索引。
BASE64 字元表:
。
某項目的數據交換採用 XML 的為介質,XML 的結構包括個人基本信息:姓名、性別、相片等信息,其中相片信息是採用經過 BASE64 函數轉換後的文本型數據,圖像圖形信息通過 BASE64 進行數據轉換後,形成文本格式的數據類型,再將相應的數據存放到 XML 中,最終形成可供交換的文本型的 XML 數據結構。
XML 的數據結構如下所示:
<?xml version=」1.0」 encoding=」UTF-8」 ?>
<HeadInfo>
<TotalNum>10<TotalNum>
<TransDate>2007-10-18</TransDate>
</HeadInfo>
<Data>
<Name> 張三 </Name>
<Sex> 男 </Sex>
<Photo>/9j/4AAQSkZJRgABAQAAAQABAAD......</Photo>
<Data>
--------------------------------------------------------------------------------
回頁首
相片數據在 DB2 嵌入式 C 程序的實現方法
該項目要求能夠在 DB2 資料庫中將相片數據存儲為二進制 BLOB 格式。我們採用 DATASTAGE 進行 XML 數據載入,將 XML 中的姓名、性別等基本數據項載入到相應的欄位,其中文本型的相片數據則載入到 CLOB 欄位中,再按照 BASE64 的編碼規則進行逆向轉碼,整個數據流程如下圖所示:
圖 1. 相片存儲流程圖
用戶的相片每天的更新數據為 30 萬條,而且每個相片的平均大於 32KB,為了獲得最佳的資料庫性能,選擇採用 C 存儲過程的方式開發了 BASE64 的轉換函數。每次函數讀取存儲在 CLOB 欄位的文本格式數據全部存儲到內存中,並且通過 decode 函數在內存中進行轉碼,轉碼後再存入資料庫中。
程序的清單 1 是逐行讀取 CLOB 欄位,並且調用 decode 函數進行轉碼;程序的清單 2 是 decode 函數的關鍵性代碼。完整的程序見源代碼下載部分。
清單 1. 讀入 CLOB,寫入 BLOB 欄位
EXEC sql BEGIN DECLARE SECTION;
SQL TYPE IS CLOB(100 K) clobResume; //CLOB 結構體變數
SQL TYPE IS BLOB(100 K) blobResume; //BLOB 結構體變數
sqlint16 bobind;
sqlint16 lobind;
sqlint16 cobind;
sqlint32 idValue;
EXEC SQL END DECLARE SECTION;
int clob2bin(void)
{
// 聲明 SQLCA 結構
struct sqlca sqlca;
int charNb;
int lineNb;
long n;
n=0;
// 定義資料庫游標
EXEC SQL DECLARE c1 CURSOR WITH HOLD FOR
SELECT czrkxp_a
FROM CZRK_blob for update;
EXEC SQL OPEN c1;
// 活動 CLOB 欄位的信息,已經 CLOB 欄位的大小
EXEC SQL FETCH c1 INTO :clobResume:cobind;
// 循環讀取 CLOB 欄位,並且調用 DECODE 轉碼函數
while (sqlca.sqlcode != 100)
{
if (cobind < 0)
{
printf(「 NULL LOB indicated.\n」);
}
else
{
n++;
decode(); // 文本格式到二進制流的轉碼函數
printf(「\nCurrent Row =%ld」,n);
// 數據寫入 BLOB 欄位
EXEC SQL update czrk_blob set czrkxp_blob = :blobResume
where current of c1; ;
// 提交事務
EXEC SQL COMMIT;
}
EXEC SQL FETCH c1 INTO :clobResume:cobind ;
}
// 關閉游標
EXEC SQL CLOSE c1;
EXEC SQL COMMIT;
return 0;
}
清單 2. 文本文件到二進制文件的轉換
void decode( void )
{
unsigned char in[4], out[3], v;
int I, len;
long j,k;
j = -1;
k=0;
// 將讀入 CLOB 結構體變數的數據進行轉換
while( j < clobResume.length){
for( len = 0, I = 0; I < 4 && ( j < clobResume.length ); i++ ) {
v = 0;
while((j < clobResume.length) && v == 0 ) {
j++;
v = (unsigned char) clobResume.data[j];
v = (unsigned char) ((v < 43 || v > 122) ? 0 : cd64[ v – 43 ]);
if( v ) {
v = (unsigned char) ((v == 『$』) ? 0 : v – 61);
}
}
if( j < clobResume.length ) {
len++;
if( v ) {
in[ I ] = (unsigned char) (v – 1);
}
}
else {
in[i] = 0;
}
}
if( len ) {
decodeblock( in, out );
// 寫入到 BLOB 結構體變數中
for( I = 0; I < len – 1; i++ ) {
blobResume.data[k] = out[i];
k++;
}
}
}
blobResume.length= k;
}
--------------------------------------------------------------------------------
回頁首
數據的轉換效率和優化建議
在 IBM P570 資料庫伺服器上運行,該程序的運行效率非常高,先後進行了幾個數量級的測試,最終平均測試的轉換效率為:每 1 萬筆數據記錄,轉換的效率 55 秒,即 182 條 / 秒。值得注意的是,整個轉換過程佔用 CPU 的量並不特別大,主要的性能瓶頸在磁碟陣列中。
以後可以進一步在以下方面進行調優,確保程序轉換的效率更高:
1)採用多進程調用的方式,以獲得更高的並發數量;
2)採用每 10 次或者 100 次提交事務的方式,減少訪問磁碟的次數;
3)將 CLOB 和 BLOB 分別放置在不同的表空間上,並且將表空間分布在在多個磁碟上,獲得最佳的磁碟訪問速度。
③ 請教一下,資料庫加密的原理是什麼
資料庫加密亂塌的底層原理本質上是TDE(Transparent Data Encryption)技術,即一種透明數據加密技術,在資料庫主程序啟動時載入擴展的TDE插件。TDE插件技術可以實現:在寫入存儲介質前將數據加密,返歲實現數據的存儲加密;在從存儲介質載入數據到內存前進行數據解密,實現數據的解密使用;利用TDE插件的增強訪問控制能力實現獨立於資料庫原漏陪睜有許可權體系的增強的權控功能。安華金和資料庫加密產品就是依託這種技術實現的,想要了解更多,找他們咨詢一下吧。可以去上網路看看。
④ memsql資料庫操作
1、memsql 分布式內存資料庫瞎凳鍵,號稱是速度最快的關系資料庫。由前Facebook工程師磨巧創建,兼容MySQL,但比MySQL快30倍,能實現每秒150萬次事務。原理是僅用內存並將SQL預編譯為C++。
2、你的問題,memsql 是怎麼保證數據存入沒錯,這是資料庫的基本功能,保證數據的保存和讀取,這是通過資料庫事務、以及鎖功能來實現的,具體的原理闡述請查閱資料庫基礎書籍。
3、希望對你有幫助。祝粗指你學有所得。
⑤ 搞軟體開發,請你來談談資料庫連接池的原理吧
這次我們採取技術演進的方式來談談資料庫連接池的技術出現過程及其原理,以及當下最流行的開源資料庫連接池jar包。
1、原理
一般來說,Java應用程序訪問資料庫的過程是 :
①裝載資料庫驅動程序;
②通過jdbc建立資料庫連接;
③訪問資料庫,執行sql語句;
④斷開資料庫連接。
2、代碼
3、分析
程序開發過程中,存在很多問題:首先,每一次web請求都要建立一次資料庫連接。建立連接是一個費時的活動,每次都得花費0.05s~1s的時間,而且系統還要分配內存資源。這個時間對於一次或幾次資料庫操作,或許感覺不出系統有多大的開銷。可是對於現在的web應用,尤其是大型電子商務網站,同時有幾百人甚至幾千人在線是很正常的事。在這種情況下,頻繁的進行資料庫連接操作勢必佔用很多的系統資源,網站的響應速度必定下降,嚴重的甚至會造成伺服器的崩潰。不是危言聳聽,這就是制約某些電子商務網站發展的技術瓶頸問題。其次,對於每一次資料庫連接,使用完後都得斷開。否則,如果程序出現異常而未能關閉,將會導致資料庫系統中的內存泄漏,最終將不得不重啟資料庫。還有,這種開發不能控制被創建的連接對象數,系統資源會被毫無顧及的分配出去,如連接過多,也可能導致內存泄漏,伺服器崩潰。
上述的用戶查詢案例,如果同時有1000人訪問,就會不斷的有資料庫連接、斷開操作:
通過上面的分析,我們可以看出來,「資料庫連接」是一種稀缺的資源,為了保障網站的正常使用,應該對其進行妥善管理。其實我們查詢完資料庫後,如果不關閉連接,而是暫時存放起來,當別人使用時,把這個連接給他們使用。就避免了一次建立資料庫連接和斷開的操作時間消耗。原理如下:
由上面的分析可以看出,問題的根源就在於對資料庫連接資源的低效管理。我們知道,對於共享資源,有一個很著名的設計模式:資源池(resource pool)。該模式正是為了解決資源的頻繁分配﹑釋放所造成的問題。為解決上述問題,可以採用資料庫連接池技術。資料庫連接池的基本思想就是為資料庫連接建立一個「緩沖池」。預先在緩沖池中放入一定數量的連接,當需要建立資料庫連接時,只需從「緩沖池」中取出一個,使用完畢之後再放回去。我們可以通過設定連接池最大連接數來防止系統無盡的與資料庫連接。更為重要的是我們可以通過連接池的管理機制監視資料庫的連接的數量﹑使用情況,為系統開發﹑測試及性能調整提供依據。
我們自己嘗試開發一個連接池,來為上面的查詢業務提供資料庫連接服務:
① 編寫class 實現DataSource 介面
② 在class構造器一次性創建10個連接,將連接保存LinkedList中
③ 實現getConnection 從 LinkedList中返回一個連接
④ 提供將連接放回連接池中方法
1、連接池代碼
2、使用連接池重構我們的用戶查詢函數
這就是資料庫連接池的原理,它大大提供了資料庫連接的利用率,減小了內存吞吐的開銷。我們在開發過程中,就不需要再關心資料庫連接的問題,自然有資料庫連接池幫助我們處理,這回放心了吧。但連接池需要考慮的問題不僅僅如此,下面我們就看看還有哪些問題需要考慮。
1、並發問題
為了使連接管理服務具有最大的通用性,必須考慮多線程環境,即並發問題。這個問題相對比較好解決,因為java語言自身提供了對並發管理的支持,使用synchronized關鍵字即可確保線程是同步的。使用方法為直接在類方法前面加上synchronized關鍵字,如:
2、多資料庫伺服器和多用戶
對於大型的企業級應用,常常需要同時連接不同的資料庫(如連接oracle和sybase)。如何連接不同的資料庫呢?我們採用的策略是:設計一個符合單例模式的連接池管理類,在連接池管理類的唯一實例被創建時讀取一個資源文件,其中資源文件中存放著多個資料庫的url地址等信息。根據資源文件提供的信息,創建多個連接池類的實例,每一個實例都是一個特定資料庫的連接池。連接池管理類實例為每個連接池實例取一個名字,通過不同的名字來管理不同的連接池。
對於同一個資料庫有多個用戶使用不同的名稱和密碼訪問的情況,也可以通過資源文件處理,即在資源文件中設置多個具有相同url地址,但具有不同用戶名和密碼的資料庫連接信息。
3、事務處理
我們知道,事務具有原子性,此時要求對資料庫的操作符合「all-all-nothing」原則即對於一組sql語句要麼全做,要麼全不做。
在java語言中,connection類本身提供了對事務的支持,可以通過設置connection的autocommit屬性為false 然後顯式的調用commit或rollback方法來實現。但要高效的進行connection復用,就必須提供相應的事務支持機制。可採用每一個事務獨佔一個連接來實現,這種方法可以大大降低事務管理的復雜性。
4、連接池的分配與釋放
連接池的分配與釋放,對系統的性能有很大的影響。合理的分配與釋放,可以提高連接的復用度,從而降低建立新連接的開銷,同時還可以加快用戶的訪問速度。
對於連接的管理可使用空閑池。即把已經創建但尚未分配出去的連接按創建時間存放到一個空閑池中。每當用戶請求一個連接時,系統首先檢查空閑池內有沒有空閑連接。如果有就把建立時間最長(通過容器的順序存放實現)的那個連接分配給他(實際是先做連接是否有效的判斷,如果可用就分配給用戶,如不可用就把這個連接從空閑池刪掉,重新檢測空閑池是否還有連接);如果沒有則檢查當前所開連接池是否達到連接池所允許的最大連接數(maxconn)如果沒有達到,就新建一個連接,如果已經達到,就等待一定的時間(timeout)。如果在等待的時間內有連接被釋放出來就可以把這個連接分配給等待的用戶,如果等待時間超過預定時間timeout 則返回空值(null)。系統對已經分配出去正在使用的連接只做計數,當使用完後再返還給空閑池。對於空閑連接的狀態,可開辟專門的線程定時檢測,這樣會花費一定的系統開銷,但可以保證較快的響應速度。也可採取不開辟專門線程,只是在分配前檢測的方法。
5、連接池的配置與維護
連接池中到底應該放置多少連接,才能使系統的性能最佳?系統可採取設置最小連接數(minconn)和最大連接數(maxconn)來控制連接池中的連接。最小連接數是系統啟動時連接池所創建的連接數。如果創建過多,則系統啟動就慢,但創建後系統的響應速度會很快;如果創建過少,則系統啟動的很快,響應起來卻慢。這樣,可以在開發時,設置較小的最小連接數,開發起來會快,而在系統實際使用時設置較大的,因為這樣對訪問客戶來說速度會快些。最大連接數是連接池中允許連接的最大數目,具體設置多少,要看系統的訪問量,可通過反復測試,找到最佳點。
如何確保連接池中的最小連接數呢?有動態和靜態兩種策略。動態即每隔一定時間就對連接池進行檢測,如果發現連接數量小於最小連接數,則補充相應數量的新連接以保證連接池的正常運轉。靜態是發現空閑連接不夠時再去檢查。
理解了連接池的原理就可以了,沒有必要什麼都從頭寫一遍,那樣會花費很多時間,並且性能及穩定性也不一定滿足要求。事實上,已經存在很多流行的性能優良的第三方資料庫連接池jar包供我們使用。如:
其中c3p0已經很久沒有更新了。DBCP更新速度很慢,基本處於不活躍狀態,而Druid和HikariCP處於活躍狀態的更新中。
⑥ 面試中問到Redis持久化的原理,本篇在做詳細解答
我們知道redis是一個 高效的分布式內存資料庫 ,由於是操作內存所以性能非常之快,通常用它來做分布式緩存,用來提高微服務的高性能,但是因為是內存操作,所以當出現伺服器故障,斷電等情況就會造成 內存數據丟失 ,不可恢復,因此redis 引入了持久化機制來將內存數據寫入圓帶磁碟,從而保障了Redis的數據不被丟失。
Redis有兩種持久化的方式,一種是RDB,另外種是AOF。
RDB是將Redis內存中數據的快照存儲在磁碟內,是Redis的默認持久化方案。
RDB持久化默認有三種策略
可在redis.conf中配置,會以一段時間內達到指定修改的次數為規則來觸發快照操作,快照文件名為mp.rdb。每當Redis服務重啟的時候都會從該文件中把數據載入到內存中。
在60秒內有10000次操作即觸發RDB持久化。
沒有滿足第一種條件時,在900秒內有1次操作即觸發RDB持久化。
沒有滿足第二種條件時,在300秒內有10次操作即觸發RDB持久化。
RDB持久化除了可以根據配置中的策略來觸發外,還可以使用save和bgsave命令手動來觸發。這兩個命令的區別在於save會阻塞伺服器進程。在執行save命令的過程中,伺服器不能處理任何請求,但是bgsave(background save,後台保存)命令會通過一個子進程在後台處理數據RDB持久化。本質上save和bgsave調用的都是rdbSave函數,所以Redis不允許save和bgsave命令同時執行,當然這也是為了避免RDB文件數據出現不一致性的問題。
每次都是一個大文件,備份寫入IO操作筆記大,很容易耗時,影響進程資源使用。
如果最近一次進程崩潰,那麼最近一次數據備份後的數據就被丟失。
文件直接就可以當冷備使用
AOF(Append Only File)以獨立日誌的方式記錄每次的寫命令,可以很好地解決了數據持久化的實時性。系統重啟時可以重新執行AOF文件中的命令來恢復數據。AOF會先把命令追加畝飢在AOF緩沖區,然後根據對應策略寫入硬碟。
AOF的實現流程有三個步驟
步驟一
把命令追加到AOF緩沖區,
步驟二
將緩沖區的內容寫入程序緩沖區
步驟三
將程序緩沖區的內容寫入文件
當AOF持久化功能處於開啟狀態時,伺服器每執行完一個命令就會將命令以迅腔返協議格式追加寫入redisServer結構體的aof_buf緩沖區。而在服務重啟的時候會把AOF文件載入到緩沖區中。
AOF有 三種觸發機制
·always:每次發生數據變更都會被立即記錄到磁碟,性能較差,但數據完整性比較好。
·everysec:每秒鍾將aof_buf緩沖區的內容寫入AOF文件,如果宕機,就會有1秒內的數據丟失。
·no:將數據同步操作交給操作系統來處理,性能最好,但是數據可靠性最差。在配置文件中設置appendonly=yes後,若沒有指定apendfsync,默認會使用everysec選項。
寫入指令隨著時間的推移,記錄了很多重復的指令,導致數據量非常大。
RDB優先順序高於AOF
RDB小,AOF較大
RDB慢,AOF快
RDB快,AOF慢
⑦ sqlitememory原理
SQLite創建的資料庫有一種模式IN-MEMORY,但是它並不表示SQLite就成了一個內存資料庫。IN-MEMORY模式可以簡單地理解為,(2020 表述勘誤:本來創建的資料庫文件是基於磁碟的,現在整個文件使用內存空間來代替磁碟空間,沒有了文件作為backingstore,不必在修改資料庫後將緩存頁提交到文件系統),其它操作保持一致。也就是資料庫的設計沒有根本改變。
inmemory與tempdb是兩種節約模式,節約的對象為(rollback)日誌文件以及資料庫文件,減少IO。inmemory將日誌寫在內存,並且去除資料庫文件作為backingStore,緩存頁不用提交到文件系統。tempdb只會在只會在臟的緩存頁超過當前總量的25%才會同步刷寫到文件,換句話說在臨時資料庫模式下,事務提交時並不總同步臟頁,因此減少了IO數量,事務日誌也受這種機制影響,所以在臨時資料庫模式下,事務日誌是不是MEMORY並不重要。回過頭來看,內存模式則是臨時模式的一種極致,杜絕所有的IO。這兩種模式都只能存在一個sqlite3連接,關閉時銷毀。
提到內存,許多人就會簡單地理解為,內存比磁碟速度快很多,所以內存模式比磁碟模式的資料庫速度也快很多,甚至有人望文生意就把它變成等同於內存資料庫。
它並不是為內存資料庫應用而設計的,本質還是文件資料庫。它的資料庫存儲文件有將近一半的空間是空置的,這是它的B樹存儲決定的,(2020 勘誤:對於固定長度記錄,頁面使用率最大化,對於非自增計數鍵的索引,頁面一般會保留20~扒襪60%的空間,方便插入)請參看上一篇SQLite存儲格式。內春睜激存模式只是將資料庫存儲文件放入內存空間,但並不考慮最有效管理你的內存空間,其它臨時文件也要使用內存,事務回滾日誌一樣要生成,只是使用了內存空間。它的作用應該偏向於臨時性的用途。
(2020 補充:下面的測試有局限性,)
我們先來看一下下面的測試結果,分別往memory和disk模式的sqlite資料庫進行1w, 10w以及100w條數據的插入,採用一次性提交事務。另外使用commit_hook捕捉事務提交次數。
(註:測試場景為早襲在新建的資料庫做插入操作,所以回滾日誌是很小的,並且無需要在插入過程中查找而從資料庫載入頁面,因此測試也並不全面)
內存模式
磁碟模式
在事務提交前的耗時 (事務提交後的總耗時):
1w 10w 100w
內存模式 0.04s 0.35s 3.60s
磁碟模式 0.06s (0.27s) 0.47s (0.72s) 3.95s (4.62s)
可以看到當操作的數據越少時,內存模式的性能提高得越明顯,事務IO的同步時間消耗越顯注。
上圖還有一組數據比較,就是在單次事務提交中,如果要為每條插入語句准備的話
1w 10w 100w
內存模式 0.19s 1.92s 19.46s
磁碟模式 0.21s (0.35s) 2.06s (2.26s) 19.88s (20.41s)
我們從SQLite的設計來分析,一次插入操作,SQLite到底做了些什麼。首先SQLite的資料庫操作是以頁面大小為單位的。在單條記錄插入的事務中,回滾日誌文件被創建。在B樹中查找目標頁面,要讀入一些頁面,然後將目標頁面以及要修改的父級頁面寫出到回滾日誌。操作目標頁面的內存映像,插入一條記錄,並在頁面內重排序(索引排序,無索引做自增計數排序,參看上一篇《SQLite資料庫存儲格式》)。最後事務提交將修改的頁面寫出到資料庫文件,成功後再刪除日誌文件。在這過程中顯式進行了2次寫磁碟(1次寫日誌文件,1次同步寫資料庫),還有2次隱式寫磁碟(日誌文件的創建和刪除),這是在操作目錄節點。以及為查找載入的頁面讀操作。更加詳細可以參看官方文檔的討論章節《Atomic Commit In SQLite》。
如果假設插入100條記錄,每條記錄都要提交一次事務就很不劃算,所以需要批量操作來減少事務提交次數。假設頁面大小為4KB,記錄長度在20位元組內,每頁可放多於200條記錄,一次事務提交插入100條記錄,假設這100條記錄正好能放入到同一頁面又沒有產生頁面分裂,這樣就可以在單條記錄插入事務的IO開銷耗損代價中完成100條記錄插入。
當我們的事務中,插入的數據越多,事務的IO代價就會攤得越薄,所以在插入100w條記錄的測試結果中,內存模式和磁碟模式的耗時都十分接近。實際應用場合中也很少會需要一次插入100w的數據。有這樣的需要就不要考慮SQLite。
(補充說明一下,事務IO指代同步資料庫的IO,以及回滾日誌的IO,只在本文使用)
除了IO外,還有沒有其它地方也影響著性能。那就是語句執行。其實反觀一切,都是在對循環進行優化。
for (i = 0; i < repeat; ++i)
{
exec("BEGIN TRANS");
exec("INSERT INTO ...");
exec("END TRANS");
}
批量插入:
exec("BEGIN TRANS");
for (i = 0; i < repeat; ++i)
{
exec("INSERT INTO ...");
}
exec("END TRANS");
當我們展開插入語句的執行
exec("BEGIN TRANS");
for (i = 0; i < repeat; ++i)
{
// unwind exec("INSERT INTO ...");
prepare("INSERT INTO ...");
bind();
step();
finalize();
}
exec("END TRANS");
又發現循環內可以移出部分語句
exec("BEGIN TRANS");
// unwind exec("INSERT INTO ...");
prepare("INSERT INTO ...");
for (i = 0; i < repeat; ++i)
{
bind();
step();
}
finalize();
exec("END TRANS");
這樣就得到了批量插入的最終優化模式。
所以對sql語句的分析,編譯和釋放是直接在損耗CPU,而同步IO則是在飢餓CPU。
請看下圖
分別為內存模式1w和10w兩組測試,每組測試包括4項測試
1.只編譯一條語句,只提交一次事務
2.每次插入編譯語句,只提交一次事務
3.只編譯一條語句,但使用自動事務。
4.每次插入編譯語句,並使用自動事務。
可以看到測試項目4基本上就是測試項目2和測試項目3的結果的和。
測試項目1就是批量插入優化的最終結果。
下面是探討內存模式的使用:
經過上面的分析,內存模式在批量插入對比磁碟模式提升不是太顯注的,請現在開始關注未批量插入的結果。
下面給出的是磁碟模式0.1w和0.2w兩組測試,每組測試包括4項測試
可以看到在非批量插入情況,sqlite表現很差要100秒來完成1000次單條插入事務,但絕非sqlite很吃力,因為cpu在空載,IO阻塞了程序。
再來看內存模式20w測試
可以看到sqlite在內存模式,即使在20w次的單條插入事務,其耗時也不太遜於磁碟模式100w插入一次事務。
0.1w 0.2w 20w
內存模式(非批量插入) 15.87s
磁碟模式(非批量插入) 97.4s 198.28s
編譯1次插入語句 每次插入編譯1次語句
內存模式(20w,20w次事務) 11.10s 15.87s
磁碟模式(100w,1次事務) 4.62s 20.41s
⑧ 資料庫中多表連接的原理實現
多變關聯的實現方式有hash join,merge join,nested loop join 方式,具體使用那種內型的連接,主要依據:
1.當前的優化器模式(all_rows和rule)
2.取決於表的大小
3.取決於關聯欄位是否有索性
4.取決於關聯欄位是否排序
Hash join散列連接,優化器選擇較小的表(數悶擾據量少的表)利用連接鍵(join key)在內存中建立散列表,將數據存儲到hash列表中,然後掃描較大的表
select A.*,B.* from A left join B on a.id=b.id。
先是從A表讀取一條記錄,用on條件匹配B表的記錄,行成n行(包括重復行)如果B表沒有與匹配的數據,則select中B表的欄位顯示為空,接著讀取A表的下一條記錄,right join類似。
left join基本是A表全部掃描,在表關鍵中不建議使用子查詢作為副表,比如select A.*,B.*from A left join (select * from b where b.type=1 )這樣A表是全表掃描,B表也是全表掃描。若果查詢慢,可以考慮關聯的欄位都建索引,將不必要的排序去掉,排序會導致運行慢很多。明晌
主副表條件過濾:
table a(id, type):
id type
----------------------------------
1 1
2 1
3 2
表b結構和數據
table b(id, class):
id class
---------------------------------
1 1
2 2
Sql語句1: select a.*, b.* from a left join b on a.id = b.id and a.type = 1;
執行結果為:
a.id a.type b.id b.class
----------------------------------------
1 1 1 1
2 1 2 2
3 2
a.type=1沒有起作用
sql語句2:
select a.*, b.* from a left join b on a.id = b.id where a.type = 1;
執行結果為:
a.id a.type b.id b.class
----------------------------------------
1 1 1 1
2 1 2 2
sql語句3:
select a.*, b.* from a left join b on a.id = b.id and b.class = 1;
執行結果為:
a.id a.type b.id b.class
----------------------------------------
1 1 1 1
2 1
3 2
b.class=1條件過濾成螞槐旦功。
結論:left join中,左表(主表)的過濾條件在on後不起作用,需要在where中添加。右表(副表)的過濾條件在on後面起作用。
Mysql join原理:
Mysql join採用了Nested Loop join的演算法,
###坐車 回去補充。
⑨ 內存的工作原理
內存工作原理
1.內存定址手衡或
首先,內存從CPU獲得查找某個數據的指令,然後再找出存取資料的位置時(這個動作稱為「定址」),它先定出橫坐標(也就是「列地址」)再定出縱坐標(也就是「行地址」),這就好像在地圖上畫個十字標記一畢伍樣,非常准確地定出這個地方。對於電腦系統而言,找出這個地方時還必須確定是否位置正確,因此電腦還必須判讀該地址的信號,橫坐標有橫坐標的信號(也就是RAS信號,Row Address Strobe)縱坐標有縱坐標的信號(也就是CAS信號,Column Address Strobe),最後再進行讀或寫的動作。因此,內存在讀寫時至少必須有五個步驟:分別是畫個十字(內有定地址兩個操作以及判讀地址兩個信號,共四個操作)以及或讀或寫的操作,才能完成內存的存取操作。
2.內存傳輸
為了儲存資料,或者是從內存內部讀取資料,CPU都會為這些讀取或寫入的資料編上地址(也就是我們所說的十字定址方式),這個時候,CPU會通過地址匯流排(Address Bus)將地址送到內攔陸存,然後數據匯流排(Data Bus)就會把對應的正確數據送往微處理器,傳回去給CPU使用。
3.存取時間
所謂存取時間,指的是CPU讀或寫內存內資料的過程時間,也稱為匯流排循環(bus cycle)。以讀取為例,從CPU發出指令給內存時,便會要求內存取用特定地址的特定資料,內存響應CPU後便會將CPU所需要的資料送給CPU,一直到CPU收到數據為止,便成為一個讀取的流程。因此,這整個過程簡單地說便是CPU給出讀取指令,內存回復指令,並丟出資料給CPU的過程。我們常說的6ns(納秒,秒-9)就是指上述的過程所花費的時間,而ns便是計算運算過程的時間單位。我們平時習慣用存取時間的倒數來表示速度,比如6ns的內存實際頻率為1/6ns=166MHz(如果是DDR就標DDR333,DDR2就標DDR2 667)。
4.內存延遲
內存的延遲時間(也就是所謂的潛伏期,從FSB到DRAM)等於下列時間的綜合:FSB同主板晶元組之間的延遲時間(±1個時鍾周期),晶元組同DRAM之間的延遲時間(±1個時鍾周期),RAS到CAS延遲時間:RAS(2-3個時鍾周期,用於決定正確的行地址),CAS延遲時間 (2-3時鍾周期,用於決定正確的列地址),另外還需要1個時鍾周期來傳送數據,數據從DRAM輸出緩存通過晶元組到CPU的延遲時間(±2個時鍾周期)。一般的說明內存延遲涉及四個參數CAS(Column Address Strobe 行地址控制器)延遲,RAS(Row Address Strobe列地址控制器)-to-CAS延遲,RAS Precharge(RAS預沖電壓)延遲,Act-to-Precharge(相對於時鍾下沿的數據讀取時間)延遲。其中CAS延遲比較重要,它反映了內存從接受指令到完成傳輸結果的過程中的延遲。大家平時見到的數據3—3—3—6中,第一參數就是CAS延遲(CL=3)。當然,延遲越小速度越快。
⑩ 資料庫索引的實現原理
資料庫索引的實現原理
一、概述資料庫索引,是資料庫管理系統中一個排序的數據結構,以協助快速查詢、更新資料庫表中數據。索引的實現通常使用B樹及其變種B+樹。在數據之外,資料庫系統還維護著滿足特定查找演算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找演算法。這種數據結構,就是索引。其實說穿了,索引問題就是一個查找問題。二、索引的原理當我們的業務產生了大量的數據時,查找數據的效率問題也就隨之而來,所以我們可以通過為表設置索引,而為表設置索引要付出代價的:一是增加了資料庫的存儲空間,二是在插入和修改數據時要花費較多的時間(因為索引也要隨之變動)。
上圖展示了一種可能的索引方式。左邊是數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址(注意邏輯上相鄰的記錄在磁碟上也並不是一定物理相鄰的)。為了加快Col2的查找,可以維護一個右邊所示的二叉查找樹,每個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就可以運用二叉查找在O(log2n)的復雜度內獲取到相應數據。索引是建立在資料庫表中的某些列的上面。在創建索引的時候,應該考慮在哪些列上可以創建索引,在哪些列上不能創建索引。一般來說,應該在這些列上創建索引:在經常需要搜索的列上,可以加快搜索的速度;在作為主鍵的列上,強制該列的唯一性和組織表中數據的排列結構;在經常用在連接的列上,這些列主要是一些外鍵,可以加快連接的速度;在經常需要根據范圍進行搜索的列上創建索引,因為索引已經排序,其指定的范圍是連續的;在經常需要排序的列上創建索引,因為索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;在經常使用在WHERE子句中的列上面創建索引,加快條件的判斷速度。創建索引可以大大提高系統的性能第一,通過創建唯一性索引,可以保證資料庫表中每一行數據的唯一性。第二,可以大大加快數據的檢索速度,這也是創建索引的最主要的原因。第三,可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。第四,在使用分組和排序子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間。第五,通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。也許會有人要問:增加索引有如此多的優點,為什麼不對表中的每一個列創建一個索引呢?因為,增加索引也有許多不利的方面。創建索引的弊端第一,創建索引和維護索引要耗費時間,這種時間隨著數據量的增加而增加。第二,索引需要佔物理空間,除了數據表占數據空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。第三,當對表中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。同樣,對於有些列不應該創建索引。一般來說,不應該創建索引的的這些列具有下列特點:第一,對於那些在查詢中很少使用或者參考的列不應該創建索引。這是因為,既然這些列很少使用到,因此有索引或者無索引,並不能提高查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。第二,對於那些只有很少數據值的列也不應該增加索引。這是因為,由於這些列的取值很少,例如人事表的性別列,在查詢的結果中,結果集的數據行佔了表中數據行的很大比例,即需要在表中搜索的數據行的比例很大。增加索引,並不能明顯加快檢索速度。第三,對於那些定義為text, image和bit數據類型的列不應該增加索引。這是因為,這些列的數據量要麼相當大,要麼取值很少。第四,當修改性能遠遠大於檢索性能時,不應該創建索引。這是因為,修改性能和檢索性能是互相矛盾的。當增加索引時,會提高檢索性能,但是會降低修改性能。當減少索引時,會提高修改性能,降低檢索性能。因此,當修改性能遠遠大於檢索性能時,不應該創建索引。三、索引的類型根據資料庫的功能,可以在資料庫設計器中創建三種索引:唯一索引、主鍵索引和聚集索引。唯一索引唯一索引是不允許其中任何兩行具有相同索引值的索引。當現有數據中存在重復的鍵值時,大多數資料庫不允許將新創建的唯一索引與表一起保存。資料庫還可能防止添加將在表中創建重復鍵值的新數據。例如,如果在employee表中職員的姓(lname)上創建了唯一索引,則任何兩個員工都不能同姓。主鍵索引資料庫表經常有一列或列組合,其值唯一標識表中的每一行。該列稱為表的主鍵。在資料庫關系圖中為表定義主鍵將自動創建主鍵索引,主鍵索引是唯一索引的特定類型。該索引要求主鍵中的每個值都唯一。當在查詢中使用主鍵索引時,它還允許對數據的快速訪問。聚集索引在聚集索引中,表中行的物理順序與鍵值的邏輯(索引)順序相同。一個表只能包含一個聚集索引。如果某索引不是聚集索引,則表中行的物理順序與鍵值的邏輯順序不匹配。與非聚集索引相比,聚集索引通常提供更快的數據訪問速度。四、局部性原理與磁碟預讀由於存儲介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,磁碟的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁碟I/O。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的數據放入內存。這樣做的理論依據是計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也通常會馬上被使用。程序運行期間所需要的數據通常比較集中。由於磁碟順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有局部性的程序來說,預讀可以提高I/O效率。預讀的長度一般為頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬體及操作系統往往將主存和磁碟存儲區分割為連續的大小相等的塊,每個存儲塊稱為一頁(在許多操作系統中,頁得大小通常為4k),主存和磁碟以頁為單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁碟發出讀盤信號,磁碟會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中,然後異常返回,程序繼續運行。五、B樹和B+樹數據結構1、B樹B樹中每個節點包含了鍵值和鍵值對於的數據對象存放地址指針,所以成功搜索一個對象可以不用到達樹的葉節點。成功搜索包括節點內搜索和沿某一路徑的搜索,成功搜索時間取決於關鍵碼所在的層次以及節點內關鍵碼的數量。在B樹中查找給定關鍵字的方法是:首先把根結點取來,在根結點所包含的關鍵字K1,…,kj查找給定的關鍵字(可用順序查找或二分查找法),若找到等於給定值的關鍵字,則查找成功;否則,一定可以確定要查的關鍵字在某個Ki或Ki+1之間,於是取Pi所指的下一層索引節點塊繼續查找,直到找到,或指針Pi為空時查找失敗。2、B+樹B+樹非葉節點中存放的關鍵碼並不指示數據對象的地址指針,非也節點只是索引部分。所有的葉節點在同一層上,包含了全部關鍵碼和相應數據對象的存放地址指針,且葉節點按關鍵碼從小到大順序鏈接。如果實際數據對象按加入的順序存儲而不是按關鍵碼次數存儲的話,葉節點的索引必須是稠密索引,若實際數據存儲按關鍵碼次序存放的話,葉節點索引時稀疏索引。B+樹有2個頭指針,一個是樹的根節點,一個是最小關鍵碼的葉節點。所以 B+樹有兩種搜索方法:一種是按葉節點自己拉起的鏈表順序搜索。一種是從根節點開始搜索,和B樹類似,不過如果非葉節點的關鍵碼等於給定值,搜索並不停止,而是繼續沿右指針,一直查到葉節點上的關鍵碼。所以無論搜索是否成功,都將走完樹的所有層。B+ 樹中,數據對象的插入和刪除僅在葉節點上進行。這兩種處理索引的數據結構的不同之處:1、B樹中同一鍵值不會出現多次,並且它有可能出現在葉結點,也有可能出現在非葉結點中。而B+樹的鍵一定會出現在葉結點中,並且有可能在非葉結點中也有可能重復出現,以維持B+樹的平衡。2、因為B樹鍵位置不定,且在整個樹結構中只出現一次,雖然可以節省存儲空間,但使得在插入、刪除操作復雜度明顯增加。B+樹相比來說是一種較好的折中。3、B樹的查詢效率與鍵在樹中的位置有關,最大時間復雜度與B+樹相同(在葉結點的時候),最小時間復雜度為1(在根結點的時候)。而B+樹的時候復雜度對某建成的樹是固定的。六、B/+Tree索引的性能分析到這里終於可以分析B-/+Tree索引的性能了。上文說過一般使用磁碟I/O次數評價索引結構的優劣。先從B-Tree分析,根據B-Tree的定義,可知檢索一次最多需要訪問h個節點。資料庫系統的設計者巧妙利用了磁碟預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。為了達到這個目的,在實際實現B-Tree還需要使用如下技巧:每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁里,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。B-Tree中一次檢索最多需要h-1次I/O(根節點常駐內存),漸進復雜度為O(h)=O(logdN)。一般實際應用中,出度d是非常大的數字,通常超過100,因此h非常小(通常不超過3)。而紅黑樹這種結構,h明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用局部性,所以紅黑樹的I/O漸進復雜度也為O(h),效率明顯比B-Tree差很多。綜上所述,用B-Tree作為索引結構效率是非常高的。