數據庫面試題整理

數據庫

如下是對面試常見面試題整理,來自知乎大神分享的pdf,引用部分連接已給出,若是有沒有標註的,純屬意外,但願提醒。這篇主要整理出來給本身看的
B/B+樹html

B/B+java

1、B樹:

  1. 定義:B 樹又叫平衡多路查找樹。一棵m階的B 樹 的特性以下:
  • 樹中每一個結點最多含有m個孩子(m>=2);
  • 除根結點和葉子結點外,其它每一個結點至少有[ceil(m / 2)]個孩子(其中ceil(x)是一個取上限的函數);
  • 若根結點不是葉子結點,則至少有2個孩子(特殊狀況:沒有孩子的根結點,即根結點爲葉子結點,整棵樹只有一個根節點);
  • 全部葉子結點都出如今同一層,葉子結點不包含任何關鍵字信息
  • 每一個非終端結點中包含有n個關鍵字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中: 
    • Ki (i=1...n)爲關鍵字,且關鍵字按順序升序排序K(i-1)< Ki
    • Pi爲指向子樹根的接點,且指針P(i-1)指向子樹種全部結點的關鍵字均小於Ki,但都大於K(i-1)。
    • 關鍵字的個數n必須知足: [ceil(m / 2)-1]<= n <= m-1。以下圖所示:

image

B樹中的每一個結點根據實際狀況能夠包含大量的關鍵字信息和分支(固然是不能超過磁盤塊的大小,根據磁盤驅動(disk drives)的不一樣,通常塊的大小在1k~4k左右);這樣樹的深度下降了,這就意味着查找一個元素只要不多結點從外存磁盤中讀入內存,很快訪問到要查找的數據mysql

代碼定義:
imageweb

  1. B樹優點
    高度比平衡樹低,因此IO磁盤操做次數少,查找更快面試

    當B樹包含N個關鍵字時,B樹的最大高度爲l-1(由於計算B樹高度時,葉結點所在層不計算在內),即:l - 1 = log┌m/2┐((N+1)/2 )+1。sql

    2、B+樹

  2. 定義
  • 有n棵子樹的結點中含有n-1 個關鍵字;
  • 全部的葉子結點中包含了所有關鍵字的信息,及指向含有這些關鍵字記錄的指針,且葉子結點自己依關鍵字的大小自小而大的順序連接。 (而B 樹的葉子節點並無包括所有須要查找的信息)
  • 全部的非終端結點能夠當作是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。 (而B 樹的非終節點也包含須要查找的有效信息)

image

  1. B+樹優點
  • B+-tree的磁盤讀寫代價更低:
    B+-tree的內部結點並無指向關鍵字具體信息的指針。所以其內部結點相對B 樹更小。若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。數據庫

    舉個例子,假設磁盤中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體信息指針2bytes。一棵9階B-tree(一個結點最多8個關鍵字)的內部結點須要2個盤快。而B+ 樹內部結點只須要1個盤快。當須要把內部結點讀入內存中的時候,B 樹就比B+ 樹多一次盤塊查找時間(在磁盤中就是盤片旋轉的時間)。設計模式

  • B+-tree的查詢效率更加穩定:
    因爲非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。數組

3、 B*樹

  1. 定義
    B-tree是B+-tree的變體,在B+樹的基礎上(全部的葉子結點中包含了所有關鍵字的信息,及指向含有這些關鍵字記錄的指針),B樹中非根和非葉子結點再增長指向兄弟的指針;B樹定義了非葉子結點關鍵字個數至少爲(2/3)M,即塊的最低使用率爲2/3(代替B+樹的1/2)

image

4、總結

  1. B樹對於全庫掃描具備優點,B樹須要中序遍歷的方法按序掃庫,這樣IO操做次數會比較多。而B+樹直接從葉子結點挨個掃一遍就完了。另外B+樹支持range-query很是方便,而B樹不支持。這是數據庫選用B+樹的最主要緣由。
  2. 而B樹對於成功查詢頗有優點,由於它不用從根節點走到子節點就已經找到數據了。有不少基於頻率的搜索是選用B樹,越頻繁查詢的結點越往根上走,前提是須要對查詢成功率作統計。
  3. mysql 底層存儲是用B+樹實現的。內存中B+樹是沒有優點的,可是一到磁盤,B+樹的威力就出來了。由於它的非葉節點只存儲關鍵字,那麼單位磁盤上能夠存儲的關鍵字就更多,比起B樹,須要的磁盤IO就更少。

