最新、最全的 MySQL 面試題整理,吊打面試官徹底沒問題了!(建議收藏)

數據庫基礎知識

爲何要使用數據庫
  • 數據保存在內存mysql

    • 優勢: 存取速度快
    • 缺點: 數據不能永久保存
  • 數據保存在文件程序員

    • 優勢: 數據永久保存
    • 缺點:1)速度比內存操做慢,頻繁的IO操做。2)查詢數據不方便
  • 數據保存在數據庫面試

    • 數據永久保存
    • 使用SQL語句,查詢方便效率高。
    • 管理數據方便
什麼是SQL?

結構化查詢語言(Structured Query Language)簡稱SQL,是一種數據庫查詢語言(12800字!SQL 語法速成手冊)。redis

做用:用於存取數據、查詢、更新和管理關係數據庫系統。算法

什麼是MySQL?

MySQL是一個關係型數據庫管理系統,由瑞典MySQL) AB 公司開發,屬於 Oracle 旗下產品。MySQL 是最流行的關係型數據庫管理系統之一,在 WEB 應用方面,MySQL是最好的 RDBMS (Relational Database Management System,關係數據庫管理系統) 應用軟件之一。在Java企業級開發中很是經常使用,由於 MySQL 是開源免費的,而且方便擴展。sql

數據庫三大範式是什麼
  • 第一範式:每一個列都不能夠再拆分。
  • 第二範式:在第一範式的基礎上,非主鍵列徹底依賴於主鍵,而不能是依賴於主鍵的一部分。
  • 第三範式:在第二範式的基礎上,非主鍵列只依賴於主鍵,不依賴於其餘非主鍵。

在設計數據庫結構的時候,要儘可能遵照三範式,若是不遵照,必須有足夠的理由。好比性能。事實上咱們常常會爲了性能而妥協數據庫的設計。數據庫

mysql有關權限的表都有哪幾個

MySQL服務器經過權限表來控制用戶對數據庫的訪問,權限表存放在mysql數據庫裏,由mysql_install_db腳本初始化。這些權限表分別user,db,table_priv,columns_priv和host。下面分別介紹一下這些表的結構和內容:編程

user權限表:記錄容許鏈接到服務器的用戶賬號信息,裏面的權限是全局級的。
db權限表:記錄各個賬號在各個數據庫上的操做權限。
table_priv權限表:記錄數據表級的操做權限。
columns_priv權限表:記錄數據列級的操做權限。
host權限表:配合db權限表對給定主機上數據庫級操做權限做更細緻的控制。這個權限表不受GRANT和REVOKE語句的影響。後端

MySQL的binlog有有幾種錄入格式?分別有什麼區別?

有三種格式,statement,row和mixedROW 仍是 STATEMENT?線上 MySQL Binlog 怎麼選?數組

  • statement模式下,每一條會修改數據的sql都會記錄在binlog中。不須要記錄每一行的變化,減小了binlog日誌量,節約了IO,提升性能。因爲sql的執行是有上下文的,所以在保存的時候須要保存相關的信息,同時還有一些使用了函數之類的語句沒法被記錄複製。
  • row級別下,不記錄sql語句上下文相關信息,僅保存哪條記錄被修改。記錄單元爲每一行的改動,基本是能夠所有記下來可是因爲不少操做,會致使大量行的改動(好比alter table),所以這種模式的文件保存的信息太多,日誌量太大。
  • mixed,一種折中的方案,普通操做使用statement記錄,當沒法使用statement的時候使用row。

此外,新版的MySQL中對row級別也作了一些優化,當表結構發生變化的時候,會記錄語句而不是逐行記錄。贊!7000 字學習筆記,MySQL 從入門到放棄

數據類型

mysql有哪些數據類型


  • 整數類型

    • 包括TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分別表示1字節、2字節、3字節、4字節、8字節整數。任何整數類型均可以加上UNSIGNED屬性,表示數據是無符號的,即非負整數。
    • 長度:整數類型能夠被指定長度,例如:INT(11)表示長度爲11的INT類型。長度在大多數場景是沒有意義的,它不會限制值的合法範圍,只會影響顯示字符的個數,並且須要和UNSIGNED ZEROFILL屬性配合使用纔有意義。
    • 例子,假定類型設定爲INT(5),屬性爲UNSIGNED ZEROFILL,若是用戶插入的數據爲12的話,那麼數據庫實際存儲數據爲00012。
  • 實數類型

    • 包括FLOAT、DOUBLE、DECIMAL。
    • DECIMAL能夠用於存儲比BIGINT還大的整型,能存儲精確的小數。
    • 而FLOAT和DOUBLE是有取值範圍的,並支持使用標準的浮點進行近似計算。
    • 計算時FLOAT和DOUBLE相比DECIMAL效率更高一些,DECIMAL你能夠理解成是用字符串進行處理。
  • 字符串類型

    • 包括VARCHAR、CHAR、TEXT、BLOB
    • VARCHAR用於存儲可變長字符串,它比定長類型更節省空間。
    • VARCHAR使用額外1或2個字節存儲字符串長度。列長度小於255字節時,使用1字節表示,不然使用2字節表示。
    • VARCHAR存儲的內容超出設置的長度時,內容會被截斷。
    • CHAR是定長的,根據定義的字符串長度分配足夠的空間。
    • CHAR會根據須要使用空格進行填充方便比較。
    • CHAR適合存儲很短的字符串,或者全部值都接近同一個長度。
    • CHAR存儲的內容超出設置的長度時,內容一樣會被截斷。
使用策略:
  • 對於常常變動的數據來講,CHAR比VARCHAR更好,由於CHAR不容易產生碎片。
  • 對於很是短的列,CHAR比VARCHAR在存儲空間上更有效率。
  • 使用時要注意只分配須要的空間,更長的列排序時會消耗更多內存。
  • 儘可能避免使用TEXT/BLOB類型,查詢時會使用臨時表,致使嚴重的性能開銷。
  • 枚舉類型(ENUM),把不重複的數據存儲爲一個預約義的集合。

    • 有時能夠使用ENUM代替經常使用的字符串類型。
    • ENUM存儲很是緊湊,會把列表值壓縮到一個或兩個字節。
    • ENUM在內部存儲時,其實存的是整數。
    • 儘可能避免使用數字做爲ENUM枚舉的常量,由於容易混亂。
    • 排序是按照內部存儲的整數
  • 日期和時間類型,儘可能使用timestamp,空間效率高於datetime,

    • 用整數保存時間戳一般不方便處理。
    • 若是須要存儲微妙,能夠使用bigint存儲。

引擎

MySQL存儲引擎MyISAM與InnoDB區別

存儲引擎Storage engine:MySQL中的數據、索引以及其餘對象是如何存儲的,是一套文件系統的實現。

經常使用的存儲引擎有如下:
  • Innodb引擎:Innodb引擎提供了對數據庫ACID事務的支持。而且還提供了行級鎖和外鍵的約束。它的設計的目標就是處理大數據容量的數據庫系統。
  • MyIASM引擎(本來Mysql的默認引擎):不提供事務的支持,也不支持行級鎖和外鍵。
  • MEMORY引擎:全部的數據都在內存中,數據的處理速度快,可是安全性不高。
MyISAM與InnoDB區別


MyISAM索引與InnoDB索引的區別?
  • InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
  • InnoDB的主鍵索引的葉子節點存儲着行數據,所以主鍵索引很是高效。
  • MyISAM索引的葉子節點存儲的是行數據地址,須要再尋址一次才能獲得數據。
  • InnoDB非主鍵索引的葉子節點存儲的是主鍵和其餘帶索引的列數據,所以查詢時作到覆蓋索引會很是高效。
InnoDB引擎的4大特性
  • 插入緩衝(insert buffer)
  • 二次寫(double write)
  • 自適應哈希索引(ahi)
  • 預讀(read ahead)
存儲引擎選擇

若是沒有特別的需求,使用默認的Innodb便可。

MyISAM:以讀寫插入爲主的應用程序,好比博客系統、新聞門戶網站。

Innodb:更新(刪除)操做頻率也高,或者要保證數據的完整性;併發量高,支持事務和外鍵。好比OA自動化辦公系統。

索引

什麼是索引?

索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏全部記錄的引用指針。

索引是一種數據結構。數據庫索引,是數據庫管理系統中一個排序的數據結構,以協助快速查詢、更新數據庫表中數據。索引的實現一般使用B樹及其變種B+樹。

更通俗的說,索引就至關於目錄。爲了方便查找書中的內容,經過對內容創建索引造成目錄。索引是一個文件,它是要佔據物理空間的。

索引有哪些優缺點?
  • 索引的優勢

    • 能夠大大加快數據的檢索速度,這也是建立索引的最主要的緣由。
    • 經過使用索引,能夠在查詢的過程當中,使用優化隱藏器,提升系統的性能。
  • 索引的缺點

    • 時間方面:建立索引和維護索引要耗費時間,具體地,當對錶中的數據進行增長、刪除和修改的時候,索引也要動態的維護,會下降增/改/刪的執行效率;
    • 空間方面:索引須要佔物理空間。
索引使用場景(重點)
  • where

上圖中,根據id查詢記錄,由於id字段僅創建了主鍵索引,所以此SQL執行可選的索引只有主鍵索引,若是有多個,最終會選一個較優的做爲檢索的依據。

-- 增長一個沒有創建索引的字段
alter table innodb1 add sex char(1);
-- 按sex檢索時可選的索引爲null
EXPLAIN SELECT * from innodb1 where sex='男';

能夠嘗試在一個字段未創建索引時,根據該字段查詢的效率,而後對該字段創建索引(alter table 表名 add index(字段名)),一樣的SQL執行的效率,你會發現查詢效率會有明顯的提高(數據量越大越明顯)。

  • order by

當咱們使用order by將查詢結果按照某個字段排序時,若是該字段沒有創建索引,那麼執行計劃會將查詢出的全部數據使用外部排序(將數據從硬盤分批讀取到內存使用內部排序,最後合併排序結果),這個操做是很影響性能的,由於須要將查詢涉及到的全部數據從磁盤中讀到內存(若是單條數據過大或者數據量過多都會下降效率),更不管讀到內存以後的排序了。

可是若是咱們對該字段創建索引alter table 表名 add index(字段名),那麼因爲索引自己是有序的,所以直接按照索引的順序和映射關係逐條取出數據便可。並且若是分頁的,那麼只用取出索引表某個範圍內的索引對應的數據,而不用像上述那取出全部數據進行排序再返回某個範圍內的數據。(從磁盤取數據是最影響性能的)

  • join

對join語句匹配關係(on)涉及的字段創建索引可以提升效率

  • 索引覆蓋

若是要查詢的字段都創建過索引,那麼引擎會直接在索引表中查詢而不會訪問原始數據(不然只要有一個字段沒有創建索引就會作全表掃描),這叫索引覆蓋。所以咱們須要儘量的在select後只寫必要的查詢字段,以增長索引覆蓋的概率。

這裏值得注意的是不要想着爲每一個字段創建索引,由於優先使用索引的優點就在於其體積小。

