1. 程式人生 > >oracle學習筆記 buffer_cache作用概述

oracle學習筆記 buffer_cache作用概述



oracle學習筆記  buffer_cache作用概述

從這節課開始講buffercache
對oracle資料庫來講最重要的記憶體結構是buffercache
buffercache的合理使用它直接關係到資料庫執行的效能
對DBA來講資料庫的效能是非常重要的

和效能即對立又相附的是安全
oracle資料庫的資料安全性一致性有時會出現問題
我們掌握基本的一些概念和手法可以避免
當然有好多知識需要去學習
oracle資料庫的一致性一旦出問題就是災難性的
很多時候把資料搞得不一致了老師也沒有很好的辦法
只能一點點的驗證,很麻煩

不一致簡單講就是資料庫中資料的值和真實值不一樣了
可人為造成,也可是資料庫執行時由軟體或硬體造成
就是資料出錯了
對一個大型的庫來講想排除一個這樣的錯誤是非常麻煩的,
因為你可能根本不知道它已經出錯了
或者根本不知道它錯在哪裡
即使找到了地方也有可能根本不知道正確的值是什麼
對於一些不穩定的配置是極易出現這種問題的
如一些"先進"的軟硬體配置
因為先進裝置帶來的最大副作用就是不穩定也就是說經常出錯
儘管一些對軟硬體的修改未必是什麼新的技術,說成是先進很勉強
但作用是肯定有的,原軟體穩定性差了
想保證一個數據庫系統的安全首要的任務是使用安全的軟硬體設施
這樣資料的一致才有最好的保證

但是我們平時工作中最主要的時間和精力花費在oracle資料庫的優化上
優化是讓資料庫執行的更快更穩定
優化是從一個很高的高度來設定資料庫
但我們首要的任務應該是得到一個安全穩定的基礎

我們在優化過程中非常注意關注的一個地方就是buffercache

這節課主要講一下buffercache的工作機制和工作原理
當然我們後面會給大家講一些操作和sql語句
讓大家更深一步的去理解這些概念和原理

今天我們就從buffercache最基本的講起

一)buffer_cache作用概述


先講一個最基礎的東西
段、區、塊的概念

1)資料庫基本功能和結構

我們需要資料庫是因為它可以
1、儲存資料
但並不是我們最終使用的目的
對oracle資料庫來講它儲存資料的同時我們可以很方便的
2、檢索和處理資料
處理裡面包括
增加資料、刪除資料、修改資料

舉個很簡單的例子
假設有個公司有十萬個員工
我們把十萬個員工所有的資訊全部儲存在資料庫裡面去
將來要檢索要找某個員工資訊的時候非常容易
資料庫裡面一條sql語句一下就出來啦

但如果說你不用資料庫你用別的格式的文件去儲存這個資訊的話
你找的時候要翻半天
但資料庫不是
它以儲存資料為手段
最主要的是要檢索以及處理資料
這是我們處理資料庫的目的

資料庫裡面儲存的資料其實就是表
表是一個有行列的二維結構

比如說一個關於人員的一個表,員工employee表
表裡有員工編號、員工姓名、性別、出生日期,
還有些別的資訊如家庭住址、電話號碼等等
這就是一張表
oracle資料庫裡面就是儲存著幾十張幾百張表
一般老師做的資料庫裡面多是四五百張以上
可以這麼認為oracle資料庫裡面儲存著表
平時可以檢索表資料,同時也可以對錶資料進行處理
當然了我們為了檢索處理資料
除了表以外在資料庫裡面還建了一堆索引、儲存過程、函式、檢視、序列包括物化檢視等等
都是為了配合我們對資料庫的資料的檢索以及處理而產生的一些物件
但是對資料庫來講最基礎的我們最關心的最關注的就是表

資料庫裡面儲存的是表
表就是實實在在的這麼一張表

oracle資料庫的體系結構裡有
控制檔案、redolog檔案、dbf資料檔案
資料檔案裡面放的很簡單是表

dbf檔案有它的結構組織
只要這個dbf檔案屬於oracle資料庫就被分成一個一個大小相等的塊
大小可以是通常的8K
可以是4K、2K也可以是16K、32K
一般的都是8K

可以看一下我們使用的資料庫塊的大小