在MySQL中,索引屬於存儲引擎級別的概念,不一樣存儲引擎對索引的實現方式是不一樣的,這裏主要討論MyISAM和InnoDB兩個存儲引擎的索引實現方式
索引服務器

1、MyISAM索引實現

MyISAM引擎使用B+Tree做爲索引結構,葉節點的data域存放的是數據記錄的地址。下圖是MyISAM索引的原理圖
image
假設咱們以Col1爲主鍵,則圖8是一個MyISAM表的主索引(Primary key)示意。能夠看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是惟一的,而輔助索引的key能夠重複
image
MyISAM的索引方式也叫作「非彙集」的,之因此這麼稱呼是爲了與InnoDB的彙集索引區分。

2、InnoDB索引實現

雖然InnoDB也使用B+Tree做爲索引結構,但具體實現方式卻與MyISAM大相徑庭。

  1. 第一個重大區別是InnoDB的數據文件自己就是索引文件
    image
    葉節點包含了完整的數據記錄。這種索引叫作彙集索引。由於InnoDB的數據文件自己要按主鍵彙集,因此InnoDB要求表必須有主鍵(MyISAM能夠沒有)
  2. 第二個與MyISAM索引的不一樣是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址
    image
    彙集索引這種實現方式使得按主鍵的搜索十分高效,可是輔助索引搜索須要檢索兩遍索引:首先檢索輔助索引得到主鍵,而後用主鍵到主索引中檢索得到記錄。
  3. 瞭解不一樣存儲引擎的索引實現方式對於正確使用和優化索引都很是有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲何不建議使用過長的字段做爲主鍵,由於全部輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段做爲主鍵在InnoDB中不是個好主意,由於InnoDB數據文件自己是一顆B+Tree,非單調的主鍵會形成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段做爲主鍵則是一個很好的選擇。

    3、索引使用策略及優化

  4. 實例數據庫
    image
  5. 最左前綴原理與相關優化
    索引:<emp_no, title, from_date>
  • 全列匹配
  • 最左前綴匹配。
    當查詢條件精確匹配索引的左邊連續一個或幾個列時,如 或<emp_no, title>,因此能夠被用到,可是隻能用到一部分,即條件所組成的最左前綴。上面的查詢從分析結果看用到了PRIMARY索引,可是key_len爲4,說明只用到了索引的第一列前綴。
  • 查詢條件用到了索引中列的精確匹配,可是中間某個條件未提供。
  • 查詢條件沒有指定索引第一列。
  • 匹配某列的前綴字符串
  • 範圍查詢。範圍列能夠用到索引(必須是最左前綴),可是範圍列後面的列沒法用到索引。同時,索引最多用於一個範圍列,所以若是查詢條件中有兩個範圍列則沒法全用到索引
  • 查詢條件中含有函數或表達式。
    很不幸,若是查詢條件中含有函數或表達式,則MySQL不會爲這列使用索引

索引的使用

3、用索引

  • 表數量比較多而且選擇性高的狀況考慮用索引
  • 索引長度不能過長,能夠考慮某一個列的前綴作索引
  • 在使用InnoDB存儲引擎時,若是沒有特別的須要,請永遠使用一個與業務無關的自增字段做爲主鍵。爲了插入數據的時候可以不開啓新的磁盤頁。
  1. 索引的建立、刪除
  • 索引的類型:
    • UNIQUE(惟一索引):不能夠出現相同的值,能夠有NULL值
    • INDEX(普通索引):容許出現相同的索引內容
    • PROMARY KEY(主鍵索引):不容許出現相同的值
    • fulltext index(全文索引):能夠針對值中的某個單詞,但效率確實不敢恭維
    • 組合索引:實質上是將多個字段建到一個索引裏,列值的組合必須惟一
  • 使用ALTER TABLE語句建立索性