索引有哪幾種類型?
  • 主鍵索引: 數據列不容許重複,不容許爲NULL,一個表只能有一個主鍵。
  • 惟一索引: 數據列不容許重複,容許爲NULL值,一個表容許多個列建立惟一索引。

    能夠經過 ALTER TABLE table_name ADD UNIQUE (column); 建立惟一索引
    
    能夠經過 ALTER TABLE table_name ADD UNIQUE (column1,column2); 建立惟一組合索引
  • 普通索引: 基本的索引類型,沒有惟一性的限制,容許爲NULL值。

    能夠經過ALTER TABLE table_name ADD INDEX index_name (column);建立普通索引
    
    能夠經過ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);建立組合索引
  • 全文索引: 是目前搜索引擎使用的一種關鍵技術。

    能夠經過ALTER TABLE table_name ADD FULLTEXT (column);建立全文索引
    索引的數據結構(b樹,hash)

    索引的數據結構和具體存儲引擎的實現有關,在MySQL中使用較多的索引有Hash索引,B+樹索引等,而咱們常用的InnoDB存儲引擎的默認索引實現爲:B+樹索引。對於哈希索引來講,底層的數據結構就是哈希表,所以在絕大多數需求爲單條記錄查詢的時候,能夠選擇哈希索引,查詢性能最快;其他大部分場景,建議選擇BTree索引。

  • B樹索引

mysql經過存儲引擎取數據,基本上90%的人用的就是InnoDB了,按照實現方式分,InnoDB的索引類型目前只有兩種:BTREE(B樹)索引和HASH索引。B樹索引是Mysql數據庫中使用最頻繁的索引類型,基本全部存儲引擎都支持BTree索引。一般咱們說的索引不出意外指的就是(B樹)索引(實際是用B+樹實現的,由於在查看錶索引時,mysql一概打印BTREE,因此簡稱爲B樹索引)

查詢方式

  • 主鍵索引區:PI(關聯保存的時數據的地址)按主鍵查詢,
  • 普通索引區:si(關聯的id的地址,而後再到達上面的地址)。因此按主鍵查詢,速度最快

B+tree性質

  • 1.)n棵子tree的節點包含n個關鍵字,不用來保存數據而是保存數據的索引。
  • 2.)全部的葉子結點中包含了所有關鍵字的信息,及指向含這些關鍵字記錄的指針,且葉子結點自己依關鍵字的大小自小而大順序連接。
  • 3.)全部的非終端結點能夠當作是索引部分,結點中僅含其子樹中的最大(或最小)關鍵字。
  • 4.)B+ 樹中,數據對象的插入和刪除僅在葉節點上進行。
  • 5.)B+樹有2個頭指針,一個是樹的根節點,一個是最小關鍵碼的葉節點。
  • 哈希索引

簡要說下,相似於數據結構中簡單實現的HASH表(散列表)同樣,當咱們在mysql中用哈希索引時,主要就是經過Hash算法(常見的Hash算法有直接定址法、平方取中法、摺疊法、除數取餘法、隨機數法),將數據庫字段數據轉換成定長的Hash值,與這條數據的行指針一併存入Hash表的對應位置;若是發生Hash碰撞(兩個不一樣關鍵字的Hash值相同),則在對應Hash鍵下以鏈表形式存儲。固然這只是簡略模擬圖。

索引的基本原理

索引用來快速地尋找那些具備特定值的記錄。若是沒有索引,通常來講執行查詢時遍歷整張表。

索引的原理很簡單,就是把無序的數據變成有序的查詢

  • 把建立了索引的列的內容進行排序
  • 對排序結果生成倒排表
  • 在倒排表內容上拼上數據地址鏈
  • 在查詢的時候,先拿到倒排表內容,再取出數據地址鏈,從而拿到具體數據
索引算法有哪些?

索引算法有BTree算法和Hash算法

  • BTree算法

BTree是最經常使用的mysql數據庫索引算法,也是mysql默認的算法。由於它不只能夠被用在=,>,>=,<,<=和between這些比較操做符上,並且還能夠用於like操做符,只要它的查詢條件是一個不以通配符開頭的常量, 例如:

-- 只要它的查詢條件是一個不以通配符開頭的常量
select * from user where name like 'jack%'; 
-- 若是一通配符開頭,或者沒有使用常量,則不會使用索引,例如: 
select * from user where name like '%jack';
  • Hash算法

Hash Hash索引只能用於對等比較,例如=,<=>(至關於=)操做符。因爲是一次定位數據,不像BTree索引須要從根節點到枝節點,最後才能訪問到頁節點這樣屢次IO訪問,因此檢索效率遠高於BTree索引。

索引設計的原則?
  • 適合索引的列是出如今where子句中的列,或者鏈接子句中指定的列
  • 基數較小的類,索引效果較差,沒有必要在此列創建索引
  • 使用短索引,若是對長字符串列進行索引,應該指定一個前綴長度,這樣可以節省大量索引空間
  • 不要過分索引。索引須要額外的磁盤空間,並下降寫操做的性能。在修改表內容的時候,索引會進行更新甚至重構,索引列越多,這個時間就會越長。因此只保持須要的索引有利於查詢便可。
建立索引的原則(重中之重)

索引雖好,但也不是無限制的使用,最好符合一下幾個原則

  • 1) 最左前綴匹配原則,組合索引很是重要的原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配,好比a = 1 and b = 2 and c > 3 and d = 4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,若是創建(a,b,d,c)的索引則均可以用到,a,b,d的順序能夠任意調整。
  • 2)較頻繁做爲查詢條件的字段纔去建立索引
  • 3)更新頻繁字段不適合建立索引
  • 4)如果不能有效區分數據的列不適合作索引列(如性別,男女未知,最多也就三種,區分度實在過低)
  • 5)儘可能的擴展索引,不要新建索引。好比表中已經有a的索引,如今要加(a,b)的索引,那麼只須要修改原來的索引便可。
  • 6)定義有外鍵的數據列必定要創建索引。
  • 7)對於那些查詢中不多涉及的列,重複值比較多的列不要創建索引。
  • 8)對於定義爲text、image和bit的數據類型的列不要創建索引。
建立索引的三種方式,刪除索引
  • 第一種方式:在執行CREATE TABLE時建立索引

    CREATE TABLE user_index2 (
      id INT auto_increment PRIMARY KEY,
      first_name VARCHAR (16),
      last_name VARCHAR (16),
      id_card VARCHAR (18),
      information text,
      KEY name (first_name, last_name),
      FULLTEXT KEY (information),
      UNIQUE KEY (id_card)
    );
  • 第二種方式:使用ALTER TABLE命令去增長索引

    ALTER TABLE table_name ADD INDEX index_name (column_list);
  • ALTER TABLE用來建立普通索引、UNIQUE索引或PRIMARY KEY索引。
  • 其中table_name是要增長索引的表名,column_list指出對哪些列進行索引,多列時各列之間用逗號分隔。
  • 索引名index_name可本身命名,缺省時,MySQL將根據第一個索引列賦一個名稱。另外,ALTER TABLE容許在單個語句中更改多個表,所以能夠在同時建立多個索引。
  • 第三種方式:使用CREATE INDEX命令建立

    CREATE INDEX index_name ON table_name (column_list);

    CREATE INDEX可對錶增長普通索引或UNIQUE索引。(可是,不能建立PRIMARY KEY索引)

刪除索引

根據索引名刪除普通索引、惟一索引、全文索引:alter table 表名 drop KEY 索引名

alter table user_index drop KEY name;
alter table user_index drop KEY id_card;
alter table user_index drop KEY information;

刪除主鍵索引:alter table 表名 drop primary key(由於主鍵只有一個)。這裏值得注意的是,若是主鍵自增加,那麼不能直接執行此操做(自增加依賴於主鍵索引):

須要取消自增加再行刪除:

alter table user_index
-- 從新定義字段
MODIFY id int,
drop PRIMARY KEY

但一般不會刪除主鍵,由於設計主鍵必定與業務邏輯無關。

建立索引時須要注意什麼?
  • 非空字段:應該指定列爲NOT NULL,除非你想存儲NULL。在mysql中,含有空值的列很難進行查詢優化,由於它們使得索引、索引的統計信息以及比較運算更加複雜。你應該用0、一個特殊的值或者一個空串代替空值;
  • 取值離散大的字段:(變量各個取值之間的差別程度)的列放到聯合索引的前面,能夠經過count()函數查看字段的差別值,返回值越大說明字段的惟一值越多字段的離散程度高;
  • 索引字段越小越好:數據庫的數據存儲以頁爲單位一頁存儲的數據越多一次IO操做獲取的數據越大效率越高。
使用索引查詢必定能提升查詢的性能嗎?爲何

一般,經過索引查詢數據比全表掃描要快。可是咱們也必須注意到它的代價。

  • 索引須要空間來存儲,也須要按期維護, 每當有記錄在表中增減或索引列被修改時,索引自己也會被修改。 這意味着每條記錄的INSERT,DELETE,UPDATE將爲此多付出4,5 次的磁盤I/O。 由於索引須要額外的存儲空間和處理,那些沒必要要的索引反而會使查詢反應時間變慢。使用索引查詢不必定能提升查詢性能,索引範圍查詢(INDEX RANGE SCAN)適用於兩種狀況:
  • 基於一個範圍的檢索,通常查詢返回結果集小於表中記錄數的30%
  • 基於非惟一性索引的檢索
百萬級別或以上的數據如何刪除

關於索引:因爲索引須要額外的維護成本,由於索引文件是單獨存在的文件,因此當咱們對數據的增長,修改,刪除,都會產生額外的對索引文件的操做,這些操做須要消耗額外的IO,會下降增/改/刪的執行效率。因此,在咱們刪除數據庫百萬級別數據的時候,查詢MySQL官方手冊得知刪除數據的速度和建立的索引數量是成正比的。

  • 因此咱們想要刪除百萬數據的時候能夠先刪除索引(此時大概耗時三分多鐘)
  • 而後刪除其中無用數據(此過程須要不到兩分鐘)
  • 刪除完成後從新建立索引(此時數據較少了)建立索引也很是快,約十分鐘左右。
  • 與以前的直接刪除絕對是要快速不少,更別說萬一刪除中斷,一切刪除會回滾。那更是坑了。
前綴索引

語法:index(field(10)),使用字段值的前10個字符創建索引,默認是使用字段的所有內容創建索引。

前提:前綴的標識度高。好比密碼就適合創建前綴索引,由於密碼幾乎各不相同。

實操的難度:在於前綴截取的長度。

咱們能夠利用select count(*)/count(distinct left(password,prefixLen));,經過從調整prefixLen的值(從1自增)查看不一樣前綴長度的一個平均匹配度,接近1時就能夠了(表示一個密碼的前prefixLen個字符幾乎能肯定惟一一條記錄)