老師在課程中使用了
export NLS_LANG=american_america.zhs16gbk
是因為它的環境中引數NLS_LANG設定有問題
導致很多字元輸出的亂碼,
而我的環境變數已設定正確不會出現字元輸出亂碼
如果已經設定正確了不用去理會。
前面在軟體安裝準備工作中講過
oracle使用者此環境變數可在此使用者的 .bash_profile檔案中設定

關於資料庫塊的環境變數
SQL> show parameter block

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_block_buffers                     integer     0
db_block_checking                    string      FALSE
db_block_checksum                    string      TRUE
db_block_size                        integer     8192
db_file_multiblock_read_count        integer     16

有這麼一行
db_block_size                        integer     8192
說明當前資料塊的大小是8K

oracle資料庫裡面dbf檔案被分割成了無數個8K大小的塊
在dbf拿出一個來時就是一個block

oracle的塊block是oracle的io的最小單位

2)段、區、塊的概念


段可以這樣認為:一個表就是一個段

建一個表
SQL> create table t2(id int,name varchar2(20));

Table created.

對oracle資料庫來講建立了一個表t2
在oracle資料庫裡它自然給你建一個段,段名t2

我們講分割槽表會有很多段現在不要想這麼多
現在姑且認為一個表一個段

然後可以插入資料
SQL> insert into t2 values(1,'xkj');

1 row created.

SQL> insert into t2 values (2,'jiagulun');

1 row created.

SQL> commit;

Commit complete.

剛才給演示的過程在oracle資料庫裡面的體現

首先來講建了一個表t2
oracle資料庫建了一個段t2
在dbf檔案裡面
oracle為t2分配一個區

一個表就是一個段
你建了一個表同時建了一個段
段一旦建了以後
oracle做的第一件事情給一個段分配一個區
區的英文extent(區域)
區的概念是物理上連續的多個塊

這裡比如8個塊做為一個區給t2這個段
這個區是8個連續的oracle塊全部屬於t2這個段

建完以後我們開始插入資料
段的前幾個塊被段自己使用了,資料部分從後面開始
insert into開始插入一行行資料
插入資料時oracle會
找一個空塊然後把資料一行行的寫進去
寫滿了這個塊以後
接著寫第二個塊、第三個塊
第八個塊寫滿了的時候
我們給它分配的區已經用滿了
區既然都用完了oracle接著再分配一個區
還是八個塊
這時候這個段又有了新的一個區又有了8個塊可用
接著再插入資料
再用完了再給它分配一個區

建表自然就建了一個段
接著給這個段分配一個區
oracle就可以使用區裡面的一些空塊
用完後再分配新區

區是oracle給段分配空間的最小單位
也就是oracle給段分配空間的時候一次性分配一個區
不是分配一個塊是一次性分配一個區

3)
塊是oracle I/O的最小單位

dbf檔案上有好多塊
表t2在這些塊裡面
現在我要訪問t2
要訪問t2裡面的某個塊
塊裡面放的是資料行
原則上來講一個塊裡面放多個行
一般的情況不會出現一個行在多個塊裡面

但在發生了行連結、行遷移行為和儲存資料型別為long或lob的情況下
可能會出現一個行
在一個塊裡面有點在另外一個塊裡也有點

到目前為止就認為一個塊裡面有多個行

比如我要訪問某個行
select * from t2 where id=10;
oracle計算髮現要訪問的這個行在某個塊裡面
這個塊裡面有20行,但你只訪問其中一行
這時oracle不會只讀其中一行
oracle會向磁碟發出I/O請求去請求一個塊
這時作業系統會把整個塊讀到記憶體裡面去
然後cpu會在這個塊裡面找到我們需要的那一行
把這一行讀出來返給使用者
oracle IO的最小單位是塊是oracle塊
就是db_block_size值確定的大小的塊

有人說了oracle太笨了
你只是要讀其中一行資料
假設這一行所在塊8K
這一行只有400個300個位元組
為了讀300個位元組把整個塊全部讀到記憶體裡面去
大家認為oracle很笨

其實原因又回到我們以前的快取的概念
和我們講的物理硬碟的工作原理

對硬碟來講一次IO裡面最主要的是尋道時間
一次尋道甚至佔到一次IO的90%的時間
我們要讀這個塊至少要發生一次尋道
然後我們把整個塊讀出來