//普通索引
alter table table_name add index index_name (column_list) ;
//惟一索引
alter table table_name add unique (column_list) ;
//主鍵索引
alter table table_name add primary key (column_list) ;
  • 使用CREATE INDEX語句對錶增長索引
//CREATE INDEX可用於對錶增長普通索引或UNIQUE索引,可用於建表時建立索引。

CREATE INDEX index_name ON table_name(username(length)); 
//若是是CHAR,VARCHAR類型,length能夠小於字段實際長度;若是是BLOB和TEXT類型,必須指定 length。

//create只能添加這兩種索引;
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
  • 刪除索引
drop index index_name on table_name ;

alter table table_name drop index index_name ;

alter table table_name drop primary key ;
  1. 注意事項
  • 雖然索引大大提升了查詢速度,同時卻會下降更新表的速度。
  • 創建索引會佔用磁盤空間的索引文件
  • 索引不會包含有NULL的列
  • 使用短索引
  • 索引列排序
  • like語句操做
  • 不要在列上進行運算
  • 不使用NOT IN 、<>、!=操做,但<,<=,=,>,>=,BETWEEN,IN是能夠用到索引的
  • 索引要創建在常常進行select操做的字段上。
  • where的查詢條件裏有不等號(where column != …),mysql將沒法使用索引。

1、事務隔離級別

  1. Mysql隔離級別
  • 未提交讀:事務能夠讀取未提交數據,稱爲髒讀
  • 提交讀:一個事物只能看見其餘已提交事務的修改過的數據
  • 可重複讀:保證同一事務屢次讀取一數據結果是一致的。解決了髒讀,但會致使幻讀。幻讀就是一個事務在讀取某一個範圍數據時,另外一個事務在該範圍內插入數據。以前事務再次讀取範圍內數據,會致使幻讀。可重複讀是Mysql默認隔離級別
  • 可串行化:最高隔離級別,強制事務串行化。避免了幻讀,可是併發很差
  1. 數據庫特性
  • 原子性(Atomicity):事務裏的
    全部操做要麼所有作完,要麼都不作
  • 一致性(Consistency):從一個一致性狀態到另外一個一致性狀
    態。

    例如現有完整性約束 a+b=10,若是一個事務改變了a,那麼必須
    得改變b,使得事務結束後依然知足 a+b=10,不然事務失敗。

  • 隔離性(Isolation):一個事務所作的修改在最終提交之前,對其它事務不可見。

    好比現有有個交易是從A 帳戶轉100 元至 B 帳戶,在這個交易還
    未完成的狀況下,若是此時 B 查詢本身的帳戶,是看不到新增長的
    100元的。

  • (Durability) 持久性
    持久性是指一旦事務提交後,它所作的修改將會永久的保存在數據庫
    上,即便出現宕機也不會丟失。

    2、sql優化

  1. 查詢優化,避免全表掃描
  2. in和not in慎用
  3. 拆分大的update和insert,提升併發性
  4. 避免使用!=,<,>
  5. 避免對null判斷,不然會致使引擎放棄索引而全表掃描
  6. 避免用or
  7. 避免表達式或者函數,不然全表掃描
  8. exists代替in
  9. 避免更新彙集列
  10. 儘可能使用數字字段而不是字符字段

    3、實踐中如何優化sql

  11. sql語句以及索引優化,如上
  12. 數據庫表結構優化
  • 儘可能不要用double
  • 儘可能不用text
  • 儘可能使用timestamp而不是datetime

    4、數據庫範式

  1. 1nf:屬性原子性
  2. 2nf:消除非主屬性對部分嗎依賴
  3. 3nf:消除對碼的傳遞依賴
  4. bcnf:消除子屬性對部分碼依賴
  5. 4nf:消除多值依賴

5、數據庫鏈接池