什麼是最左前綴原則?什麼是最左匹配原則
  • 顧名思義,就是最左優先,在建立多列索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊。
  • 最左前綴匹配原則,很是重要的原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配,好比a = 1 and b = 2 and c > 3 and d = 4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,若是創建(a,b,d,c)的索引則均可以用到,a,b,d的順序能夠任意調整。
  • =和in能夠亂序,好比a = 1 and b = 2 and c = 3 創建(a,b,c)索引能夠任意順序,mysql的查詢優化器會幫你優化成索引能夠識別的形式

B樹和B+樹的區別

在B樹中,你能夠將鍵和值存放在內部節點和葉子節點;但在B+樹中,內部節點都是鍵,沒有值,葉子節點同時存放鍵和值。

B+樹的葉子節點有一條鏈相連,而B樹的葉子節點各自獨立。

使用B樹的好處

B樹能夠在內部節點同時存儲鍵和值,所以,把頻繁訪問的數據放在靠近根節點的地方將會大大提升熱點數據的查詢效率。這種特性使得B樹在特定數據重複屢次查詢的場景中更加高效。

使用B+樹的好處

因爲B+樹的內部節點只存放鍵,不存放值,所以,一次讀取,能夠在內存頁中獲取更多的鍵,有利於更快地縮小查找範圍。 B+樹的葉節點由一條鏈相連,所以,當須要進行一次全數據遍歷的時候,B+樹只須要使用O(logN)時間找到最小的一個節點,而後經過鏈進行O(N)的順序遍歷便可。而B樹則須要對樹的每一層進行遍歷,這會須要更多的內存置換次數,所以也就須要花費更多的時間

Hash索引和B+樹全部有什麼區別或者說優劣呢?

首先要知道Hash索引和B+樹索引的底層實現原理:

hash索引底層就是hash表,進行查找時,調用一次hash函數就能夠獲取到相應的鍵值,以後進行回表查詢得到實際數據。B+樹底層實現是多路平衡查找樹。對於每一次的查詢都是從根節點出發,查找到葉子節點方能夠得到所查鍵值,而後根據查詢判斷是否須要回表查詢數據。

那麼能夠看出他們有如下的不一樣:

  • hash索引進行等值查詢更快(通常狀況下),可是卻沒法進行範圍查詢。

由於在hash索引中通過hash函數創建索引以後,索引的順序與原順序沒法保持一致,不能支持範圍查詢。而B+樹的的全部節點皆遵循(左節點小於父節點,右節點大於父節點,多叉樹也相似),自然支持範圍。

hash索引不支持使用索引進行排序,原理同上。

  • hash索引不支持模糊查詢以及多列索引的最左前綴匹配。原理也是由於hash函數的不可預測。AAAA和AAAAB的索引沒有相關性。
  • hash索引任什麼時候候都避免不了回表查詢數據,而B+樹在符合某些條件(聚簇索引,覆蓋索引等)的時候能夠只經過索引完成查詢。
  • hash索引雖然在等值查詢上較快,可是不穩定。性能不可預測,當某個鍵值存在大量重複的時候,發生hash碰撞,此時效率可能極差。而B+樹的查詢效率比較穩定,對於全部的查詢都是從根節點到葉子節點,且樹的高度較低。

所以,在大多數狀況下,直接選擇B+樹索引能夠得到穩定且較好的查詢速度。而不須要使用hash索引。

數據庫爲何使用B+樹而不是B樹
  • B樹只適合隨機檢索,而B+樹同時支持隨機檢索和順序檢索;
  • B+樹空間利用率更高,可減小I/O次數,磁盤讀寫代價更低。通常來講,索引自己也很大,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程當中就要產生磁盤I/O消耗。B+樹的內部結點並無指向關鍵字具體信息的指針,只是做爲索引使用,其內部結點比B樹小,盤塊能容納的結點中關鍵字數量更多,一次性讀入內存中能夠查找的關鍵字也就越多,相對的,IO讀寫次數也就下降了。而IO讀寫次數是影響索引檢索效率的最大因素;
  • B+樹的查詢效率更加穩定。B樹搜索有可能會在非葉子結點結束,越靠近根節點的記錄查找時間越短,只要找到關鍵字便可肯定記錄的存在,其性能等價於在關鍵字全集內作一次二分查找。而在B+樹中,順序檢索比較明顯,隨機檢索時,任何關鍵字的查找都必須走一條從根節點到葉節點的路,全部關鍵字的查找路徑長度相同,致使每個關鍵字的查詢效率至關。
  • B-樹在提升了磁盤IO性能的同時並無解決元素遍歷的效率低下的問題。B+樹的葉子節點使用指針順序鏈接在一塊兒,只要遍歷葉子節點就能夠實現整棵樹的遍歷。並且在數據庫中基於範圍的查詢是很是頻繁的,而B樹不支持這樣的操做。
  • 增刪文件(節點)時,效率更高。由於B+樹的葉子節點包含全部關鍵字,並以有序的鏈表結構存儲,這樣可很好提升增刪效率。
B+樹在知足聚簇索引和覆蓋索引的時候不須要回表查詢數據,

在B+樹的索引中,葉子節點可能存儲了當前的key值,也可能存儲了當前的key值以及整行的數據,這就是聚簇索引和非聚簇索引。 在InnoDB中,只有主鍵索引是聚簇索引,若是沒有主鍵,則挑選一個惟一鍵創建聚簇索引。若是沒有惟一鍵,則隱式的生成一個鍵來創建聚簇索引。

當查詢使用聚簇索引時,在對應的葉子節點,能夠獲取到整行數據,所以不用再次進行回表查詢。

什麼是聚簇索引?什麼時候使用聚簇索引與非聚簇索引
  • 聚簇索引:將數據存儲與索引放到了一塊,找到索引也就找到了數據
  • 非聚簇索引:將數據存儲於索引分開結構,索引結構的葉子節點指向了數據的對應行,myisam經過key_buffer把索引先緩存到內存中,當須要訪問數據時(經過索引訪問數據),在內存中直接搜索索引,而後經過索引找到磁盤相應數據,這也就是爲何索引不在key buffer命中時,速度慢的緣由

澄清一個概念:innodb中,在聚簇索引之上建立的索引稱之爲輔助索引,輔助索引訪問數據老是須要二次查找,非聚簇索引都是輔助索引,像複合索引、前綴索引、惟一索引,輔助索引葉子節點存儲的再也不是行的物理位置,而是主鍵值

什麼時候使用聚簇索引與非聚簇索引

非聚簇索引必定會回表查詢嗎?

不必定,這涉及到查詢語句所要求的字段是否所有命中了索引,若是所有命中了索引,那麼就沒必要再進行回表查詢。

舉個簡單的例子,假設咱們在員工表的年齡上創建了索引,那麼當進行select age from employee where age < 20的查詢時,在索引的葉子節點上,已經包含了age信息,不會再次進行回表查詢。

聯合索引是什麼?爲何須要注意聯合索引中的順序?

MySQL能夠使用多個字段同時創建一個索引,叫作聯合索引。在聯合索引中,若是想要命中索引,須要按照創建索引時的字段順序挨個使用,不然沒法命中索引。

具體緣由爲:

MySQL使用索引時須要索引有序,假設如今創建了"name,age,school"的聯合索引,那麼索引的排序爲: 先按照name排序,若是name相同,則按照age排序,若是age的值也相等,則按照school進行排序。

當進行查詢時,此時索引僅僅按照name嚴格有序,所以必須首先使用name字段進行等值查詢,以後對於匹配到的列而言,其按照age字段嚴格有序,此時能夠使用age字段用作索引查找,以此類推。所以在創建聯合索引的時候應該注意索引列的順序,通常狀況下,將查詢需求頻繁或者字段選擇性高的列放在前面。此外能夠根據特例的查詢或者表結構進行單獨的調整。

事務

什麼是數據庫事務?

事務是一個不可分割的數據庫操做序列,也是數據庫併發控制的基本單位,其執行的結果必須使數據庫從一種一致性狀態變到另外一種一致性狀態。事務是邏輯上的一組操做,要麼都執行,要麼都不執行。

事務最經典也常常被拿出來講例子就是轉帳了。

假如小明要給小紅轉帳1000元,這個轉帳會涉及到兩個關鍵操做就是:將小明的餘額減小1000元,將小紅的餘額增長1000元。萬一在這兩個操做之間忽然出現錯誤好比銀行系統崩潰,致使小明餘額減小而小紅的餘額沒有增長,這樣就不對了。事務就是保證這兩個關鍵操做要麼都成功,要麼都要失敗。

事物的四大特性(ACID)介紹一下?

關係性數據庫須要遵循ACID規則,具體內容以下:

  • 原子性: 事務是最小的執行單位,不容許分割。事務的原子性確保動做要麼所有完成,要麼徹底不起做用;
  • 一致性: 執行事務先後,數據保持一致,多個事務對同一個數據讀取的結果是相同的;
  • 隔離性: 併發訪問數據庫時,一個用戶的事務不被其餘事務所幹擾,各併發事務之間數據庫是獨立的;
  • 持久性: 一個事務被提交以後。它對數據庫中數據的改變是持久的,即便數據庫發生故障也不該該對其有任何影響。
什麼是髒讀?幻讀?不可重複讀?

髒讀(Drity Read):某個事務已更新一份數據,另外一個事務在此時讀取了同一份數據,因爲某些緣由,前一個RollBack了操做,則後一個事務所讀取的數據就會是不正確的。

不可重複讀(Non-repeatable read):在一個事務的兩次查詢之中數據不一致,這多是兩次查詢過程當中間插入了一個事務更新的原有的數據。

幻讀(Phantom Read):在一個事務的兩次查詢中數據筆數不一致,例若有一個事務查詢了幾列(Row)數據,而另外一個事務卻在此時插入了新的幾列數據,先前的事務在接下來的查詢中,就會發現有幾列數據是它先前所沒有的。

什麼是事務的隔離級別?MySQL的默認隔離級別是什麼?

爲了達到事務的四大特性,數據庫定義了4種不一樣的事務隔離級別,由低到高依次爲Read uncommitted、Read committed、Repeatable read、Serializable,這四個級別能夠逐個解決髒讀、不可重複讀、幻讀這幾類問題。

SQL 標準定義了四個隔離級別:

  • READ-UNCOMMITTED(讀取未提交): 最低的隔離級別,容許讀取還沒有提交的數據變動,可能會致使髒讀、幻讀或不可重複讀。
  • READ-COMMITTED(讀取已提交): 容許讀取併發事務已經提交的數據,能夠阻止髒讀,可是幻讀或不可重複讀仍有可能發生。
  • REPEATABLE-READ(可重複讀): 對同一字段的屢次讀取結果都是一致的,除非數據是被自己事務本身所修改,能夠阻止髒讀和不可重複讀,但幻讀仍有可能發生。
  • SERIALIZABLE(可串行化): 最高的隔離級別,徹底服從ACID的隔離級別。全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀。

這裏須要注意的是:Mysql 默認採用的 REPEATABLE_READ隔離級別 Oracle 默認採用的 READ_COMMITTED隔離級別

事務隔離機制的實現基於鎖機制和併發調度。其中併發調度使用的是MVVC(多版本併發控制),經過保存修改的舊版本信息來支持併發一致性讀和回滾等特性。