如發生了尋道花了10ms
讀一行需要花1ms,讀塊中所有20行需要花1.2毫秒
差不多這個時間比例
1.2和1相對10ms來講都很小
所以我把整個塊讀到記憶體裡面去合適
還有一個概念
oracle讀了這一行以後
接著再讀這一行上面的和下面的行機率是很大的
就是我把整個塊讀到記憶體去以後
有可能在接下來的10個8個讀裡面oracle都會讀這個塊裡面的內容
所以oracle的io的最小單位是塊而不是行


講到目前為止講了兩個概念
1、講了段區塊的概念
區是oralce給段分配空間的最小單位
區是物理上連續的幾個oracle塊
2、塊是oracle IO的最小單位
oracle要從dbf裡面取一行十行
這時oracle都是以塊為單位去發出io請求的
io的最小單位是塊


二)buffercache的兩個意義

1)block和buffer

oracle的記憶體結構中SGA記憶體中
有sharedpool、logbuffer
最大的是buffercache

硬碟中有
控制檔案 和 dbf 和 logbuffer

sharedpool裡面儲存的主要是sql語句和執行計劃以及我們的資料字典
logbuffer裡面存的是日誌

buffercache和dbf是對應的
dbf的資料會被調到buffercache裡面
也就是buffercache快取的是dbf的資料
dbf檔案都被分成了大小相等的多個block塊大小為8K
自然buffercache也要被劃成一個一個的塊我們給它起名叫buffer

所以在dbf裡面一個塊就一個block
記憶體裡面塊叫一個buffer
block和buffer是一一對應的
都是8K

2)buffercache第一個意義


buffercache最主要的意義有兩個

第一個意義快取dbf檔案

我們要訪問t2的某一行
比如t2的第11行
這時oracle經過計算以後發現t2的第11行在某個塊裡面
而oracle IO的最小單位是block
這時oracle 發出一個IO請求
傳達給作業系統,作業系統傳給檔案系統,檔案系統傳達到磁碟上
最終這個塊被調入到記憶體裡面去成了一個buffer

dbf裡的塊block和buffercache裡面的buffer對應了
然後oracle就從這個buffer塊裡面把11行取出來
這個時候我們說oracle發生了一次物理的io,從磁碟到記憶體

接著oracle要讀t2的第12行
oracle要讀一個表的時候首先到buffercache裡面去找
看看錶對應的塊有沒有在記憶體裡面

oracle要讀一個表就是訪問一個段
這時候oracle有能力計算出來
要訪問的這個表的這個行所對應的這個段
這個行在這個段的那個塊裡面

oracle要讀t2的11行的時候
oracle發現它需要讀這個段的比如說第8個塊
到記憶體裡找
發現這個段的第8個塊沒有在記憶體裡
這時候就認為在磁碟上
就到磁碟上去把這個塊讀到記憶體裡面去
讀進來以後然後再把11行讀出來
接著oracle讀12行
讀第12行的時候
oracle發現第12行是段的第8個塊
在記憶體裡面找正好在記憶體裡面找到了這個段的第8個塊
確實在記憶體裡
這時oracle就直接在記憶體裡面把這個塊給讀了
不需要再到磁碟上去讀

這時候我們發現oracle在讀這個塊的時候命中了一次

oracle接著讀13行的時候發現還是這個塊
在記憶體裡又找到了

發生三次讀其中一次物理讀兩次邏輯讀
結果賺了
這就是以前我們講過的命中率的概念

物理io就是塊block從磁碟到記憶體叫物理io
邏輯io就是所讀的資料塊直接在記憶體裡面
物理io叫磁碟讀
邏輯io叫記憶體讀

buffercache裡面也有命中率的概念
而且buffercache裡的命中率比sharedpool的命中率更重要
因為給buffercache來講只要是沒有命中一定發生物理讀
只要是物理讀一定有尋道,就一定有磁碟的旋轉
就一定產生相對比較差的效能
這就是講的buffercache的其中一個作用
快取dbf從而減少物理io


3)buffercache第二個意義

在buffercache裡面除了快取block以外
第二個意義構造cr塊

cr塊和oracle的隔離級別有關係