鏈接池

  1. 早期咱們怎麼進行數據庫操做
  • ①裝載數據庫驅動程序;
  • ②經過jdbc創建數據庫鏈接;
  • ③訪問數據庫,執行sql語句;
  • ④斷開數據庫鏈接。
Public void FindAllUsers(){
              //一、裝載sqlserver驅動對象
              DriverManager.registerDriver(new SQLServerDriver());             
              //二、經過JDBC創建數據庫鏈接
              Connection con =DriverManager.getConnection("jdbc:sqlserver://192.168.2.6:1433;DatabaseName=customer", "sa", "123");            
              //三、建立狀態
              Statement state =con.createStatement();           
              //四、查詢數據庫並返回結果
              ResultSet result =state.executeQuery("select * from users");           
              //五、輸出查詢結果
              while(result.next()){
                     System.out.println(result.getString("email"));
              }            
              //六、斷開數據庫鏈接
              result.close();
              state.close();
              con.close();
        }

程序開發過程當中,存在不少問題:首先,每一次web請求都要創建一次數據庫鏈接。創建鏈接是一個費時的活動,每次都得花費0.05s~1s的時間,並且系統還要分配內存資源。這個時間對於一次或幾回數據庫操做,或許感受不出系統有多大的開銷。但是對於如今的web應用,尤爲是大型電子商務網站,同時有幾百人甚至幾千人在線是很正常的事。在這種狀況下,頻繁的進行數據庫鏈接操做勢必佔用不少的系統資源,網站的響應速度一定降低,嚴重的甚至會形成服務器的崩潰。不是危言聳聽,這就是制約某些電子商務網站發展的技術瓶頸問題。其次,對於每一次數據庫鏈接,使用完後都得斷開。不然,若是程序出現異常而未能關閉,將會致使數據庫系統中的內存泄漏,最終將不得不重啓數據庫。還有,這種開發不能控制被建立的鏈接對象數,系統資源會被毫無顧及的分配出去,如鏈接過多,也可能致使內存泄漏,服務器崩潰。

  1. 技術演進出來的數據庫鏈接池
       咱們本身嘗試開發一個鏈接池,來爲上面的查詢業務提供數據庫鏈接服務:
  • ①   編寫class 實現DataSource 接口
  • ②   在class構造器一次性建立10個鏈接,將鏈接保存LinkedList中
  • ③   實現getConnection  從 LinkedList中返回一個鏈接
  • ④   提供將鏈接放回鏈接池中方法
public class MyDataSource implements DataSource {
              //鏈表 --- 實現棧結構
              privateLinkedList<Connection> dataSources = new LinkedList<Connection>();
 
              //初始化鏈接數量
              publicMyDataSource() {
                     //一次性建立10個鏈接
                     for(int i = 0; i < 10; i++) {
                            try {
                               //一、裝載sqlserver驅動對象
                               DriverManager.registerDriver(new SQLServerDriver());
                               //二、經過JDBC創建數據庫鏈接
                               Connection con =DriverManager.getConnection(
                                  "jdbc:sqlserver://192.168.2.6:1433;DatabaseName=customer", "sa", "123");
                               //三、將鏈接加入鏈接池中
                               dataSources.add(con);
                            } catch (Exception e) {
                               e.printStackTrace();
                            }
                     }
              }
 
              @Override
              publicConnection getConnection() throws SQLException {
                     //取出鏈接池中一個鏈接
                     finalConnection conn = dataSources.removeFirst(); // 刪除第一個鏈接返回
                     returnconn;
              }
 
              //將鏈接放回鏈接池
              publicvoid releaseConnection(Connection conn) {
                     dataSources.add(conn);
                     }
       }