由於隔離級別越低,事務請求的鎖越少,因此大部分數據庫系統的隔離級別都是READ-COMMITTED(讀取提交內容):,可是你要知道的是InnoDB 存儲引擎默認使用 REPEATABLE-READ(可重讀)並不會有任何性能損失。

InnoDB 存儲引擎在 分佈式事務 的狀況下通常會用到SERIALIZABLE(可串行化)隔離級別。

對MySQL的鎖瞭解嗎

當數據庫有併發事務的時候,可能會產生數據的不一致,這時候須要一些機制來保證訪問的次序,鎖機制就是這樣的一個機制。

就像酒店的房間,若是你們隨意進出,就會出現多人搶奪同一個房間的狀況,而在房間上裝上鎖,申請到鑰匙的人才能夠入住而且將房間鎖起來,其餘人只有等他使用完畢才能夠再次使用。

隔離級別與鎖的關係

在Read Uncommitted級別下,讀取數據不須要加共享鎖,這樣就不會跟被修改的數據上的排他鎖衝突

在Read Committed級別下,讀操做須要加共享鎖,可是在語句執行完之後釋放共享鎖;

在Repeatable Read級別下,讀操做須要加共享鎖,可是在事務提交以前並不釋放共享鎖,也就是必須等待事務執行完畢之後才釋放共享鎖。

SERIALIZABLE 是限制性最強的隔離級別,由於該級別鎖定整個範圍的鍵,並一直持有鎖,直到事務完成。

按照鎖的粒度分數據庫鎖有哪些?鎖機制與InnoDB鎖算法

在關係型數據庫中,能夠按照鎖的粒度把數據庫鎖分爲行級鎖(INNODB引擎)、表級鎖(MYISAM引擎)和頁級鎖(BDB引擎 )。

MyISAM和InnoDB存儲引擎使用的鎖:
  • MyISAM採用表級鎖(table-level locking)。
  • InnoDB支持行級鎖(row-level locking)和表級鎖,默認爲行級鎖

行級鎖,表級鎖和頁級鎖對比

行級鎖是Mysql中鎖定粒度最細的一種鎖,表示只針對當前操做的行進行加鎖。行級鎖能大大減小數據庫操做的衝突。其加鎖粒度最小,但加鎖的開銷也最大。行級鎖分爲共享鎖 和 排他鎖。

特色:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。

表級鎖是MySQL中鎖定粒度最大的一種鎖,表示對當前操做的整張表加鎖,它實現簡單,資源消耗較少,被大部分MySQL引擎支持。最常使用的MYISAM與INNODB都支持表級鎖定。表級鎖定分爲表共享讀鎖(共享鎖)與表獨佔寫鎖(排他鎖)。

特色:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發出鎖衝突的機率最高,併發度最低。

頁級鎖是MySQL中鎖定粒度介於行級鎖和表級鎖中間的一種鎖。表級鎖速度快,但衝突多,行級衝突少,但速度慢。因此取了折衷的頁級,一次鎖定相鄰的一組記錄。

特色:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常

從鎖的類別上分MySQL都有哪些鎖呢?像上面那樣子進行鎖定豈不是有點阻礙併發效率了

從鎖的類別上來說,有共享鎖和排他鎖。

共享鎖: 又叫作讀鎖。 當用戶要進行數據的讀取時,對數據加上共享鎖。共享鎖能夠同時加上多個。

排他鎖: 又叫作寫鎖。 當用戶要進行數據的寫入時,對數據加上排他鎖。排他鎖只能夠加一個,他和其餘的排他鎖,共享鎖都相斥。

用上面的例子來講就是用戶的行爲有兩種,一種是來看房,多個用戶一塊兒看房是能夠接受的。 一種是真正的入住一晚,在這期間,不管是想入住的仍是想看房的都不能夠。

鎖的粒度取決於具體的存儲引擎,InnoDB實現了行級鎖,頁級鎖,表級鎖。

他們的加鎖開銷從大到小,併發能力也是從大到小。

MySQL中InnoDB引擎的行鎖是怎麼實現的?

答:InnoDB是基於索引來完成行鎖

例: select * from tab_with_index where id = 1 for update;

for update 能夠根據條件來完成行鎖鎖定,而且 id 是有索引鍵的列,若是 id 不是索引鍵那麼InnoDB將完成表鎖,併發將無從談起

InnoDB存儲引擎的鎖的算法有三種
  • Record lock:單個行記錄上的鎖
  • Gap lock:間隙鎖,鎖定一個範圍,不包括記錄自己
  • Next-key lock:record+gap 鎖定一個範圍,包含記錄自己
相關知識點:
  • innodb對於行的查詢使用next-key lock
  • Next-locking keying爲了解決Phantom Problem幻讀問題
  • 當查詢的索引含有惟一屬性時,將next-key lock降級爲record key
  • Gap鎖設計的目的是爲了阻止多個事務將記錄插入到同一範圍內,而這會致使幻讀問題的產生
  • 有兩種方式顯式關閉gap鎖:(除了外鍵約束和惟一性檢查外,其他狀況僅使用record lock) A. 將事務隔離級別設置爲RC B. 將參數innodb_locks_unsafe_for_binlog設置爲1
什麼是死鎖?怎麼解決?

死鎖是指兩個或多個事務在同一資源上相互佔用,並請求鎖定對方的資源,從而致使惡性循環的現象。

常見的解決死鎖的方法

  • 一、若是不一樣程序會併發存取多個表,儘可能約定以相同的順序訪問表,能夠大大下降死鎖機會。
  • 二、在同一個事務中,儘量作到一次鎖定所須要的全部資源,減小死鎖產生機率;
  • 三、對於很是容易產生死鎖的業務部分,能夠嘗試使用升級鎖定顆粒度,經過表級鎖定來減小死鎖產生的機率;

若是業務處理很差能夠用分佈式事務鎖或者使用樂觀鎖

數據庫的樂觀鎖和悲觀鎖是什麼?怎麼實現的?

數據庫管理系統(DBMS)中的併發控制的任務是確保在多個事務同時存取數據庫中同一數據時不破壞事務的隔離性和統一性以及數據庫的統一性。樂觀併發控制(樂觀鎖)和悲觀併發控制(悲觀鎖)是併發控制主要採用的技術手段。

悲觀鎖:假定會發生併發衝突,屏蔽一切可能違反數據完整性的操做。在查詢完數據的時候就把事務鎖起來,直到提交事務。實現方式:使用數據庫中的鎖機制

樂觀鎖:假設不會發生併發衝突,只在提交操做時檢查是否違反數據完整性。在修改數據的時候把事務鎖起來,經過version的方式來進行鎖定。實現方式:樂通常會使用版本號機制或CAS算法實現。

兩種鎖的使用場景

從上面對兩種鎖的介紹,咱們知道兩種鎖各有優缺點,不可認爲一種好於另外一種,像樂觀鎖適用於寫比較少的狀況下(多讀場景),即衝突真的不多發生的時候,這樣能夠省去了鎖的開銷,加大了系統的整個吞吐量。

但若是是多寫的狀況,通常會常常產生衝突,這就會致使上層應用會不斷的進行retry,這樣反卻是下降了性能,因此通常多寫的場景下用悲觀鎖就比較合適。

視圖

爲何要使用視圖?什麼是視圖?

爲了提升複雜SQL語句的複用性和表操做的安全性,MySQL數據庫管理系統提供了視圖特性。所謂視圖,本質上是一種虛擬表,在物理上是不存在的,其內容與真實的表類似,包含一系列帶有名稱的列和行數據。可是,視圖並不在數據庫中以儲存的數據值形式存在。行和列數據來自定義視圖的查詢所引用基本表,而且在具體引用視圖時動態生成。

視圖使開發者只關心感興趣的某些特定數據和所負責的特定任務,只能看到視圖中所定義的數據,而不是視圖所引用表中的數據,從而提升了數據庫中數據的安全性。

視圖有哪些特色?

視圖的特色以下:

  • 視圖的列能夠來自不一樣的表,是表的抽象和在邏輯意義上創建的新關係。
  • 視圖是由基本表(實表)產生的表(虛表)。
  • 視圖的創建和刪除不影響基本表。
  • 對視圖內容的更新(添加,刪除和修改)直接影響基本表。
  • 當視圖來自多個基本表時,不容許添加和刪除數據。

視圖的操做包括建立視圖,查看視圖,刪除視圖和修改視圖。

視圖的使用場景有哪些?

視圖根本用途:簡化sql查詢,提升開發效率。若是說還有另一個用途那就是兼容老的表結構。

下面是視圖的常見使用場景:

  • 重用SQL語句;
  • 簡化複雜的SQL操做。在編寫查詢後,能夠方便的重用它而沒必要知道它的基本查詢細節;
  • 使用表的組成部分而不是整個表;
  • 保護數據。能夠給用戶授予表的特定部分的訪問權限而不是整個表的訪問權限;
  • 更改數據格式和表示。視圖可返回與底層表的表示和格式不一樣的數據。
視圖的優勢
  • 查詢簡單化。視圖能簡化用戶的操做
  • 數據安全性。視圖使用戶能以多種角度看待同一數據,可以對機密數據提供安全保護
  • 邏輯數據獨立性。視圖對重構數據庫提供了必定程度的邏輯獨立性
視圖的缺點

性能。數據庫必須把視圖的查詢轉化成對基本表的查詢,若是這個視圖是由一個複雜的多表查詢所定義,那麼,即便是視圖的一個簡單查詢,數據庫也把它變成一個複雜的結合體,須要花費必定的時間。

修改限制。當用戶試圖修改視圖的某些行時,數據庫必須把它轉化爲對基本表的某些行的修改。事實上,當從視圖中插入或者刪除時,狀況也是這樣。對於簡單視圖來講,這是很方便的,可是,對於比較複雜的視圖,多是不可修改的

這些視圖有以下特徵:1.有UNIQUE等集合操做符的視圖。2.有GROUP BY子句的視圖。3.有諸如AVG\SUM\MAX等聚合函數的視圖。 4.使用DISTINCT關鍵字的視圖。5.鏈接表的視圖(其中有些例外)

什麼是遊標?

遊標是系統爲用戶開設的一個數據緩衝區,存放SQL語句的執行結果,每一個遊標區都有一個名字。用戶能夠經過遊標逐一獲取記錄並賦給主變量,交由主語言進一步處理。

存儲過程與函數

什麼是存儲過程?有哪些優缺點?

存儲過程是一個預編譯的SQL語句,優勢是容許模塊化的設計,就是說只須要建立一次,之後在該程序中就能夠調用屢次。若是某次操做須要執行屢次SQL,使用存儲過程比單純SQL語句執行要快。

優勢

  • 1)存儲過程是預編譯過的,執行效率高。
  • 2)存儲過程的代碼直接存放於數據庫中,經過存儲過程名直接調用,減小網絡通信。
  • 3)安全性高,執行存儲過程須要有必定權限的用戶。
  • 4)存儲過程能夠重複使用,減小數據庫開發人員的工做量。