隔離級別(isolation level),是併發控制的整體解決方案,是指事務與事務之間的隔離程度,
來解決事務併發效率和異常控制。
鎖是資料庫併發控制的內部機制,是基礎。
對使用者來說,只有當事務隔離級別無法解決一些併發問題和需求時,才有必要在語句中手動設定鎖。

在多事務環境下,事務隔離對產生cr塊有影響

一個會話
這裡暫且認為是一個sqlplus登上來了

提交的概念:
比如做了insert語句
這時候insert修改並沒有真真實實的儲存起來
只有commit以後
才能實實在在的儲存起來

oracle有這麼一個原則
一個會話所修改的資料在沒有commit以前
別的使用者是看不到的

前面的課程中我們已經在t2裡面插入了兩行資料
並且已經提交了
現在另外再起一個會話執行
SQL> select * from t2;

        ID NAME
---------- --------------------
         1 xkj
         2 jiagulun
原會話中的執行結果看到了,因為它已經提交了

也就是說oracle資料庫裡面
我可以直接讀已經提交的資料

假設另外做一件事情
現有兩個會話
會話1中:
SQL> delete from t2 where id=1;

1 row deleted.
刪了一行,t2表中有兩行刪了一行
但是沒有提交

會話2再來讀這個塊的時候
會話1已經把這一行刪了
這個行在這個塊裡沒有了
會話2要讀這個塊
因為會話1還沒有提交
就是對塊的修改還沒有提交
會話2不能直接讀這個塊

這樣會話2會在記憶體裡面在buffercache裡面再單獨的申請一個新塊
將資料填進新塊
第二行因為沒有修改直接填回來
第一行因為已經修改了但是還沒有提交
這時這個會話2會把第一行找出來
把第一行修改前資料找回來填到新構造塊裡面去

第一行盡然在原塊裡面被刪了
會話2去哪裡找這一行呢

oracle資料庫裡面
有這麼一堆dbf我們叫回滾資料叫undo
叫undo空間
這些檔案只做一件事情
會話1刪了一行資料
這時候被刪的資料就會進到undo裡面去
也就是原來這個塊裡面被修改的修改前的資料被寫到undo裡面去

會話2讀這個塊的時候
發現這個塊裡有一個行被刪了但刪還沒有提交
它就會到undo裡面去找到這一行
把它寫到新構造塊裡面去
這時對會話2來講
就放棄對原塊的讀
直接讀新構造塊

再看例項
會話1已經把t2的一行刪了
在會話2中讀:
SQL> select * from t2;

        ID NAME
---------- --------------------
         1 xkj
         2 jiagulun
我們發現還是兩行

會話2讀原塊的時候t2只剩一行了,為什麼讀出來兩行呢
是因為會話2讀這個塊的時候發現這個塊裡面的第二行沒有被改變直接寫過來
第一行被改變了而且還沒有被提交
於是就去undo裡面把已經改變沒有提交的改變前的資料寫到這個塊裡面去
然後讀出來

這個新塊就叫cr塊

oracle資料庫在改變一個塊以前
它會把改變前的資料寫到undo裡面去
undo的作用在刪完一行資料可以後悔
認為刪錯了可以強制執行一個命令rollback

會話1中
先讀一下當前資料
SQL> select * from t2;

        ID NAME
---------- --------------------
         2 jiagulun


然後回滾


SQL> rollback;

Rollback complete.

rollback,就是把上次提交以後
到目前資料的改變恢復出來

當前在會話1中就是把
delete的操作取消了
原理是在undo裡面把修改前的資料給複製覆蓋回來

再去讀
會話1中:
SQL> select * from t2;

        ID NAME
---------- --------------------
         1 xkj
         2 jiagulun
會話2中:
SQL> select * from t2;

        ID NAME
---------- --------------------
         1 xkj
         2 jiagulun
結果是兩行。

目前為止undo有幾個作用
第一個
可以對沒有提交的操作回滾
第二個作用
構造cr塊
構造cr塊需要的空間在buffercache裡面

buffercache有兩個作用
一個快取block,減少物理io,這裡面有命中率的概念
一個構造cr塊

oracle可以辦到:
我做了一些操作我只要沒提交我就可以回滾,就是可以後悔,取消前面的修改
只要未提交,別的會話就看不見修改,看不見怎麼實現呢,就是通過cr塊

這節講了block、buffer包括undo等等這些概念


2016年9月10日
    文字:韻箏