//查詢全部用戶
       Public void FindAllUsers(){
              //一、使用鏈接池創建數據庫鏈接
              MyDataSource dataSource = new MyDataSource();
              Connection conn =dataSource.getConnection();        
              //二、建立狀態
              Statement state =con.createStatement();           
              //三、查詢數據庫並返回結果
              ResultSet result =state.executeQuery("select * from users");           
              //四、輸出查詢結果
              while(result.next()){
                     System.out.println(result.getString("email"));
              }            
              //五、斷開數據庫鏈接
              result.close();
              state.close();
              //六、歸還數據庫鏈接給鏈接池
              dataSource.releaseConnection(conn);
        }
  1. 鏈接池還要考慮更多的問題
  • 併發問題:使用synchronized關鍵字
  • 多數據庫服務器和多用戶:設計一個符合單例模式的鏈接池管理類,在鏈接池管理類的惟一實例被建立時讀取一個資源文件,其中資源文件中存放着多個數據庫的url地址等信息。
  • 事務處理:設置connection的autocommit屬性爲false 而後顯式的調用commit或rollback方法來實現。可採用每個事務獨佔一個鏈接來實現,這種方法能夠大大下降事務管理的複雜性。
  • 鏈接池的分配與釋放
  • 鏈接池的配置與維護

問題

1. 500 萬數字排序,內存只能容納 5 萬個,如何排序,如何優化?
  • 方案一:位圖法
    java代碼
    對這些數進行位圖排序,只須要約5000000/8 = 625000byte,就是0.625M,排序後輸出。可是該方法具備侷限性,須要知道這些數據中的最大值。並且要考慮數據疏密程度,若是最大值1000000,而只有100個元素,那麼效率會變得很是低
  • 方案二:歸併排序法
    多路歸併代碼
    ,多路歸併就是從多個有序數列中歸併。
  1. 將500萬的數據,分紅40個有序文件,分別在內存中排序,而後對這40個有序文件進行歸併排序。
  2. 讀取每一個文件中第一個數(每一個文件的最小數),存放在一個大小爲40的data數組中
  3. 選擇data數組中最小的數min_data,及其相應的文件索引(來自哪一個文件)index
  4. 將min_data寫入到文件result,而後更新數組data(根據index,讀取該文件的下一個數代替min_data)
  5. 判讀是否全部數據都讀取完畢,不然返回到2步。
2. 平時怎麼寫數據庫的模糊查詢

前綴查詢,例如「abc%」,仍是索
引策略的問題

3. 數據庫鏈接池(druid)、線程池做用等等

對於共享資源,有一個很著名的設計模式:資源池(resource pool)。該模式正是爲了解決資源的頻繁分配﹑釋放所形成的問題。爲解決上述問題,能夠採用數據庫鏈接池技術。數據庫鏈接池的基本思想就是爲數據庫鏈接創建一個「緩衝池」。預先在緩衝池中放入必定數量的鏈接,當須要創建數據庫鏈接時,只需從「緩衝池」中取出一個,使用完畢以後再放回去。咱們能夠經過設定鏈接池最大鏈接數來防止系統無盡的與數據庫鏈接。更爲重要的是咱們能夠經過鏈接池的管理機制監視數據庫的鏈接的數量﹑使用狀況,爲系統開發﹑測試及性能調整提供依據。線程池也是如此,由於頻繁開啓關閉線程會下降系統資源,因此能夠用線程池達到資源共享,統一管理線程資源的目的。初始化一個比較大的線程池,每當程序須要開啓新的線程時,會到線程池中申請。一樣的咱們能夠初始化適當的線程數來達到最大的資源利用率

4. 數據庫設計通常設計成第幾範式

我的認爲是第三範式,此時數據冗餘較小,範式級別太高對查詢效率也不利

5. 表(三個字段:姓名,id,分數)要求查出平均分大於 80 的 id 而後分數降序排

序。

select id from table group by id having avg(score) > 80 order by avg(score) desc
6. 一個整形數組中,找出第三大的數

利用快排

/找出第K大的數
int find_k_big(int A[], int low, int high, int k)
{
    if(low < high)
    {
        int pivot_pos = partion2(A, low, high);
        if(pivot_pos + 1 == k)
            return A[pivot_pos];
        else if(pivot_pos + 1 > k)
            find_k_big(A, low, pivot_pos - 1, k);
        else
            find_k_big(A, pivot_pos + 1, high, k);
    }
    else
        return -1;
7. 悲觀鎖實現

悲觀鎖

8. 建表的原則
9. 索引的內涵和用法
相關文章
相關標籤/搜索