缺點

  • 1)調試麻煩,可是用 PL/SQL Developer 調試很方便!彌補這個缺點。
  • 2)移植問題,數據庫端代碼固然是與數據庫相關的。可是若是是作工程型項目,基本不存在移植問題。
  • 3)從新編譯問題,由於後端代碼是運行前編譯的,若是帶有引用關係的對象發生改變時,受影響的存儲過程、包將須要從新編譯(不過也能夠設置成運行時刻自動編譯)。
  • 4)若是在一個程序系統中大量的使用存儲過程,到程序交付使用的時候隨着用戶需求的增長會致使數據結構的變化,接着就是系統的相關問題了,最後若是用戶想維護該系統能夠說是很難很難、並且代價是空前的,維護起來更麻煩。

觸發器

什麼是觸發器?觸發器的使用場景有哪些?

觸發器是用戶定義在關係表上的一類由事件驅動的特殊的存儲過程。觸發器是指一段代碼,當觸發某個事件時,自動執行這些代碼。

使用場景
  • 能夠經過數據庫中的相關表實現級聯更改。
  • 實時監控某張表中的某個字段的更改而須要作出相應的處理。
  • 例如能夠生成某些業務的編號。

注意不要濫用,不然會形成數據庫及應用程序的維護困難。

你們須要牢記以上基礎知識點,重點是理解數據類型CHAR和VARCHAR的差別,表存儲引擎InnoDB和MyISAM的區別。

MySQL中都有哪些觸發器?

在MySQL數據庫中有以下六種觸發器:

  • Before Insert
  • After Insert
  • Before Update
  • After Update
  • Before Delete
  • After Delete

經常使用SQL語句

SQL語句主要分爲哪幾類

數據定義語言DDL(Data Ddefinition Language)CREATE,DROP,ALTER

主要爲以上操做 即對邏輯結構等有操做的,其中包括表結構,視圖和索引。

數據查詢語言DQL(Data Query Language)SELECT

這個較爲好理解 即查詢操做,以select關鍵字。各類簡單查詢,鏈接查詢等 都屬於DQL。

數據操縱語言DML(Data Manipulation Language)INSERT,UPDATE,DELETE

主要爲以上操做 即對數據進行操做的,對應上面所說的查詢操做 DQL與DML共同構建了多數初級程序員經常使用的增刪改查操做。而查詢是較爲特殊的一種 被劃分到DQL中。

數據控制功能DCL(Data Control Language)GRANT,REVOKE,COMMIT,ROLLBACK

主要爲以上操做 即對數據庫安全性完整性等有操做的,能夠簡單的理解爲權限控制等。

超鍵、候選鍵、主鍵、外鍵分別是什麼?
  • 超鍵:在關係中能惟一標識元組的屬性集稱爲關係模式的超鍵。一個屬性能夠爲做爲一個超鍵,多個屬性組合在一塊兒也能夠做爲一個超鍵。超鍵包含候選鍵和主鍵。
  • 候選鍵:是最小超鍵,即沒有冗餘元素的超鍵。
  • 主鍵:數據庫表中對儲存數據對象予以惟一和完整標識的數據列或屬性的組合。一個數據列只能有一個主鍵,且主鍵的取值不能缺失,即不能爲空值(Null)。
  • 外鍵:在一個表中存在的另外一個表的主鍵稱此表的外鍵。
SQL 約束有哪幾種?
  • NOT NULL: 用於控制字段的內容必定不能爲空(NULL)。
  • UNIQUE: 控件字段內容不能重複,一個表容許有多個 Unique 約束。
  • PRIMARY KEY: 也是用於控件字段內容不能重複,但它在一個表只容許出現一個。
  • FOREIGN KEY: 用於預防破壞表之間鏈接的動做,也能防止非法數據插入外鍵列,由於它必須是它指向的那個表中的值之一。
  • CHECK: 用於控制字段的值範圍。
六種關聯查詢
  • i交叉鏈接(CROSS JOIN)
  • i內鏈接(INNER JOIN)
  • i外鏈接(LEFT JOIN/RIGHT JOIN)
  • i聯合查詢(UNION與UNION ALL)
  • i全鏈接(FULL JOIN)
  • i交叉鏈接(CROSS JOIN)

    SELECT * FROM A,B(,C)或者SELECT * FROM A CROSS JOIN B (CROSS JOIN C)#沒有任何關聯條件,結果是笛卡爾積,結果集會很大,沒有意義,不多使用內鏈接(INNER JOIN)SELECT * FROM A,B WHERE A.id=B.id或者SELECT * FROM A INNER JOIN B ON A.id=B.id多表中同時符合某種條件的數據記錄的集合,INNER JOIN能夠縮寫爲JOIN

    內鏈接分爲三類

  • 等值鏈接:ON A.id=B.id
  • 不等值鏈接:ON A.id > B.id
  • 自鏈接:SELECT * FROM A T1 INNER JOIN A T2 ON T1.id=T2.pid

外鏈接(LEFT JOIN/RIGHT JOIN)

  • 左外鏈接:LEFT OUTER JOIN, 以左表爲主,先查詢出左表,按照ON後的關聯條件匹配右表,沒有匹配到的用NULL填充,能夠簡寫成LEFT JOIN
  • 右外鏈接:RIGHT OUTER JOIN, 以右表爲主,先查詢出右表,按照ON後的關聯條件匹配左表,沒有匹配到的用NULL填充,能夠簡寫成RIGHT JOIN

聯合查詢(UNION與UNION ALL)

SELECT * FROM A UNION SELECT * FROM B UNION ...
  • 就是把多個結果集集中在一塊兒,UNION前的結果爲基準,須要注意的是聯合查詢的列數要相等,相同的記錄行會合並
  • 若是使用UNION ALL,不會合並重復的記錄行
  • 效率 UNION 高於 UNION ALL

全鏈接(FULL JOIN)

  • MySQL不支持全鏈接
  • 能夠使用LEFT JOIN 和UNION和RIGHT JOIN聯合使用

    SELECT * FROM A LEFT JOIN B ON A.id=B.id UNIONSELECT * FROM A RIGHT JOIN B ON A.id=B.id

    錶鏈接面試題

有2張表,1張R、1張S,R表有ABC三列,S表有CD兩列,表中各有三條記錄。

#交叉鏈接(笛卡爾積):

select r.*,s.* from r,s

內鏈接結果:

select r.*,s.* from r inner join s on r.c=s.c

左鏈接結果:

select r.*,s.* from r left join s on r.c=s.c

右鏈接結果:

select r.*,s.* from r right join s on r.c=s.c

全錶鏈接的結果(MySql不支持,Oracle支持):

select r.*,s.* from r full join s on r.c=s.c

什麼是子查詢

條件:一條SQL語句的查詢結果作爲另外一條查詢語句的條件或查詢結果

嵌套:多條SQL語句嵌套使用,內部的SQL查詢語句稱爲子查詢。

子查詢的三種狀況

子查詢是單行單列的狀況:結果集是一個值,父查詢使用:=、 <、 > 等運算符

-- 查詢工資最高的員工是誰? 
select  * from employee where salary=(select max(salary) from employee);

子查詢是多行單列的狀況:結果集相似於一個數組,父查詢使用:in 運算符

-- 查詢工資最高的員工是誰? 
select  * from employee where salary=(select max(salary) from employee);

子查詢是多行多列的狀況:結果集相似於一張虛擬表,不能用於where條件,用於select子句中作爲子表

-- 1) 查詢出2011年之後入職的員工信息
-- 2) 查詢全部的部門信息,與上面的虛擬表中的信息比對,找出全部部門ID相等的員工。
select * from dept d,  (select * from employee where join_date > '2011-1-1') e where e.dept_id =  d.id;    

-- 使用錶鏈接:
select d.*, e.* from  dept d inner join employee e on d.id = e.dept_id where e.join_date >  '2011-1-1'
mysql中 in 和 exists 區別

mysql中的in語句是把外表和內表做hash 鏈接,而exists語句是對外表做loop循環,每次loop循環再對內表進行查詢。一直你們都認爲exists比in語句的效率要高,這種說法實際上是不許確的。這個是要區分環境的。

  • 若是查詢的兩個表大小至關,那麼用in和exists差異不大。
  • 若是兩個表中一個較小,一個是大表,則子查詢表大的用exists,子查詢表小的用in。
  • not in 和not exists:若是查詢語句使用了not in,那麼內外表都進行全表掃描,沒有用到索引;而not extsts的子查詢依然能用到表上的索引。因此不管那個表大,用not exists都比not in要快。
varchar與char的區別
  • char的特色

    • char表示定長字符串,長度是固定的;
    • 若是插入數據的長度小於char的固定長度時,則用空格填充;
    • 由於長度固定,因此存取速度要比varchar快不少,甚至能快50%,但正由於其長度固定,因此會佔據多餘的空間,是空間換時間的作法;
    • 對於char來講,最多能存放的字符個數爲255,和編碼無關
  • varchar的特色

    • varchar表示可變長字符串,長度是可變的;
    • 插入的數據是多長,就按照多長來存儲;
    • varchar在存取方面與char相反,它存取慢,由於長度不固定,但正因如此,不佔據多餘的空間,是時間換空間的作法;
    • 對於varchar來講,最多能存放的字符個數爲65532

總之,結合性能角度(char更快)和節省磁盤空間角度(varchar更小),具體狀況還需具體來設計數據庫纔是穩當的作法。

varchar(50)中50的涵義

最多存放50個字符,varchar(50)和(200)存儲hello所佔空間同樣,但後者在排序時會消耗更多內存,由於order by col採用fixed_length計算col長度(memory引擎也同樣)。在早期 MySQL 版本中, 50 表明字節數,如今表明字符數。

int(20)中20的涵義

是指顯示字符的長度。20表示最大顯示寬度爲20,但仍佔4字節存儲,存儲範圍不變;

不影響內部存儲,只是影響帶 zerofill 定義的 int 時,前面補多少個 0,易於報表展現

mysql爲何這麼設計

對大多數應用沒有意義,只是規定一些工具用來顯示字符的個數;int(1)和int(20)存儲和計算均同樣;

mysql中int(10)和char(10)以及varchar(10)的區別

  • int(10)的10表示顯示的數據的長度,不是存儲數據的大小;chart(10)和varchar(10)的10表示存儲數據的大小,即表示存儲多少個字符。
  • int(10) 10位的數據長度 9999999999,佔32個字節,int型4位
  • char(10) 10位固定字符串,不足補空格 最多10個字符
  • varchar(10) 10位可變字符串,不足補空格 最多10個字符
  • char(10)表示存儲定長的10個字符,不足10個就用空格補齊,佔用更多的存儲空間
  • varchar(10)表示存儲10個變長的字符,存儲多少個就是多少個,空格也按一個字符存儲,這一點是和char(10)的空格不一樣的,char(10)的空格表示佔位不算一個字符
FLOAT和DOUBLE的區別是什麼?
  • FLOAT類型數據能夠存儲至多8位十進制數,並在內存中佔4字節。
  • DOUBLE類型數據能夠存儲至多18位十進制數,並在內存中佔8字節。
drop、delete與truncate的區別

