(1)InnoDB存儲引擎支持事務,而MyISAM不支持事務;html
(2)InnoDB支持行級鎖,而MyISAM只支持表級鎖;前端
( InnoDB行鎖是經過給索引加鎖實現的,即只有經過索引條件檢索數據,InnoDB才使用行級鎖,不然將使用表級鎖!行級鎖在每次獲取鎖和釋放鎖的操做須要比表級鎖消耗更多的資源。mysql
MySQL表級鎖有兩種模式:表共享讀鎖和表獨佔寫鎖。就是說對MyIASM表進行讀操做時,它不會阻塞其餘用戶對同一表的讀請求,但會阻塞對同一表的寫操做;而對MyISAM表的寫操做,會阻塞其餘用戶對同一表的讀和寫操做。)程序員
(3)InnoDB支持外鍵,而MyISAM不支持外鍵;面試
(4)InnoDB不保存數據庫表中表的具體行數,而MyISAM會保存;算法
( 也就是說,執行 select count(*) from table 時,InnoDB要掃描一遍整個表來計算有多少行,而MyISAM只須要讀出保存好的行數便可(內部維護了一個計算器,能夠直接調取)。【注】:當count(*)
語句包含where
條件時,兩種表的操做是同樣的。也就是上述介紹到的InnoDB使用表鎖的一種狀況。)sql
對於select ,update ,insert ,delete 操做:mongodb
若是執行大量的SELECT,MyISAM是更好的選擇(由於MyISAM不支持事務,使得MySQL能夠提供高速存儲和檢索,以及全文搜索能力);數據庫
若是執行大量的INSERT或UPDATE,出於性能方面的考慮,應該使用InnoDB表(由於InnoDB支持事務,在一些列增刪改中只要哪一個出錯還能夠回滾還原,而MyISAM就不能夠了)。編程
插入緩衝、二次寫、自適應哈希索引、預讀
(1)插入緩衝:
通常狀況下,主鍵是行惟一的標識符。一般應用程序中行記錄的插入順序是按照主鍵遞增的順序進行插入的。所以,插入彙集索引通常是順序的,不須要磁盤的隨機讀取。由於,對於此類狀況下的插入,速度仍是很是快的。
若是索引是非彙集的且不惟一,在進行插入操做時,數據的存放對於非彙集索引葉子節點的插入不是順序的,這時須要離散地訪問非彙集索引頁,因爲隨機讀取的存在而致使了插入操做性能降低。(這是由於B+樹的特性決定了非彙集索引插入的離散性。)
插入緩衝對於非彙集索引的插入和更新操做,不是每一次直接插入索引頁中,而是先判斷插入的非彙集索引頁是否在緩存池中。若是在,則直接插入;若是不在,則先放入一個插入緩衝區中,好似欺騙數據庫這個非彙集的索引已經插入到葉子結點了,而後再以必定的頻率執行插入緩衝和非彙集索引頁子節點的合併操做,這時一般能將多個插入合併到一個操做中(由於在一個索引頁中),這就大大提升了對非彙集索引執行插入和修改操做的性能。
插入緩衝的使用要知足兩個條件:
存在的問題:
在寫密集的狀況下,插入緩衝會過多的佔用緩衝池內存,默認狀況下最大能夠佔用1/2的緩衝池內存。
(2)二次寫
當數據庫宕機時,可能發生數據庫正在寫一個頁面,而這個頁只寫了一部分的狀況,咱們稱之爲部分寫失效。當寫入失效發生時,先經過頁的副原本還原該頁,再重作日誌,這就是兩次寫。
doublewrite步驟:
若是操做系統在將頁寫入磁盤的過程當中崩潰了,在恢復過程當中,InnoDB存儲引擎能夠從共享表空間中的doublewrite中找到該頁的一個副本,將其拷貝到表空間文件,再應用重作日誌,就完成了恢復過程。由於有副本因此也不擔憂表空間中數據頁是否損壞。
(3)自適應哈希索引
InnoDB存儲引擎會監控對錶上索引的查找,若是觀察到創建哈希索引能夠帶來速度的提高,則創建哈希索引,因此稱爲自適應的。自適應哈希索引經過緩衝池的B+樹構造而來,所以創建的速度很快,並且不須要將整個表都建哈希索引,InnoDB存儲引擎會自動根據訪問的頻率和模式來爲某些頁創建哈希索引。
(4)預讀
MySQL的存儲引擎InnoDB使用重作日誌(redo log)保證一致性與持久性,回滾日誌(undo log)保證原子性,使用各類鎖來保證隔離性。
MySQL中有六種日誌文件,分別是:
重作日誌(redo log) 回滾日誌(undo log) 二進制日誌(binlog) 錯誤日誌(errorlog) 慢查詢日誌(slow query log) 通常查詢日誌(general log) 中繼日誌(relay log)
其中重作日誌和回滾日誌與事務操做息息相關,二進制日誌也與事務操做有必定的關係。事務是如何經過日誌來實現的?
Undo 記錄某 數據 被修改 前 的值,能夠用來在事務失敗時進行 rollback;
Redo 記錄某 數據塊 被修改 後 的值,能夠用來恢復未寫入 data file 的已成功事務更新的數據。
即,
- Redo Log 保證事務的持久性
- Undo Log 保證事務的原子性(在 InnoDB 引擎中,還用 Undo Log 來實現 MVCC)
好比某一時刻數據庫 DOWN 機了,有兩個事務,一個事務已經提交,另外一個事務正在處理。數據庫重啓的時候就要根據日誌進行前滾及回滾,把已提交事務的更改寫到數據文件,未提交事務的更改恢復到事務開始前的狀態。即 經過 redo log 將全部已經在存儲引擎內部提交的事務應用 redo log 恢復, 全部已經 prepared 可是沒有 commit 的事務將會應用 undo log 作回滾。
redo log在事務沒有提交前,會記錄每個修改操做變動後的數據。主要是防止在發生故障的時間點,尚有髒頁未寫入磁盤。在重啓mysql服務的時候,根據redo log進行重作,從而達到事務的持久性這一特性。(做用)
在事務提交前,只要將 Redo Log 持久化便可,不須要將數據持久化。當系統崩潰時,系統能夠根據redo Log的內容,將全部數據恢復到最新的狀態。(持久化:先將重作日誌寫入緩存,再刷新(fsync)到磁盤)
重作日誌是物理日誌,記錄的是對於每一個頁的修改。事務開始後Innodb存儲引擎先將重作日誌寫入緩存(innodb_log_buffer)中。而後會經過如下三種方式將innodb日誌緩衝區的日誌刷新到磁盤。
當事務提交時,必須先將該事務的全部日誌寫入到重作日誌文件進行持久化。
一、內容:
物理格式的日誌,記錄的是物理數據頁面的修改的信息,其redo log是順序寫入redo log file的物理文件中去的。
二、redo log是何時寫盤的?
是在事物開始以後逐步寫盤的。
事務開始以後就產生redo log,redo log的寫盤並非隨着事務的提交才寫入的,而是在事務的執行過程當中,便開始寫入redo log文件中。(先將重作日誌寫入緩存,將日誌緩衝區的日誌刷新到磁盤,寫入磁盤的方式有上面3種)
【注】即便某個事務尚未提交,Innodb存儲引擎仍然每秒會將重作日誌緩存刷新到重作日誌文件。這一點是必需要知道的,由於這能夠很好地解釋再大的事務的提交(commit)的時間也是很短暫的。
三、何時釋放:
當對應事務的髒頁寫入到磁盤以後,redo log的使命也就完成了,重作日誌佔用的空間就能夠重用(被覆蓋)。
保存了事務發生以前的數據的一個版本,能夠用於回滾,同時能夠提供多版本併發控制下的讀(MVCC),也即非鎖定讀。(做用)
事務發生異常須要回滾,這時就須要回滾日誌。回滾日誌不一樣於重作日誌,它是邏輯日誌,對數據庫的修改都邏輯的取消了。當事務回滾時,它實際上作的是與先前相反的工做。對於每一個INSERT,InnoDB存儲引擎都會完成一個DELETE;對於每一個UPDATE,InnoDB存儲引擎都會執行一個相反的UPDATE。
未提交的事務和回滾了的事務也會產生重作日誌。InnoDB存儲引擎會重作全部事務包括未提交的事務和回滾了的事務,而後經過回滾日誌回滾那些未提交的事務。使用這種策略須要回滾日誌在重作日誌以前寫入磁盤,使得持久化變得複雜起來。爲了下降複雜度,InnoDB存儲引擎將回滾日誌做數據,記錄回滾日誌的操做也會記錄到重作日誌中。這樣回滾日誌就能夠像數據同樣緩存起來,而不用在重寫日誌以前寫入磁盤了。
一、內容:
邏輯格式的日誌,在執行undo的時候,僅僅是將數據從邏輯上恢復至事務以前的狀態,而不是從物理頁面上操做實現的,這一點是不一樣於redo log的。
二、何時產生?
事務開始以前,將當前是的版本生成undo log,undo 也會產生 redo 來保證undo log的可靠性
三、何時釋放?
當事務提交以後,undo log並不能立馬被刪除,而是放入待清理的鏈表,由purge線程判斷是否由其餘事務在使用undo段中表的上一個事務以前的版本信息,決定是否能夠清理undo log的日誌空間。
一、做用:
用於複製,在主從複製中,從庫利用主庫上的binlog進行重播,實現主從同步。 用於數據庫的基於時間點的還原。
二、內容:
邏輯格式的日誌,能夠簡單認爲就是執行過的事務中的sql語句。
但又不徹底是sql語句這麼簡單,而是包括了執行的sql語句(增刪改)反向的信息,也就意味着delete對應着delete自己和其反向的insert;update對應着update執行先後的版本的信息;insert對應着delete和insert自己的信息。
在使用mysqlbinlog解析binlog以後一些都會真相大白。
所以能夠基於binlog作到相似於oracle的閃回功能,其實都是依賴於binlog中的日誌記錄。
三、何時產生:
事務提交的時候,一次性將事務中的sql語句(一個事物可能對應多個sql語句)按照必定的格式記錄到binlog中。這裏與redo log很明顯的差別就是redo log並不必定是在事務提交的時候刷新到磁盤,redo log是在事務開始以後就開始逐步寫入磁盤。
所以對於事務的提交,即使是較大的事務,提交(commit)都是很快的,可是在開啓了bin_log的狀況下,對於較大事務的提交,可能會變得比較慢一些。這是由於binlog是在事務提交的時候一次性寫入的形成的,這些能夠經過測試驗證。
四、何時釋放:
binlog的默認是保持時間由參數expire_logs_days配置,也就是說對於非活動的日誌文件,在生成時間超過expire_logs_days配置的天數以後,會被自動刪除。
在MySQL數據庫中還有一種二進制日誌,其用來基於時間點的還原及主從複製。從表面上來看其和重作日誌很是類似,都是記錄了對於數據庫操做的日誌。可是,從本質上來看有着很是大的不一樣。 首先重作日誌是在InnoDB存儲引擎層產生的,而二進制日誌是在MySQL數據庫的上層產生的。其次,兩種日誌記錄的內容形式不一樣。二進制日誌是一種邏輯日誌,其記錄的是對應的SQL語句。而重作日誌是物理日誌,記錄的是每一個頁的修改。此外,兩種日誌記錄寫入磁盤的時間點不一樣,二進制日誌只在事務提交完成後進行一次寫入,重作日誌在事務進行時不斷地寫入。
事務就是一個操做序列,這些操做要麼都執行,要麼都不執行,它是一個不可分割的工做單位。事務是數據庫維護數據一致性的單位,在每一個事務結束時,都能保持數據一致性。
原子性、一致性、隔離性、持久性 (ACID)
(隔離性由鎖來實現;原子性、一致性和持久性經過數據庫的redo和undo來完成。)
經過鎖能夠實現事務隔離性的要求,使得事務能夠併發地工做。由於事務隔離性的要求,鎖會帶來3種問題:丟失更新、髒讀、不可重複讀。
丟失更新:
指一個事務正在訪問修改數據,與此同時另外一個事務也在訪問修改此數據,兩個事務互相不知道對方的存在。假如在是事務A修改數據前事務B已經修改過1次數據,那麼事務A最終只能查詢到假數據,丟失了更新操做。
解決方案:
悲觀鎖的方式: 加鎖,建議最後一步更新數據的時候加上排它鎖,不要在一開始就加鎖。執行到了最後一步更新,首先作一下加鎖的查詢確認數據有沒有沒改變,若是沒有被改變,則進行數據的更新,不然失敗。 必定要是作加鎖的查詢確認,由於若是你不加鎖的話,有可能你在作確認的時候數據又發生了改變。
樂觀鎖的方式:使用版本控制實現。
髒讀:
一個事務讀取了另外一個事務未提交的數據,那這個讀取就是髒讀。
解決方法 : 把數據庫的事務隔離級別調整到read commited。
不可重複讀:
不可重複讀是指在一個事務內屢次讀同一數據,在這個事務尚未結束時,另一個事務也訪問並修改該同一數據,那麼在第一個事務的兩次讀數據之間,因爲第二個事務的修改,第一個事務兩次讀到的數據多是不同的。這樣就發生了在一個事務內兩次讀到的數據是不同的,所以稱爲不可重複讀。
如何避免:InnoDB存儲引擎中,經過使用Next-Key Lock算法來避免不可重複讀的問題。在Next-Key Lock算法下,對於索引的掃描,不只僅是鎖住掃描到的索引,並且還能鎖住這些索引覆蓋的範圍。所以對於這個範圍內的插入都是不容許的。InnoDB存儲引擎的默認事務隔離級別是READ REPEATABLE,採用Next-Key Lock算法,就避免了不可重複讀的現象。
解決辦法:把數據庫的事務隔離級別調整到 REPEATABLE READ , 讀取時候不容許其餘事務修改該數據,無論數據在事務過程當中讀取多少次,數據都是一致的。
幻讀:
是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的所有數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,之後就會發生操做第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺同樣。
如何避免:Repeatable read及以上級別經過間隙鎖來防止幻讀的出現,即鎖定特定數據的先後間隙讓數據沒法被插入。
InnoDB經過預寫日誌的方式來保證事務的完整性。這意味着磁盤上存儲的數據頁和內存緩衝池中的數據頁是不一樣步的,對於內存緩衝池中頁的修改,先是寫入重作日誌文件,而後再寫入磁盤,所以是一種異步的方式。
樂觀併發控制(樂觀鎖)和悲觀併發控制(悲觀鎖)是併發控制主要採用的技術手段。
悲觀鎖:假定會發生併發衝突,屏蔽掉一切可能違反數據完整性的操做,在讀取的時候就對數據進行加鎖, 在該用戶讀取數據的期間,其餘任何用戶都不能來修改該數據,可是其餘用戶是能夠讀取該數據的, 只有當本身讀取完畢才釋放鎖。
樂觀鎖:假定不會發生併發衝突,只在提交的時候檢查是否發生併發衝突。
事務和鎖的存在都是爲了更好地解決併發訪問形成的數據不一致性問題。樂觀鎖和悲觀鎖都是爲了解決併發控制問題,樂觀鎖能夠看作一種在最後提交時檢測衝突的手段,而悲觀鎖是一種避免衝突的手段。
(1)樂觀鎖:假設不會發生併發衝突,只在提交的時候檢查是否發生併發衝突。能夠使用版本號機制和CAS算法實現。
版本號機制:通常在數據表中加一個數據版本號version字段,表示數據被修改的次數,當數據被修改時version值加一。當線程A要更新數據值時,在讀取數據的同時也會讀取version值,在提交更新時,若當前讀取到的version值與第一次讀取到的數據庫version值相等時才更新,不然重試更新操做,直到更新成功。
例子:
假設數據庫中賬戶信息表中有一個 version 字段,當前值爲 1 ;而當前賬戶餘額字段( balance )爲 100 。
- 操做員A此時將其讀出( version=1 ),並從其賬戶餘額中扣除 50(100-50 =50);
- 在操做員A操做的過程當中,操做員B也讀入此用戶信息( version=1 ),並從其賬戶餘額中扣除20 (100-20=80 )。
- 操做員A完成了修改工做,將數據版本號加一( version=2 ),連同賬戶扣除後餘額( balance=50 ),提交至數據庫更新,此時因爲提交數據版本大於數據庫記錄當前版本,數據被更新,數據庫記錄 version 更新爲 2 ;
- 操做員B完成了操做,也將版本號加一( version=2 )試圖向數據庫提交數據( balance=80 ),但此時比對數據庫記錄版本時發現,操做員 B 提交的數據版本號爲 2 ,數據庫記錄當前版本也爲 2 ,不知足 「 當前最後更新的version與操做員第一次的版本號相等 「 的樂觀鎖策略,所以,操做員B的提交被駁回。
CAS機制:即compare and swap(比較與交換),無鎖編程,在不使用鎖的狀況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的狀況下實現變量的同步,所以也叫非阻塞同步。
CAS過程是這樣:它包含3個參數:內存值V(要更新變量的值),舊的預期值A,要修改的值B。當且僅當預期值A的值等於內存值V時,纔會將內存值V修改成B,不然不會執行任何操做(V值和A值不一樣,則說明已經有其餘線程作了更新)。通常狀況下是一個自旋操做,即不斷的重試。
例子:
- 假設 t1,t2 線程同時更新同一變量56的值。
- 由於t1和t2線程都同時去訪問同一變量56,因此他們會把主內存的值徹底拷貝一份到本身的工做內存空間,因此t1和t2線程的預期值都爲56。
- 假設t1在與t2線程競爭中線程t1能去更新變量的值,而其餘線程都失敗。(失敗的線程並不會被掛起,而是被告知此次競爭中失敗,並能夠再次發起嘗試)。t1線程去更新變量值改成57,而後寫到內存中。此時對於t2來講,內存值變爲了57,與預期值56不一致,就操做失敗了(想改的值再也不是原來的值)。
樂觀鎖的優點和劣勢 :
優點:若是數據庫記錄始終處於悲觀鎖加鎖狀態,能夠想見,若是面對幾百上千個併發,那麼要不斷的加鎖減鎖,並且用戶等待的時間會很是的長, 樂觀鎖機制避免了長事務中的數據庫加鎖解鎖開銷,大大提高了大併發量下的系統總體性能表現。因此若是系統的併發很是大的話,悲觀鎖定會帶來很是大的性能問題,因此建議就要選擇樂觀鎖定的方法, 而若是併發量不大,徹底能夠使用悲觀鎖定的方法。樂觀鎖也適合於讀比較多的場景。
劣勢: 樂觀鎖只能在提交數據時才發現業務事務將要失敗,若是系統的衝突很是的多,並且一旦衝突就要由於從新計算提交而形成較大的代價的話,樂觀鎖也會帶來很大的問題。並且樂觀鎖也沒法解決髒讀的問題 。(2)悲觀鎖:假定會發生併發衝突,在讀取的時候就對數據進行加鎖, 在該用戶讀取數據的期間,其餘任何用戶都不能來修改該數據,可是其餘用戶是能夠讀取該數據的, 只有當本身讀取完畢才釋放鎖。
在數據庫中能夠使用Repeatable Read的隔離級別(可重複讀)來實現悲觀鎖,它徹底知足悲觀鎖的要求(加鎖)。Java中
悲觀鎖的優點和劣勢 :synchronized
和ReentrantLock
等獨佔鎖就是悲觀鎖思想的實現。
優點: 能避免衝突的發生 。
劣勢 :開銷較大,並且加鎖時間較長,對於併發的訪問性支持很差。兩種鎖的使用場景:
若是衝突不多,或者衝突的後果不會很嚴重,那麼一般狀況下應該選擇樂觀鎖,由於它能獲得更好的併發性;
若是衝突太多或者衝突的結果對於用戶來講痛苦的,那麼就須要使用悲觀策略,它能避免衝突的發生。
通常樂觀鎖適用於寫比較少的狀況下(多讀場景),即衝突真的不多發生的時候;悲觀鎖適用於多寫的狀況,多寫的狀況通常會常常產生衝突。
共享鎖和排它鎖是具體的鎖,是數據庫機制上的鎖。
加鎖會有粒度問題,從粒度上從大到小能夠劃分爲 :
存儲過程:
(1)定義:
存儲過程是一組SQL命令集合,通過預編譯存放在系統中。也就是將經常使用的或很複雜的工做,預先用SQL語句寫好並用一個指定的名稱存儲起來,之後只要調用它就能夠完成相應的功能。
(2)存儲過程的種類:
存儲過程通常分爲「系統存儲過程」與「用戶存儲過程」。系統存儲過程通常以sp_開頭,用戶不能夠編輯修改,只能調用;用戶存儲過程是用戶編寫的處理數據的存儲過程。
(3)存儲過程的建立和使用:
create procedure proc1 --建立一個存儲過程
as begin
--在存儲過程當中處理SQL
select * from bank
end
【注】若是有參數,存儲過程的參數是在as關鍵字以前,as以後的是局部變量,2種變量在存儲過程當中均可以使用,可是命名時不能夠衝突。例:
create procedure proc2
@mobile varchar(50),@sendMsg varchar(50)
as
begin
print @mobile ---輸出mobile這個參數
end
(4)存儲過程與通常的SQL語句有什麼區別呢? (存儲過程的優勢: )
缺點:對於簡單的sql語句不必使用存儲過程,存儲過程適合用於對數據庫進行復雜的操做。
觸發器:
(1)定義:
觸發器(Trigger)是個特殊的存儲過程,它不是由用戶主動發起調用的,而是當發生某一事件而觸發,由系統自動調用。好比當用戶在數據庫中新增一條商品記錄,咱們但願同時在庫存中作登記,而庫存登記不是人工去錄入,是在發生新增商品記錄這一事件時發生,由系統自動完成錄入,這個工做就能夠交給一個特殊的存儲過程來完成,這個存儲過程就是觸發器。
(2)觸發器的工做機制:
觸發器是建在表上的,當這個表發生新增、修改、刪除操做時,若是這個表上有觸發器,就會被自動調用。在這個事件的過程當中,系統會產生一個臨時表,這個臨時表只有一行記錄:
當須要觸發器連帶操做登記庫存時就能夠從inserted表或者deleted表中得到變量,更新到庫存表中數據。
(3)做用:維護表的完整性,記錄表的修改來審計表的相關信息。分爲:
觸發器用處仍是不少的,好比校內網、開心網、Facebook,你發一個日誌,自動通知好友,其實就是在增長日誌時作一個後觸發,再向通知表中寫入條目。由於觸發器效率高。
(4)建立觸發器的SQL語法:
create trigger 觸發器名稱 --觸發器名稱
on 表名 --建在那個表上
for insert|update|delete --是插入事件處理仍是修改事件處理仍是刪除事件處理
as --如下是觸發器基本格式
begin
end
調用存儲過程:call procedure_name(參數,參數...)
(5)觸發器優勢:
缺點:不一樣數據庫,語法差異很大,移植困難,換了數據庫,須要從新編寫;很差管理,把過多業務邏輯寫在存儲過程很差維護,不利於分層管理,容易混亂,通常存儲過程適用於個別對性能要求較高的業務。
存儲過程與觸發器:
它們都是sql語句集,不一樣的是:
存儲過程是須要用戶調用的(經過存儲過程名字直接調用),而觸發器不是由用戶主動發起調用的,而是當發生某一事件而觸發,由系統自動調用。在insert、delete和update命令以前或以後自動調用sql命令或者存儲過程。
函數:
函數:MySQL中提供了許多內置函數,還能夠自定義函數(實現程序員須要sql邏輯處理)
自定義函數建立語法:
建立:CREATE FUNCTION 函數名稱(參數列表)
RETURNS 返回值類型 函數體
修改: ALTER FUNCTION 函數名稱 [characteristic ...]
刪除:DROP FUNCTION [IF EXISTS] 函數名稱
調用:SELECT 函數名稱(參數列表)
存儲過程和函數的區別:
索引是對數據庫表中一或多個列的值進行排序的結構,利用索引可快速訪問數據庫表的特定信息。
舉個例子:假設有一張數據表Emplyee,該表有三列:Employee_name,Employee_age,Employee_address,表中有幾萬條記錄。如今要執行下面這條查詢語句:Select * from Employee where Employee_name='Jesus'。
若是沒有數據庫索引功能,數據庫系統會全表掃描,逐行的遍歷整張表,對於每一行都要檢查其Employee_Name字段是否等於「Jesus」。而數據庫索引功能索引的最大做用就是加快查詢速度,它能從根本上減小須要掃表的記錄/行的數量。
優勢:
缺點:
普通索引、惟一索引、主鍵索引、聯合索引、全文索引。
http://www.javashuo.com/article/p-kijtdxkb-k.html
一般,經過索引查詢數據比全表掃描要快。可是咱們也必須注意到它的代價:
索引須要空間來存儲,也須要按期維護,每當有記錄在表中增減或索引列被修改時,索引自己也會被修改。這意味着每條記錄的INSERT、DELETE、UPDATE將爲此多付出4,5 次的磁盤I/O。由於索引須要額外的存儲空間和處理,那些沒必要要的索引反而會使查詢反應時間變慢。使用索引查詢不必定能提升查詢性能,索引範圍查詢(INDEX RANGE SCAN)適用於兩種狀況:
聯合索引是指對錶上的多個列作索引。在mysql創建聯合索引時會遵循最左前綴匹配的原則,即最左優先,在檢索數據時從聯合索引的最左邊開始匹配。
最左前綴匹配原則:
最左優先,在檢索數據時從聯合索引的最左邊開始匹配。
對列col一、列col2和列col3建一個聯合索引:
KEY test_col1_col2_col3 on test(col1,col2,col3);
聯合索引 test_col1_col2_col3 至關於創建了(col1)、(col1,col2)、(col,col2,col3)三個索引。
(1)
SELECT * FROM test WHERE col1="1" AND clo2="2" AND clo4=|"4"
上面這個查詢語句執行時會依照最左前綴匹配原則,檢索時會使用索引(col1,col2)進行數據匹配。
(2)索引的字段能夠是任意順序的,如:
SELECT * FROM test WHERE col1=「1」 AND clo2=「2」 SELECT * FROM test WHERE col2=「2」 AND clo1=「1」
這兩個查詢語句都會用到索引(col1,col2),mysql建立聯合索引的規則是首先會對聯合合索引的最左邊的,也就是第一個字段col1的數據進行排序,在第一個字段的排序基礎上,而後再對後面第二個字段col2進行排序。其實就至關於實現了相似 order by col1 col2這樣一種排序規則。
有人會疑惑第二個查詢語句不符合最左前綴匹配:首先能夠確定是兩個查詢語句都保函索引(col1,col2)中的col一、col2兩個字段,只是順序不同,查詢條件同樣,最後所查詢的結果確定是同樣的。既然結果是同樣的,到底以何種順序的查詢方式最好呢?此時咱們能夠藉助mysql查詢優化器explain,explain會糾正sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃。
(3)若是隻查詢col2:
SELECT * FROM test WHERE col2=2;
第一個col字段是絕對有序的,而第二字段就是無序的了。因此一般狀況下,直接使用第二個字段col2進行條件判斷是用不到索引的。固然是col2字段的索引數據也是有序的狀況下才能使用咯,何時纔是有序的呢?在col1字段是等值匹配的狀況下,cid纔是有序的。這也就是mysql索引規則中要求複合索引要想使用第二個索引,必須先使用第一個索引的緣由。(並且第一個索引必須是等值匹配)。
爲何要使用聯合索引?
- 減小開銷。建一個聯合索引(col1,col2,col3),實際至關於建了(col1),(col1,col2),(col1,col2,col3)三個索引。每多一個索引,都會增長寫操做的開銷和磁盤空間的開銷。對於大量數據的表,使用聯合索引會大大的減小開銷!
- 覆蓋索引。對聯合索引(col1,col2,col3),若是有以下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那麼MySQL能夠直接經過遍歷索引取得數據,而無需回表,這減小了不少的隨機io操做。減小io操做,特別的隨機io實際上是dba主要的優化策略。因此,在真正的實際應用中,覆蓋索引是主要的提高性能的優化手段之一。
- 效率高。索引列越多,經過索引篩選出的數據越少。有1000W條數據的表,有以下sql:select from table where col1=1 and col2=2 and col3=3,假設假設每一個條件能夠篩選出10%的數據,若是隻有單值索引,那麼經過該索引能篩選出1000W10%=100w條數據,而後再回表從100w條數據中找到符合col2=2 and col3= 3的數據,而後再排序,再分頁;若是是聯合索引,經過索引篩選出1000w10% 10% *10%=1w,效率提高可想而知!
從本質上來講,聯合索引仍是一顆B+樹,不一樣的是聯合索引的鍵值的數量不是1,而是大於等於2。
對於查詢 SELECT * FROM TABLE WHERE a=xxx and b=xxx,顯然能夠使用(a,b)這個聯合索引。對於單個的a列查詢 SELECT * FROM TABLE WHERE a=xxx 也是能夠使用(a,b)索引。但對於b列的查詢 SELECT * FROM TABLE WHERE b=xxx 不能夠使用這顆B+樹索引。由於葉節點上的b值爲1,2,1,4,1,2,顯然不是排序的,所以對於b列的查詢使用不到(a,b)的索引。
聯合索引的第二個好處是,能夠對第二個鍵值進行排序。例如,在不少狀況下咱們都須要查詢某個用戶的購物狀況,並按照時間排序,去除最近3次的購買記錄,這是使用聯合索引能夠避免多一次的排序操做,由於索引自己在葉節點已經排序了。
【注】:對於相同的第一個鍵值的數據,第二個鍵值是排好序的。
對於單個列a的查詢每每使用單個鍵的索引,由於其葉節點包含單個鍵值,能存放的記錄更多。
Hash索引和B+樹索引的特色:
Hash索引結構的特殊性,其檢索效率很是高,索引的檢索能夠一次定位;
B+樹索引須要從根節點到枝節點,最後才能訪問到頁節點這樣屢次的IO訪問。
Hash索引與B+樹索引區別?
B+樹是一種平衡查找樹。在B+樹中,全部記錄節點都是按鍵值的大小順序存放在同一層的葉節點中,各葉結點指針進行鏈接。
(平衡二叉樹AVL:首先符合二叉查找樹的定義(最結點的值比根節點小,右結點的值比根結點大),其次必須知足任何節點的左右兩個子樹的高度最大差爲1。)
(1)B+的磁盤讀寫代價更低
B+的內部結點並無指向關鍵字具體信息的指針。所以其內部結點相對B樹更小。若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。
(2)B+tree的查詢效率更加穩定
因爲非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。
數據庫中的B+索引能夠分爲彙集索引和輔助彙集索引。無論是彙集索引仍是非彙集的索引,其內部都是B+樹的,即高度平衡的,葉節點存放着全部的數據,彙集索引與非彙集索引不一樣的是,葉節點存放的是不是一整行的信息。
彙集索引就是按照每張表的主鍵構造一顆B+樹,而且葉節點中存放着整張表的行記錄數據,所以也讓彙集索引的葉節點成爲數據頁。彙集索引的這個特性決定了索引組織表中數據也是索引的一部分。因爲實際的數據頁只能按照一顆B+樹進行排序,所以每張表只能擁有一個彙集索引。
彙集索引表記錄的排列順序和索引的排列順序一致,因此查詢效率快,只要找到第一個索引值記錄,其他就連續性的記錄在物理也同樣連續存放。彙集索引對應的缺點就是修改慢,由於爲了保證表中記錄的物理和索引順序一致,在記錄插入的時候,會對數據頁從新排序。
對於輔助索引(非彙集索引),葉級別不包含行的所有數據。彙集索引鍵來告訴InnoDB存儲引擎,哪裏能夠找到與索引相對應的行數據。輔助索引的存在並不影響數據在彙集索引中的組織,所以每張表上能夠有多個輔助索引。經過輔助索引來尋找數據時,InnoDB存儲引擎會遍歷輔助索引並經過葉級別的指針得到指向主鍵索引的主鍵,而後再經過主鍵索引來找到一個完整的行記錄。
非彙集索引指定了表中記錄的邏輯順序,可是記錄的物理和索引不必定一致,兩種索引都採用B+樹結構,非彙集索引的葉子層並不和實際數據頁相重疊,而採用葉子層包含一個指向表中的記錄在數據頁中的指針方式。非彙集索引層次多,不會形成數據重排。
根本區別:
彙集索引和非彙集索引的根本區別是表記錄的排列順序和與索引的排列順序是否一致。
(1)truncate和delete只刪除數據,而drop則刪除整個表(結構和數據)。
(2)delete語句執行刪除的過程是每次從表中刪除一行,而且同時將該行的刪除操做做爲事務記錄在日誌中保存以便進行進行回滾操做。truncate table則一次性地從表中刪除全部的數據並不把單獨的刪除操做記錄記入日誌保存,刪除行是不能恢復的。而且在刪除的過程當中不會激活與表有關的刪除觸發器。執行速度快。
(3)執行速度:drop> truncate >delete
(4)delete語句是dml,這個操做會放到rollback segement中,事務提交以後才生效。若是有相應的trigger(觸發器),執行的時候將被觸發;
truncate、drop是ddl,操做當即生效,原數據不放到rollback segment中,不能回滾,操做不觸發trigger。
(5)當表被truncate後,這個表和索引所佔用的空間會恢復到初始大小, delete操做不會減小表或索引所佔用的空間。
(5)應用範圍:truncate只能對table;delete能夠是table和view
若是直接刪除一個表drop,對數據量很大的表,這個過程會佔用比較長的時間,若是先truncat後drop table:一、能夠下降操做失敗的風險;二、能夠下降數據字典鎖佔用的時間,下降系統開銷。
超鍵 :在關係中能惟一標識元組的屬性集稱爲關係模式的超鍵。一個屬性能夠爲做爲一個超鍵,多個屬性組合在一塊兒也能夠做爲一個超鍵。超鍵包含候選鍵和主鍵。
候選鍵:是最小超鍵,即沒有冗餘元素的超鍵。
主鍵 :數據庫表中對存儲數據對象予以惟一和完整標識的數據列或屬性的組合。一個數據列只能有一個主鍵,且主鍵的取值不能缺失,即不能爲空值(Null)。
外鍵 :在一個表中存在的另外一個表的主鍵稱此表的外鍵。
若是咱們定義了主鍵(PRIMARY KEY),那麼InnoDB會選擇主鍵做爲彙集索引、若是沒有顯式定義主鍵,則InnoDB會選擇第一個不包含有NULL值的惟一索引做爲主鍵索引、若是也沒有這樣的惟一索引,則InnoDB會選擇內置6字節長的ROWID做爲隱含的彙集索引(ROWID隨着行記錄的寫入而主鍵遞增,這個ROWID不像ORACLE的ROWID那樣可引用,是隱含的)。
char是一種固定長度的類型,varchar是一種可變長度的類型。
varchar(50)中50的含義:
最多存放50個字符,varchar(50)和(200)存儲hello所佔空間同樣,但後者在排序時會消耗更多內存,由於order by col採用fixed_length計算col長度。ppp-
int(20)中20的含義:
是指顯示字符的長度
但要加參數的,最大爲255,好比它是記錄行數的id,插入10筆資料,它就顯示00000000001 ~~~00000000010,當字符的位數超過11,它也只顯示11位,若是你沒有加那個讓它未滿11位就前面加0的參數,它不會在前面加0.
20表示最大顯示寬度爲20,但仍佔4字節存儲,存儲範圍不變;
mysql爲何這麼設計:
對大多數應用沒有意義,只是規定一些工具用來顯示字符的個數;int(1)和int(20)存儲和計算均同樣。
視圖(View)是一個命名的虛表,它由一個查詢來定義,能夠看成表使用。
視圖有什麼用(應用場景):
一、當一個查詢你須要頻頻的做爲子查詢使用時,視圖能夠簡化代碼,直接調用而不是每次都去重複寫這個東西。
二、系統的數據庫管理員,須要給他人提供一張表的某兩列數據,而不但願他能夠看到其餘任何數據,這時能夠建一個只有這兩列數據的視圖,而後把視圖公佈給他。
建立視圖sql語句:
CREATE VIEW view_name AS
SELECT column_name(s)
FROM table_name
WHERE condition
視圖與表的區別:
一、視圖是已經編譯好的sql語句,而表不是。
二、視圖沒有實際的物理記錄,而表有。
三、表是內容,視圖是窗口。
四、表只用物理空間而視圖不佔用物理空間,視圖只是邏輯概念的存在,表能夠及時對它進行修改,但視圖只能由建立的語句來修改。
五、視圖是查看數據表的一種方法,能夠查詢數據表中某些字段構成的數據,只是一些SQL語句的集合。從安全的角度說,視圖能夠不給用戶接觸數據表,從而不知道表結構。
六、表屬於全局模式中的表,是實表;視圖屬於局部模式的表,是虛表。
七、視圖的創建和刪除隻影響視圖自己,不影響對應的基本表。
八、不能對視圖進行update或者insert into操做。
第一範式(1NF)
(在任何一個關係數據庫中,第一範式(1NF)是對關係模式的基本要求,不知足第一範式(1NF)的數據庫就不是關係數據庫。)
所謂第一範式(1NF)是指數據庫表的每一列都是不可分割的基本數據項,第一範式就是無重複的列。強調的是列的原子性,即列不可以再分紅其餘幾列。
第二範式(2NF)
知足第二範式(2NF)必須先知足第一範式(1NF)。另外包含兩部份內容,一是表必須有主鍵;二是沒有包含在主鍵中的列必須徹底依賴於主鍵,而不能只依賴於主鍵的一部分。
第三範式(3NF)
知足第三範式(3NF)必須先知足第二範式(2NF)第三範式就是屬性不依賴於其它非主屬性。非主鍵列必須直接依賴於主鍵,不能存在傳遞依賴。
1. explain sql 分析sql語句,這個語句能夠打印出的各類item的意義:
2. 查詢語句不一樣元素(where、jion、limit、group by、having等等)執行前後順序?
查詢中用到的關鍵詞主要包含6個,而且他們的順序依次爲 select--from--where--group by--having--order by--limit。
其中select和from是必須的,其餘關鍵詞是可選的。
(使用having字句對分組後的結果進行篩選,因此having只能用在group by以後;
limit 起始記錄位置:取記錄的條數對記錄進行選取,主要用來實現分頁功能)
非關係型數據庫的優點:
關係型數據庫的優點:
其餘:
1.對於這兩類數據庫,對方的優點就是本身的弱勢,反之亦然。
2.NOSQL數據庫慢慢開始具有SQL數據庫的一些複雜查詢功能,好比MongoDB。
3.對於事務的支持也能夠用一些系統級的原子操做來實現例如樂觀鎖之類的方法來曲線救國,好比Redis set nx。
例如1:
SELECT a.,b. FROM luntan LEFT JOIN usertable as b ON a.username=b.username
例如2:
SELECT a.,b. FROM city as a FULL OUTER JOIN user as b ON a.username=b.username
例如:
SELECT type,pub_name FROM titles CROSS JOIN publishers ORDER BY type
1.以A,B兩張表爲例
A left join B
選出A的全部記錄,B表中沒有的以null 代替
right join 同理
2.inner join
A,B的全部記錄都選出,沒有的記錄以null代替
3.cross join (笛卡爾積)
A中的每一條記錄和B中的每一條記錄生成一條記錄
例如A中有4條,B中有4條,cross join 就有16條記錄
% 通配符
:表示任何字符出現任意次數 (能夠是0次)。 _ 通配符
:表示只能匹配單個字符,不能多也不能少,就是一個字符。
使用通配符進行模糊查詢須要用 like操做符,例:
SELECT * FROM products WHERE products.prod_name like '%es%';
SELECT * FROM products WHERE products.prod_name like '_es';
【注】若是在使用like操做符時,後面的沒有使用通用匹配符效果是和 = 是
一致的。
SELECT * FROM products WHERE products.prod_name like '1000':
只能匹配的結果爲1000
,而不能匹配像JetPack 1000
這樣的結果。
count(column) :對特定的列的值具備的行進行計數,不包含NULL值。
count(1)這個用法和count(*)的結果是同樣的,包含null。
性能問題:
count(1)跟count(主鍵)同樣,只掃描主鍵;count(*)跟count(非主鍵)同樣,掃描整個表。明顯前者更快一些。
若是是utf8字符集的話,須要升級至utf8_mb4方可支持。
監控的工具備不少,例如zabbix,lepus,我這裏用的是lepus。
開啓慢日誌查詢:set global slow_query_log='ON';
那麼開啓了慢查詢日誌後,什麼樣的SQL纔會記錄到慢查詢日誌裏面呢?
這個是由參數long_query_time控制,默認狀況下long_query_time的值爲10秒,能夠使用命令修改,也能夠在my.cnf參數裏面修改。運行時間大於long_query_time(非大於等於),纔會記錄到慢查詢日誌裏。
MongoDB是神馬?
MongoDB是非關係型數據庫,是一個基於分佈式文件存儲的數據庫。(文檔型數據庫:能夠存放xml、json、bson類型的數據。)同時MongoDB是由C++語言編寫。旨在爲WEB應用提供可擴展的高性能數據存儲解決方案。 是非關係數據庫當中功能最豐富,最像關係數據庫的。
MongoDB 將數據存儲爲一個文檔,數據結構由鍵值(key=>value)對組成。MongoDB 文檔相似於 JSON 對象。字段值能夠包含其餘文檔,數組及文檔數組。
它能夠存儲比較複雜的數據類型。Mongo最大的特色是它支持的查詢語言很是強大,其語法有點相似於面向對象的查詢語言,幾乎能夠實現相似關係數據庫單表查詢的絕大部分功能,並且還支持對數據創建索引。
mongodb與mysql不一樣,mysql的每一次更新操做都會直接寫入硬盤,可是mongo不會,做爲內存型數據庫,數據操做會先寫入內存,而後再會持久化到硬盤中去 ,但MongoDB採用的預分配空間的方式來防止文件碎片,因此MongoDB的數據文件很大。
MongoDB的特色是:
(1)面向文檔(2)高性能(3)高可用(4)易擴展(5)豐富的查詢語言
MongoDB 缺點:
① MongoDB 不支持事務操做(最主要的缺點)
② MongoDB 佔用空間過大
③ MongoDB 沒有如 MySQL 那樣成熟的維護工具,這對於開發和IT運營都是個值得注意的地方
存儲方式:虛擬內存+持久化。
持久化方式:MongoDB 的全部數據其實是存放在硬盤的,全部要操做的數據經過 mmap 的方式映射到內存某個區域內。而後,MongoDB 就在這塊區域裏面進行數據修改,避免了零碎的硬盤操做。
mongodb,mysql的區別?
(1)MongoDB 非關係型數據庫,MySql是關係型數據庫
(2)MongoDB存儲方式:虛擬內存+持久化; MySql在不一樣的引擎上有不一樣 的存儲方式。
(3)MongoDB查詢語句:是獨特的Mongodb的查詢方式; MySqlMySql查詢語句是使用傳統的sql語句,擁有較爲成熟的體系,成熟度很高。
(4)mysql的每一次更新操做都會直接寫入硬盤,可是mongo的數據操做會先寫入內存,而後再會持久化到硬盤中去 。(MongoDB數據是存儲在硬盤上的,只不過須要常常讀取的數據會被加載到內存中,將數據存儲在物理內存中,從而達到高速讀寫。)
(5)mysql缺點就是在海量數據處理的時候效率會顯著變慢。在適量級的內存的Mongodb的性能是很是迅速的。
(6)MongoDB 不支持事務操做,mysql的innodb和bdb存儲引擎支持事務。(注:myisam不支持事務)
先簡單介紹一下一些組件的基本做用:
一條查詢語句的執行順序:
1.客戶端經過TCP鏈接發送鏈接請求到mysql鏈接器,鏈接器會對該請求進行權限驗證及鏈接資源分配。
2.創建鏈接後客戶端發送一條語句,mysql收到該語句後,經過命令分發器判斷其是不是一條select語句,若是是,在開啓查詢緩存的狀況下,先在查詢緩存中查找該SQL是否徹底匹配,若是徹底匹配,驗證當前用戶是否具有查詢權限,若是權限驗證經過,直接返回結果集給客戶端,該查詢也就完成了。若是不匹配繼續向下執行。
3.若是在查詢緩存中未匹配成功,則將語句交給分析器做語法分析,MySQL須要知道到底要查哪些東西,若是語法不對,就會返回語法錯誤中斷查詢。
4.分析器的工做完成後,將語句傳遞給預處理器,檢查數據表和數據列是否存在,解析別名看是否存在歧義等
5.語句解析完成後,MySQL就知道要查什麼了,以後會將語句傳遞給優化器進行優化(經過索引選擇最快的查找方式),並生成執行計劃。
6.以後交給執行器去具體執行該語句,在執行以前,會先檢查該用戶是否具備查詢權限,若是有,繼續執行該語句。執行器開始執行後,會逐漸將數據保存到結果集中,同時會逐步將數據緩存到查詢緩存中,最終將結果集返回給客戶端。(若是該SQL執行過程當中超過了慢查詢閥值,該SQL會被記錄到慢查詢日誌中)
一條更新語句的執行順序:
。。。。。
有一條記錄須要更新的時候,InnoDB引擎就會先把記錄寫到 redo log(粉板)裏,並更新到內存,這個時候更新就算完成了,同時,InnoDB引擎會在適合的時候,將這個操做記錄更新到磁盤裏面,而這個更新每每實在系統比較空閒的時候作。