三者都表示刪除,可是三者有一些差異:

所以,在再也不須要一張表的時候,用drop;在想刪除部分數據行時候,用delete;在保留表而刪除全部數據的時候用truncate。

UNION與UNION ALL的區別?
  • 若是使用UNION ALL,不會合並重復的記錄行
  • 效率 UNION 高於 UNION ALL

SQL優化

如何定位及優化SQL語句的性能問題?建立的索引有沒有被使用到?或者說怎麼才能夠知道這條語句運行很慢的緣由?

對於低性能的SQL語句的定位,最重要也是最有效的方法就是使用執行計劃,MySQL提供了explain命令來查看語句的執行計劃。 咱們知道,無論是哪一種數據庫,或者是哪一種數據庫引擎,在對一條SQL語句進行執行的過程當中都會作不少相關的優化,對於查詢語句,最重要的優化方式就是使用索引。 而執行計劃,就是顯示數據庫引擎對於SQL語句的執行的詳細狀況,其中包含了是否使用索引,使用什麼索引,使用的索引的相關信息等。

執行計劃包含的信息 id 有一組數字組成。表示一個查詢中各個子查詢的執行順序;

  • id相同執行順序由上至下。
  • id不一樣,id值越大優先級越高,越先被執行。
  • id爲null時表示一個結果集,不須要使用它查詢,常出如今包含union等查詢語句中。

select_type 每一個子查詢的查詢類型,一些常見的查詢類型。

table 查詢的數據表,當從衍生表中查數據時會顯示 x 表示對應的執行計劃id partitions 表分區、表建立的時候能夠指定經過那個列進行表分區。 舉個例子:

create table tmp (
    id int unsigned not null AUTO_INCREMENT,
    name varchar(255),
    PRIMARY KEY (id)
) engine = innodb
partition by key (id) partitions 5;

type(很是重要,能夠看到有沒有走索引) 訪問類型

  • ALL 掃描全表數據
  • index 遍歷索引
  • range 索引範圍查找
  • index_subquery 在子查詢中使用 ref
  • unique_subquery 在子查詢中使用 eq_ref
  • ref_or_null 對Null進行索引的優化的 ref
  • fulltext 使用全文索引
  • ref 使用非惟一索引查找數據
  • eq_ref 在join查詢中使用PRIMARY KEYorUNIQUE NOT NULL索引關聯。

possible_keys 可能使用的索引,注意不必定會使用。查詢涉及到的字段上若存在索引,則該索引將被列出來。當該列爲 NULL時就要考慮當前的SQL是否須要優化了。

key 顯示MySQL在查詢中實際使用的索引,若沒有使用索引,顯示爲NULL。

TIPS:查詢中若使用了覆蓋索引(覆蓋索引:索引的數據覆蓋了須要查詢的全部數據),則該索引僅出如今key列表中

key_length 索引長度

ref 表示上述表的鏈接匹配條件,即哪些列或常量被用於查找索引列上的值

rows 返回估算的結果集數目,並非一個準確的值。

extra 的信息很是豐富,常見的有:

  • Using index 使用覆蓋索引
  • Using where 使用了用where子句來過濾結果集
  • Using filesort 使用文件排序,使用非索引列進行排序時出現,很是消耗性能,儘可能優化。
  • Using temporary 使用了臨時表 sql優化的目標能夠參考阿里開發手冊

【推薦】SQL性能優化的目標:至少要達到 range 級別,要求是ref級別,若是能夠是consts最好。

說明:

1) consts 單表中最多隻有一個匹配行(主鍵或者惟一索引),在優化階段便可讀取到數據。 
2) ref 指的是使用普通的索引(normal index)。 
3) range 對索引進行範圍檢索。 
反例:explain表的結果,type=index,索引物理文件全掃描,速度很是慢,這個index級別比較range還低,與全表掃描是小巫見大巫。

SQL的生命週期?

  • 應用服務器與數據庫服務器創建一個鏈接
  • 數據庫進程拿到請求sql
  • 解析並生成執行計劃,執行
  • 讀取數據到內存並進行邏輯處理
  • 經過步驟一的鏈接,發送結果到客戶端
  • 關掉鏈接,釋放資源

大表數據查詢,怎麼優化
  • 優化shema、sql語句+索引;
  • 第二加緩存,memcached, redis;
  • 主從複製,讀寫分離;
  • 垂直拆分,根據你模塊的耦合度,將一個大的系統分爲多個小的系統,也就是分佈式系統;
  • 水平切分,針對數據量大的表,這一步最麻煩,最能考驗技術水平,要選擇一個合理的sharding key, 爲了有好的查詢效率,表結構也要改動,作必定的冗餘,應用也要改,sql中儘可能帶sharding key,將數據定位到限定的表上去查,而不是掃描所有的表;
超大分頁怎麼處理?

超大的分頁通常從兩個方向上來解決.

  • 數據庫層面,這也是咱們主要集中關注的(雖然收效沒那麼大),相似於select * from table where age > 20 limit 1000000,10這種查詢其實也是有能夠優化的餘地的. 這條語句須要load1000000數據而後基本上所有丟棄,只取10條固然比較慢. 當時咱們能夠修改成select * from table where id in (select id from table where age > 20 limit 1000000,10).這樣雖然也load了一百萬的數據,可是因爲索引覆蓋,要查詢的全部字段都在索引中,因此速度會很快. 同時若是ID連續的好,咱們還能夠select * from table where id > 1000000 limit 10,效率也是不錯的,優化的可能性有許多種,可是核心思想都同樣,就是減小load的數據.
  • 從需求的角度減小這種請求…主要是不作相似的需求(直接跳轉到幾百萬頁以後的具體某一頁.只容許逐頁查看或者按照給定的路線走,這樣可預測,可緩存)以及防止ID泄漏且連續被人惡意攻擊.

解決超大分頁,其實主要是靠緩存,可預測性的提早查到內容,緩存至redis等k-V數據庫中,直接返回便可.

在阿里巴巴《Java開發手冊》中,對超大分頁的解決辦法是相似於上面提到的第一種.

【推薦】利用延遲關聯或者子查詢優化超多分頁場景。 

說明:MySQL並非跳過offset行,而是取offset+N行,而後返回放棄前offset行,返回N行,那當offset特別大的時候,效率就很是的低下,要麼控制返回的總頁數,要麼對超過特定閾值的頁數進行SQL改寫。 

正例:先快速定位須要獲取的id段,而後再關聯: 

SELECT a.* FROM 表1 a, (select id from 表1 where 條件 LIMIT 100000,20 ) b where a.id=b.id
mysql 分頁

LIMIT 子句能夠被用於強制 SELECT 語句返回指定的記錄數。LIMIT 接受一個或兩個數字參數。參數必須是一個整數常量。若是給定兩個參數,第一個參數指定第一個返回記錄行的偏移量,第二個參數指定返回記錄行的最大數目。初始記錄行的偏移量是 0(而不是 1)

mysql> SELECT * FROM table LIMIT 5,10; // 檢索記錄行 6-15

爲了檢索從某一個偏移量到記錄集的結束全部的記錄行,能夠指定第二個參數爲 -1:

mysql> SELECT * FROM table LIMIT 95,-1; // 檢索記錄行 96-last.

若是隻給定一個參數,它表示返回最大的記錄行數目:

mysql> SELECT * FROM table LIMIT 5; //檢索前 5 個記錄行

換句話說,LIMIT n 等價於 LIMIT 0,n。

慢查詢日誌

用於記錄執行時間超過某個臨界值的SQL日誌,用於快速定位慢查詢,爲咱們的優化作參考。

開啓慢查詢日誌

配置項:slow_query_log

能夠使用show variables like ‘slov_query_log’查看是否開啓,若是狀態值爲OFF,能夠使用set GLOBAL slow_query_log = on來開啓,它會在datadir下產生一個xxx-slow.log的文件。

設置臨界時間

配置項:long_query_time

查看:show VARIABLES like 'long_query_time',單位秒

設置:set long_query_time=0.5

實操時應該從長時間設置到短的時間,即將最慢的SQL優化掉

查看日誌,一旦SQL超過了咱們設置的臨界時間就會被記錄到xxx-slow.log中

關心過業務系統裏面的sql耗時嗎?統計過慢查詢嗎?對慢查詢都怎麼優化過?

在業務系統中,除了使用主鍵進行的查詢,其餘的我都會在測試庫上測試其耗時,慢查詢的統計主要由運維在作,會按期將業務中的慢查詢反饋給咱們。

慢查詢的優化首先要搞明白慢的緣由是什麼? 是查詢條件沒有命中索引?是load了不須要的數據列?仍是數據量太大?

因此優化也是針對這三個方向來的,

  • 首先分析語句,看看是否load了額外的數據,多是查詢了多餘的行而且拋棄掉了,多是加載了許多結果中並不須要的列,對語句進行分析以及重寫。
  • 分析語句的執行計劃,而後得到其使用索引的狀況,以後修改語句或者修改索引,使得語句能夠儘量的命中索引。
  • 若是對語句的優化已經沒法進行,能夠考慮表中的數據量是否太大,若是是的話能夠進行橫向或者縱向的分表。
爲何要儘可能設定一個主鍵?

主鍵是數據庫確保數據行在整張表惟一性的保障,即便業務上本張表沒有主鍵,也建議添加一個自增加的ID列做爲主鍵。設定了主鍵以後,在後續的刪改查的時候可能更加快速以及確保操做數據範圍安全。

主鍵使用自增ID仍是UUID?

推薦使用自增ID,不要使用UUID。

由於在InnoDB存儲引擎中,主鍵索引是做爲聚簇索引存在的,也就是說,主鍵索引的B+樹葉子節點上存儲了主鍵索引以及所有的數據(按照順序),若是主鍵索引是自增ID,那麼只須要不斷向後排列便可,若是是UUID,因爲到來的ID與原來的大小不肯定,會形成很是多的數據插入,數據移動,而後致使產生不少的內存碎片,進而形成插入性能的降低。

總之,在數據量大一些的狀況下,用自增主鍵性能會好一些。

關於主鍵是聚簇索引,若是沒有主鍵,InnoDB會選擇一個惟一鍵來做爲聚簇索引,若是沒有惟一鍵,會生成一個隱式的主鍵。

字段爲何要求定義爲not null?

null值會佔用更多的字節,且會在程序中形成不少與預期不符的狀況。

若是要存儲用戶的密碼散列,應該使用什麼字段進行存儲?

密碼散列,鹽,用戶身份證號等固定長度的字符串應該使用char而不是varchar來存儲,這樣能夠節省空間且提升檢索效率。

優化查詢過程當中的數據訪問
  • 訪問數據太多致使查詢性能降低
  • 肯定應用程序是否在檢索大量超過須要的數據,多是太多行或列
  • 確認MySQL服務器是否在分析大量沒必要要的數據行
  • 避免犯以下SQL語句錯誤
  • 查詢不須要的數據。解決辦法:使用limit解決
  • 多表關聯返回所有列。解決辦法:指定列名
  • 老是返回所有列。解決辦法:避免使用SELECT *
  • 重複查詢相同的數據。解決辦法:能夠緩存數據,下次直接讀取緩存
  • 是否在掃描額外的記錄。解決辦法:
  • 使用explain進行分析,若是發現查詢須要掃描大量的數據,但只返回少數的行,能夠經過以下技巧去優化:
  • 使用索引覆蓋掃描,把全部的列都放到索引中,這樣存儲引擎不須要回表獲取對應行就能夠返回結果。
  • 改變數據庫和表的結構,修改數據表範式
  • 重寫SQL語句,讓優化器能夠以更優的方式執行查詢。
優化長難的查詢語句
  • 一個複雜查詢仍是多個簡單查詢
  • MySQL內部每秒能掃描內存中上百萬行數據,相比之下,響應數據給客戶端就要慢得多
  • 使用盡量小的查詢是好的,可是有時將一個大的查詢分解爲多個小的查詢是頗有必要的。
  • 切分查詢
  • 將一個大的查詢分爲多個小的相同的查詢
  • 一次性刪除1000萬的數據要比一次刪除1萬,暫停一會的方案更加損耗服務器開銷。
  • 分解關聯查詢,讓緩存的效率更高。
  • 執行單個查詢能夠減小鎖的競爭。
  • 在應用層作關聯更容易對數據庫進行拆分。
  • 查詢效率會有大幅提高。
  • 較少冗餘記錄的查詢。
優化特定類型的查詢語句
  • count(*)會忽略全部的列,直接統計全部列數,不要使用count(列名)
  • MyISAM中,沒有任何where條件的count(*)很是快。
  • 當有where條件時,MyISAM的count統計不必定比其它引擎快。
  • 能夠使用explain查詢近似值,用近似值替代count(*)
  • 增長彙總表
  • 使用緩存
優化關聯查詢
  • 肯定ON或者USING子句中是否有索引。
  • 確保GROUP BY和ORDER BY只有一個表中的列,這樣MySQL纔有可能使用索引。
優化子查詢
  • 用關聯查詢替代
  • 優化GROUP BY和DISTINCT
  • 這兩種查詢據能夠使用索引來優化,是最有效的優化方法
  • 關聯查詢中,使用標識列分組的效率更高
  • 若是不須要ORDER BY,進行GROUP BY時加ORDER BY NULL,MySQL不會再進行文件排序。
  • WITH ROLLUP超級聚合,能夠挪到應用程序處理
優化LIMIT分頁
  • LIMIT偏移量大的時候,查詢效率較低
  • 能夠記錄上次查詢的最大ID,下次查詢時直接根據該ID來查詢
優化UNION查詢
  • UNION ALL的效率高於UNION
優化WHERE子句

解題方法

對於此類考題,先說明如何定位低效SQL語句,而後根據SQL語句可能低效的緣由作排查,先從索引着手,若是索引沒有問題,考慮以上幾個方面,數據訪問的問題,長難查詢句的問題仍是一些特定類型優化的問題,逐一回答。

SQL語句優化的一些方法?
  • 1.對查詢進行優化,應儘可能避免全表掃描,首先應考慮在 where 及 order by 涉及的列上創建索引。
  • 2.應儘可能避免在 where 子句中對字段進行 null 值判斷,不然將致使引擎放棄使用索引而進行全表掃描,如:

    select id from t where num is null
    -- 能夠在num上設置默認值0,確保表中num列沒有null值,而後這樣查詢:
    select id from t where num=
  • 3.應儘可能避免在 where 子句中使用!=或<>操做符,不然引擎將放棄使用索引而進行全表掃描。
  • 4.應儘可能避免在 where 子句中使用or 來鏈接條件,不然將致使引擎放棄使用索引而進行全表掃描,如:

    select id from t where num=10 or num=20
    -- 能夠這樣查詢:
    select id from t where num=10 union all select id from t where num=2
  • 5.in 和 not in 也要慎用,不然會致使全表掃描,如:

    select id from t where num in(1,2,3) 
    -- 對於連續的數值,能用 between 就不要用 in 了:
    select id from t where num between 1 and 3
  • 6.下面的查詢也將致使全表掃描:select id from t where name like ‘%李%’若要提升效率,能夠考慮全文檢索。
  • 7.若是在 where 子句中使用參數,也會致使全表掃描。由於SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,若是在編譯時創建訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。以下面語句將進行全表掃描:

    select id from t where num=@num
    -- 能夠改成強制查詢使用索引:
    select id from t with(index(索引名)) where num=@num
  • 8.應儘可能避免在 where 子句中對字段進行表達式操做,這將致使引擎放棄使用索引而進行全表掃描。如:

    select id from t where num/2=100
    -- 應改成:
    select id from t where num=100*2
  • 9.應儘可能避免在where子句中對字段進行函數操做,這將致使引擎放棄使用索引而進行全表掃描。如:

    select id from t where substring(name,1,3)=’abc’
    -- name以abc開頭的id應改成:
    select id from t where name like ‘abc%’
  • 10.不要在 where 子句中的「=」左邊進行函數、算術運算或其餘表達式運算,不然系統將可能沒法正確使用索引。

數據庫優化

爲何要優化
  • 系統的吞吐量瓶頸每每出如今數據庫的訪問速度上
  • 隨着應用程序的運行,數據庫的中的數據會愈來愈多,處理時間會相應變慢
  • 數據是存放在磁盤上的,讀寫速度沒法和內存相比
  • 優化原則:減小系統瓶頸,減小資源佔用,增長系統的反應速度。
數據庫結構優化

一個好的數據庫設計方案對於數據庫的性能每每會起到事半功倍的效果。

須要考慮數據冗餘、查詢和更新的速度、字段的數據類型是否合理等多方面的內容。

將字段不少的表分解成多個表

對於字段較多的表,若是有些字段的使用頻率很低,能夠將這些字段分離出來造成新表。

由於當一個表的數據量很大時,會因爲使用頻率低的字段的存在而變慢。

增長中間表

對於須要常常聯合查詢的表,能夠創建中間表以提升查詢效率。

經過創建中間表,將須要經過聯合查詢的數據插入到中間表中,而後將原來的聯合查詢改成對中間表的查詢。

增長冗餘字段

設計數據表時應儘可能遵循範式理論的規約,儘量的減小冗餘字段,讓數據庫設計看起來精緻、優雅。可是,合理的加入冗餘字段能夠提升查詢速度。

表的規範化程度越高,表和表之間的關係越多,須要鏈接查詢的狀況也就越多,性能也就越差。

注意:冗餘字段的值在一個表中修改了,就要想辦法在其餘表中更新,不然就會致使數據不一致的問題。

MySQL數據庫cpu飆升到500%的話他怎麼處理?

當 cpu 飆升到 500%時,先用操做系統命令 top 命令觀察是否是 mysqld 佔用致使的,若是不是,找出佔用高的進程,並進行相關處理。

若是是 mysqld 形成的, show processlist,看看裏面跑的 session 狀況,是否是有消耗資源的 sql 在運行。找出消耗高的 sql,看看執行計劃是否準確, index 是否缺失,或者實在是數據量太大形成。

通常來講,確定要 kill 掉這些線程(同時觀察 cpu 使用率是否降低),等進行相應的調整(好比說加索引、改 sql、改內存參數)以後,再從新跑這些 SQL。

也有多是每一個 sql 消耗資源並很少,可是忽然之間,有大量的 session 連進來致使 cpu 飆升,這種狀況就須要跟應用一塊兒來分析爲什麼鏈接數會激增,再作出相應的調整,好比說限制鏈接數等

大表怎麼優化?某個表有近千萬數據,CRUD比較慢,如何優化?分庫分表了是怎麼作的?分表分庫了有什麼問題?有用到中間件麼?他們的原理知道麼?

當MySQL單表記錄數過大時,數據庫的CRUD性能會明顯降低,一些常見的優化措施以下:

  • 限定數據的範圍: 務必禁止不帶任何限制數據範圍條件的查詢語句。好比:咱們當用戶在查詢訂單歷史的時候,咱們能夠控制在一個月的範圍內。;
  • 讀/寫分離: 經典的數據庫拆分方案,主庫負責寫,從庫負責讀;
  • 緩存: 使用MySQL的緩存,另外對重量級、更新少的數據能夠考慮使用應用級別的緩存;
    還有就是經過分庫分表的方式進行優化,主要有垂直分表和水平分表

垂直分區

根據數據庫裏面數據表的相關性進行拆分。 例如,用戶表中既有用戶的登陸信息又有用戶的基本信息,能夠將用戶表拆分紅兩個單獨的表,甚至放到單獨的庫作分庫。

簡單來講垂直拆分是指數據表列的拆分,把一張列比較多的表拆分爲多張表。 以下圖所示,這樣來講你們應該就更容易理解了。

垂直拆分的優勢: 能夠使得行數據變小,在查詢時減小讀取的Block數,減小I/O次數。此外,垂直分區能夠簡化表的結構,易於維護。

垂直拆分的缺點: 主鍵會出現冗餘,須要管理冗餘列,並會引發Join操做,能夠經過在應用層進行Join來解決。此外,垂直分區會讓事務變得更加複雜;

垂直分表

把主鍵和一些列放在一個表,而後把主鍵和另外的列放在另外一個表中

適用場景

  • 一、若是一個表中某些列經常使用,另一些列不經常使用
  • 二、能夠使數據行變小,一個數據頁能存儲更多數據,查詢時減小I/O次數
  • 缺點

    • 有些分表的策略基於應用層的邏輯算法,一旦邏輯算法改變,整個分表邏輯都會改變,擴展性較差
    • 對於應用層來講,邏輯算法增長開發成本
    • 管理冗餘列,查詢全部數據須要join操做

水平分區

保持數據表結構不變,經過某種策略存儲數據分片。這樣每一片數據分散到不一樣的表或者庫中,達到了分佈式的目的。 水平拆分能夠支撐很是大的數據量。

水平拆分是指數據錶行的拆分,表的行數超過200萬行時,就會變慢,這時能夠把一張的表的數據拆成多張表來存放。舉個例子:咱們能夠將用戶信息表拆分紅多個用戶信息表,這樣就能夠避免單一表數據量過大對性能形成影響。

水品拆分能夠支持很是大的數據量。須要注意的一點是:分表僅僅是解決了單一表數據過大的問題,但因爲表的數據仍是在同一臺機器上,其實對於提高MySQL併發能力沒有什麼意義,因此 水平拆分最好分庫 。

水平拆分可以 支持很是大的數據量存儲,應用端改造也少,但 分片事務難以解決 ,跨界點Join性能較差,邏輯複雜。

《Java工程師修煉之道》的做者推薦 儘可能不要對數據進行分片,由於拆分會帶來邏輯、部署、運維的各類複雜度 ,通常的數據表在優化得當的狀況下支撐千萬如下的數據量是沒有太大問題的。若是實在要分片,儘可能選擇客戶端分片架構,這樣能夠減小一次和中間件的網絡I/O。

水平分表

表很大,分割後能夠下降在查詢時須要讀的數據和索引的頁數,同時也下降了索引的層數,提升查詢次數

  • 適用場景

    • 一、表中的數據自己就有獨立性,例如表中分表記錄各個地區的數據或者不一樣時期的數據,特別是有些數據經常使用,有些不經常使用。
    • 二、須要把數據存放在多個介質上。
  • 水平切分的缺點

    • 一、給應用增長複雜度,一般查詢時須要多個表名,查詢全部數據都需UNION操做
    • 二、在許多數據庫應用中,這種複雜度會超過它帶來的優勢,查詢時會增長讀一個索引層的磁盤次數

下面補充一下數據庫分片的兩種常見方案:

客戶端代理: 分片邏輯在應用端,封裝在jar包中,經過修改或者封裝JDBC層來實現。 噹噹網的 Sharding-JDBC 、阿里的TDDL是兩種比較經常使用的實現。

中間件代理: 在應用和數據中間加了一個代理層。分片邏輯統一維護在中間件服務中。 咱們如今談的 Mycat 、360的Atlas、網易的DDB等等都是這種架構的實現。

分庫分表後面臨的問題
  • 事務支持 分庫分表後,就成了分佈式事務了。若是依賴數據庫自己的分佈式事務管理功能去執行事務,將付出高昂的性能代價; 若是由應用程序去協助控制,造成程序邏輯上的事務,又會形成編程方面的負擔。
  • 跨庫join
    只要是進行切分,跨節點Join的問題是不可避免的。可是良好的設計和切分卻能夠減小此類狀況的發生。解決這一問題的廣泛作法是分兩次查詢實現。在第一次查詢的結果集中找出關聯數據的id,根據這些id發起第二次請求獲得關聯數據。 分庫分表方案產品
  • 跨節點的count,order by,group by以及聚合函數問題 這些是一類問題,由於它們都須要基於所有數據集合進行計算。多數的代理都不會自動處理合並工做。解決方案:與解決跨節點join問題的相似,分別在各個節點上獲得結果後在應用程序端進行合併。和join不一樣的是每一個結點的查詢能夠並行執行,所以不少時候它的速度要比單一大表快不少。但若是結果集很大,對應用程序內存的消耗是一個問題。
  • 數據遷移,容量規劃,擴容等問題 來自淘寶綜合業務平臺團隊,它利用對2的倍數取餘具備向前兼容的特性(如對4取餘得1的數對2取餘也是1)來分配數據,避免了行級別的數據遷移,可是依然須要進行表級別的遷移,同時對擴容規模和分表數量都有限制。總得來講,這些方案都不是十分的理想,多多少少都存在一些缺點,這也從一個側面反映出了Sharding擴容的難度。
  • ID問題
  • 一旦數據庫被切分到多個物理結點上,咱們將不能再依賴數據庫自身的主鍵生成機制。一方面,某個分區數據庫自生成的ID沒法保證在全局上是惟一的;另外一方面,應用程序在插入數據以前須要先得到ID,以便進行SQL路由. 一些常見的主鍵生成策略

UUID 使用UUID做主鍵是最簡單的方案,可是缺點也是很是明顯的。因爲UUID很是的長,除佔用大量存儲空間外,最主要的問題是在索引上,在創建索引和基於索引進行查詢時都存在性能問題。 Twitter的分佈式自增ID算法Snowflake 在分佈式系統中,須要生成全局UID的場合仍是比較多的,twitter的snowflake解決了這種需求,實現也仍是很簡單的,除去配置信息,核心代碼就是毫秒級時間41位 機器ID 10位 毫秒內序列12位。

跨分片的排序分頁

般來說,分頁時須要按照指定字段進行排序。當排序字段就是分片字段的時候,咱們經過分片規則能夠比較容易定位到指定的分片,而當排序字段非分片字段的時候,狀況就會變得比較複雜了。爲了最終結果的準確性,咱們須要在不一樣的分片節點中將數據進行排序並返回,並將不一樣分片返回的結果集進行彙總和再次排序,最後再返回給用戶。以下圖所示:

MySQL的複製原理以及流程

主從複製:將主數據庫中的DDL和DML操做經過二進制日誌(BINLOG)傳輸到從數據庫上,而後將這些日誌從新執行(重作);從而使得從數據庫的數據與主數據庫保持一致。

主從複製的做用
  • 主數據庫出現問題,能夠切換到從數據庫。
  • 能夠進行數據庫層面的讀寫分離。
  • 能夠在從數據庫上進行平常備份。
MySQL主從複製解決的問題
  • 數據分佈:隨意開始或中止複製,並在不一樣地理位置分佈數據備份
  • 負載均衡:下降單個服務器的壓力
  • 高可用和故障切換:幫助應用程序避免單點失敗
  • 升級測試:能夠用更高版本的MySQL做爲從庫
MySQL主從複製工做原理
  • 在主庫上把數據更高記錄到二進制日誌
  • 從庫將主庫的日誌複製到本身的中繼日誌
  • 從庫讀取中繼日誌的事件,將其重放到從庫數據中

基本原理流程,3個線程以及之間的關聯

主:binlog線程——記錄下全部改變了數據庫數據的語句,放進master上的binlog中;

從:io線程——在使用start slave 以後,負責從master上拉取 binlog 內容,放進本身的relay log中;

從:sql執行線程——執行relay log中的語句;

複製過程

Binary log:主數據庫的二進制日誌

Relay log:從服務器的中繼日誌

第一步:master在每一個事務更新數據完成以前,將該操做記錄串行地寫入到binlog文件中。

第二步:salve開啓一個I/O Thread,該線程在master打開一個普通鏈接,主要工做是binlog dump process。若是讀取的進度已經跟上了master,就進入睡眠狀態並等待master產生新的事件。I/O線程最終的目的是將這些事件寫入到中繼日誌中。

第三步:SQL Thread會讀取中繼日誌,並順序執行該日誌中的SQL事件,從而與主數據庫中的數據保持一致。

讀寫分離有哪些解決方案?

讀寫分離是依賴於主從複製,而主從複製又是爲讀寫分離服務的。由於主從複製要求slave不能寫只能讀(若是對slave執行寫操做,那麼show slave status將會呈現Slave_SQL_Running=NO,此時你須要按照前面提到的手動同步一下slave)。

方案一:使用mysql-proxy代理

優勢:直接實現讀寫分離和負載均衡,不用修改代碼,master和slave用同樣的賬號,mysql官方不建議實際生產中使用

缺點:下降性能, 不支持事務

方案二:使用AbstractRoutingDataSource+aop+annotation在dao層決定數據源。

若是採用了mybatis, 能夠將讀寫分離放在ORM層,好比mybatis能夠經過mybatis plugin攔截sql語句,全部的insert/update/delete都訪問master庫,全部的select 都訪問salve庫,這樣對於dao層都是透明。 plugin實現時能夠經過註解或者分析語句是讀寫方法來選定主從庫。不過這樣依然有一個問題, 也就是不支持事務, 因此咱們還須要重寫一下DataSourceTransactionManager, 將read-only的事務扔進讀庫, 其他的有讀有寫的扔進寫庫。

方案三:使用AbstractRoutingDataSource+aop+annotation在service層決定數據源,能夠支持事務.

缺點:類內部方法經過this.xx()方式相互調用時,aop不會進行攔截,需進行特殊處理。

備份計劃,mysqldump以及xtranbackup的實現原理

(1)備份計劃

視庫的大小來定,通常來講 100G 內的庫,能夠考慮使用 mysqldump 來作,由於 mysqldump更加輕巧靈活,備份時間選在業務低峯期,能夠天天進行都進行全量備份(mysqldump 備份出來的文件比較小,壓縮以後更小)。

100G 以上的庫,能夠考慮用 xtranbackup 來作,備份速度明顯要比 mysqldump 要快。通常是選擇一週一個全備,其他天天進行增量備份,備份時間爲業務低峯期。

(2)備份恢復時間

物理備份恢復快,邏輯備份恢復慢

這裏跟機器,尤爲是硬盤的速率有關係,如下列舉幾個僅供參考

20G的2分鐘(mysqldump)

80G的30分鐘(mysqldump)

111G的30分鐘(mysqldump)

288G的3小時(xtra)

3T的4小時(xtra)

邏輯導入時間通常是備份時間的5倍以上

(3)備份恢復失敗如何處理

首先在恢復以前就應該作足準備工做,避免恢復的時候出錯。好比說備份以後的有效性檢查、權限檢查、空間檢查等。若是萬一報錯,再根據報錯的提示來進行相應的調整。

(4)mysqldump和xtrabackup實現原理
  • mysqldump

mysqldump 屬於邏輯備份。加入–single-transaction 選項能夠進行一致性備份。後臺進程會先設置 session 的事務隔離級別爲 RR(SET SESSION TRANSACTION ISOLATION LEVELREPEATABLE READ),以後顯式開啓一個事務(START TRANSACTION /!40100 WITH CONSISTENTSNAPSHOT /),這樣就保證了該事務裏讀到的數據都是事務事務時候的快照。以後再把表的數據讀取出來。若是加上–master-data=1 的話,在剛開始的時候還會加一個數據庫的讀鎖(FLUSH TABLES WITH READ LOCK),等開啓事務後,再記錄下數據庫此時 binlog 的位置(showmaster status),立刻解鎖,再讀取表的數據。等全部的數據都已經導完,就能夠結束事務

  • Xtrabackup:

xtrabackup 屬於物理備份,直接拷貝表空間文件,同時不斷掃描產生的 redo 日誌並保存下來。最後完成 innodb 的備份後,會作一個 flush engine logs 的操做(老版本在有 bug,在5.6 上不作此操做會丟數據),確保全部的 redo log 都已經落盤(涉及到事務的兩階段提交

概念,由於 xtrabackup 並不拷貝 binlog,因此必須保證全部的 redo log 都落盤,不然可能會丟最後一組提交事務的數據)。這個時間點就是 innodb 完成備份的時間點,數據文件雖然不是一致性的,可是有這段時間的 redo 就可讓數據文件達到一致性(恢復的時候作的事

情)。而後還須要 flush tables with read lock,把 myisam 等其餘引擎的表給備份出來,備份完後解鎖。這樣就作到了完美的熱備。

數據表損壞的修復方式有哪些?

使用 myisamchk 來修復,具體步驟:

  • 1)修復前將mysql服務中止。
  • 2)打開命令行方式,而後進入到mysql的/bin目錄。
  • 3)執行myisamchk –recover 數據庫所在路徑/*.MYI

使用repair table 或者 OPTIMIZE table命令來修復,REPAIR TABLE table_name 修復表 OPTIMIZE TABLE table_name 優化表 REPAIR TABLE 用於修復被破壞的表。 OPTIMIZE TABLE 用於回收閒置的數據庫空間,當表上的數據行被刪除時,所佔據的磁盤空間並無當即被回收,使用了OPTIMIZE TABLE命令後這些空間將被回收,而且對磁盤上的數據行進行重排(注意:是磁盤上,而非數據庫)

做者:ThinkWon,遵循CC 4.0 BY-SA版權協議
連接:https://blog.csdn.net/ThinkWo...

相關文章
相關標籤/搜索