Java面試題——數據庫

目錄

 

數據庫的三範式是什麼?

一張自增表裏面總共有 7 條數據,刪除了最後 2 條數據,重啓 mysql 數據庫,又插入了一條數據,此時 id 是幾?

mysql的默認最大連接數?

如何獲取當前數據庫版本? 

說一下 ACID 是什麼?

char 和 varchar 的區別是什麼?

float 和 double 的區別是什麼?

mysql 的內連接、左連接、右連接有什麼區別?

mysql 索引是怎麼實現的?

怎麼驗證 mysql 的索引是否滿足需求?

說一下mysql和oracle的分頁

 說一下數據庫的事務隔離?

說一下 mysql 常用的引擎?

MyISAM 和 INNODB的區別 (主要)

說一下 mysql 的行鎖和表鎖?

說一下樂觀鎖和悲觀鎖?

mysql 問題排查都有哪些手段?

如何做 mysql 的性能優化?

查找慢查詢並定位慢查詢?

 ​ 

數據庫優化之創建合適的索引?

語句優化小技巧

索引使用小技巧

 數據庫優化之分表?

簡單講一下觸發器使用場景

簡單講一下存儲過程使用場景

用JDBC調用存儲過程

說一下你對JDBC的理解

JDBC中的PreparedStatement相比Statement的好處  

數據庫連接池的作用


數據庫的三範式是什麼?

第一範式:強調的是列的原子性,即數據庫表的每一列都是不可分割的原子數據項。
第二範式:要求數據庫表中的每個行必須可以被唯一地區分。爲實現區分通常需要爲表加上一個列,以存儲各個實例的唯一標識。(主鍵)
第三範式:要求一個數據庫表中不包含已在其它表中已包含的非主關鍵字信息。(外鍵)

一張自增表裏面總共有 7 條數據,刪除了最後 2 條數據,重啓 mysql 數據庫,又插入了一條數據,此時 id 是幾?

一般情況下,我們創建的表的類型是InnoDB,如果新增一條記錄(不重啓mysql的情況下),這條記錄的id是8;
但是如果重啓(文中提到的)MySQL的話,這條記錄的ID是6。因爲InnoDB表只把自增主鍵的最大ID記錄到內存
中,所以重啓數據庫或者對錶OPTIMIZE操作,都會使最大ID丟失。
如果我們使用表的類型是MylSAM,那麼這條記錄的ID就是8。因爲MylSAM表會把自增主鍵的最大ID記錄到數據文
件裏面,重啓MYSQL後,自增主鍵的最大ID也不會丟失。

mysql的默認最大連接數?

100

如何獲取當前數據庫版本? 

使用 select version() 獲取當前 MySQL 數據庫版本。

說一下 ACID 是什麼?

Atomicity(原子性):一個事務(transaction)中的所有操作,或者全部完成,或者全部不完成,不會結束
在中間某個環節。事務在執行過程中發生錯誤,會被恢復(Rollback)到事務開始前的狀態,就像這個事務從來
沒有執行過一樣。即,事務不可分割、不可約簡。

Consistency(一致性):在事務開始之前和事務結束以後,數據庫的完整性沒有被破壞。這表示寫入的資料必須
完全符合所有的預設約束、觸發器、級聯回滾等。

Isolation(隔離性):數據庫允許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務
併發執行時由於交叉執行而導致數據的不一致。事務隔離分爲不同級別,包括讀未提交(Read uncommitted)、
讀提交(read committed)、可重複讀(repeatable read)和串行化(Serializable)。

Durability(持久性):事務處理結束後,對數據的修改就是永久的,即便系統故障也不會丟失。

char 和 varchar 的區別是什麼?

char(n) :固定長度類型,比如訂閱 char(10),當你輸入"abc"三個字符的時候,它們佔的空間還是 10 個字節,其他 7 個是空字節。
chat 優點:效率高;缺點:佔用空間;適用場景:存儲密碼的 md5 值,固定長度的,使用 char 非常合適。

varchar(n) :可變長度,存儲的值是每個值佔用的字節再加上一個用來記錄其長度的字節的長度。
所以,從空間上考慮 varcahr 比較合適;從效率上考慮 char 比較合適,二者使用需要權衡

float 和 double 的區別是什麼?

float 最多可以存儲 8 位的十進制數,並在內存中佔 4 字節。
double 最可可以存儲 16 位的十進制數,並在內存中佔 8 字節。

mysql 的內連接、左連接、右連接有什麼區別?

內連接關鍵字:inner join;左連接:left join;右連接:right join。 內連接是把匹配的關聯數據顯示出
來;左連接是左邊的表全部顯示出來,右邊的表顯示出符合條件的數據;右連接正好相反

mysql 索引是怎麼實現的?

索引是滿足某種特定查找算法的數據結構,而這些數據結構會以某種方式指向數據,從而實現高效查找數據。 具
體來說 MySQL 中的索引,不同的數據引擎實現有所不同,但目前主流的數據庫引擎的索引都是 B+ 樹實現的,
B+ 樹的搜索效率,可以到達二分法的性能,找到數據區域之後就找到了完整的數據結構了,所以索引的性能也是更好的

怎麼驗證 mysql 的索引是否滿足需求?

使用 explain 查看 SQL 是如何執行查詢語句的,從而分析你的索引是否滿足需求。
explain 語法:
    explain select * from table where type=1

說一下mysql和oracle的分頁

Mysql是使用關鍵字 limit來進行分頁的  limit offset,size 表示從多少索引去多少位 .
mysql:
      String sql =

      "select * from students order by id limit " + pageSize*(pageNumber-1) + "," + pageSize;

Oracle的分頁,大部分情況下,我們是記不住了。說思路,要使用三層嵌套查詢。
oracle:
 String sql =
  "select * from " +  
      (select *,rownum rid from 
          (select * from students order by postime desc) 
            where rid<=" +pagesize*pagenumber + ") as t" +"where t>" + pageSize*(pageNumber-1);

 說一下數據庫的事務隔離?

MySQL 的事務隔離是在 MySQL. ini 配置文件裏添加的,在文件的最後添加:

transaction-isolation = REPEATABLE-READ

可用的配置值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。

READ-UNCOMMITTED:未提交讀,最低隔離級別、事務未提交前,就可被其他事務讀取(會出現幻讀、髒讀、不可重複讀)。
READ-COMMITTED:提交讀,一個事務提交後才能被其他事務讀取到(會造成幻讀、不可重複讀)。
REPEATABLE-READ:可重複讀,默認級別,保證多次讀取同一個數據時,其值都和事務開始時候的內容是一致,
禁止讀取到別的事務未提交的數據(會造成幻讀)。
SERIALIZABLE:序列化,代價最高最可靠的隔離級別,該隔離級別能防止髒讀、不可重複讀、幻讀。
髒讀 :表示一個事務能夠讀取另一個事務中還未提交的數據。比如,某個事務嘗試插入記錄 A,此時該事務還未
提交,然後另一個事務嘗試讀取到了記錄 A。

不可重複讀 :是指在一個事務內,多次讀同一數據。

幻讀 :指同一個事務內多次查詢返回的結果集不一樣。比如同一個事務 A 第一次查詢時候有 n 條記錄,但是第
二次同等條件下查詢卻有 n+1 條記錄,這就好像產生了幻覺。發生幻讀的原因也是另外一個事務新增或者刪除或
者修改了第一個事務結果集裏面的數據,同一個記錄的數據內容被修改了,所有數據行的記錄就變多或者變少了

說一下 mysql 常用的引擎?

InnoDB 引擎:InnoDB 引擎提供了對數據庫 acid 事務的支持,並且還提供了行級鎖和外鍵的約束,它的設計
的目標就是處理大數據容量的數據庫系統。MySQL 運行的時候,InnoDB 會在內存中建立緩衝池,用於緩衝數據
和索引。但是該引擎是不支持全文搜索,同時啓動也比較的慢,它是不會保存表的行數的,所以當進行 select 
count(*) from table 指令的時候,需要進行掃描全表。由於鎖的粒度小,寫操作是不會鎖定全表的,所以在並
發度較高的場景下使用會提升效率,對事務要求高,保存的數據都是重要數據,我們建議使用 INNODB,比如訂單表,賬號表 .


MyIASM 引擎:MySQL 的默認引擎,但不提供事務的支持,也不支持行級鎖和外鍵。因此當執行插入和更新語句
時,即執行寫操作的時候需要鎖定這個表,所以會導致效率會降低。不過和 InnoDB 不同的是,MyIASM 引擎是
保存了表的行數,於是當進行 select count(*) from table 語句時,可以直接的讀取已經保存的值而不需要
進行掃描全表。所以,如果表的讀操作遠遠多於寫操作時,並且不需要事務的支持的,可以將 MyIASM 作爲數據
庫引擎的首選,如果表對事務要求不高,同時是以查詢和添加爲主的,我們考慮使用 myisam存儲引擎 . 比如 
bbs 中的 發帖表,回覆表 .

Memory 存儲
          我們數據變化頻繁,不需要入庫,同時又頻繁的查詢和修改,我們考慮使用 memory, 速度極快 .
 

MyISAM 和 INNODB的區別 (主要)

1. 事務安全  myisam不支持事務而 innodb支持
2. 查詢和添加速度  myisam不用支持事務就不用考慮同步鎖,查找和添加和添加的速度快
3. 支持全文索引  myisam支持innodb不支持
4. 鎖機制  myisam支持表鎖而 innodb支持行鎖 (事務)
5. 外鍵 MyISAM 不支持外鍵,  INNODB支持外鍵 . (通常不設置外鍵,通常是在程序中保證數據的一致 )

 

說一下 mysql 的行鎖和表鎖?

MyISAM 只支持表鎖,InnoDB 支持表鎖和行鎖,默認爲行鎖

表級鎖:開銷小,加鎖快,不會出現死鎖。鎖定粒度大,發生鎖衝突的概率最高,併發量最低
行級鎖:開銷大,加鎖慢,會出現死鎖。鎖力度小,發生鎖衝突的概率小,併發度最高

說一下樂觀鎖和悲觀鎖?

樂觀鎖:每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在提交更新的時候會判斷一下在此期間別
人有沒有去更新這個數據

悲觀鎖:每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會
阻止,直到這個鎖被釋放

數據庫的樂觀鎖需要自己實現,在表裏面添加一個 version 字段,每次修改成功值加 1,這樣每次修改的時
候先對比一下,自己擁有的 version 和數據庫現在的 version 是否一致,如果不一致就不修改,這樣就實現了樂觀鎖

mysql 問題排查都有哪些手段?

使用 show processlist 命令查看當前所有連接信息
使用 explain 命令查詢 SQL 語句執行計劃
開啓慢查詢日誌,查看慢查詢的 SQL

如何做 mysql 的性能優化?

避免使用 select *,列出需要查詢的字段
選擇正確的存儲引擎
創建索引 :創建合適的索引,我們就可以現在索引中查詢,查詢到以後直接找對應的記錄。 
分表   :當一張表的數據比較多或者一張表的某些字段的值比較多並且很少使用時,採用水平分表和垂直分表來優化 
讀寫分離:當一臺服務器不能滿足需求時,採用讀寫分離的方式進行集羣。 
緩存:使用redis來進行緩存 

查找慢查詢並定位慢查詢?

1、關閉數據庫服務器 (關閉服務 )
2、把慢查詢記錄到日誌中
3、設置慢查詢時間 
4、找出日誌中的慢查詢 SQL
5、使用explain 慢查詢語句,來詳細分析語句的問題.

  

數據庫優化之創建合適的索引?

索引(Index)是幫助DBMS高效獲取數據的數據結構。
分類:普通索引 /唯一索引 /主鍵索引 /全文索引
普通索引 :允許重複的值出現
唯一索引 :除了不能有重複的記錄外,其它和普通索引一樣 (用戶名、用戶身份證、 email,tel)
主鍵索引:是隨着設定主鍵而創建的,也就是把某個列設爲主鍵的時候,數據庫就會給改列創建索引。這就是主鍵
索引 .唯一且沒有 null值
全文索引 :用來對錶中的文本域 (char,varchar,text)進行索引,  全文索引針對 MyIsam
explain select * from articles where match(title,body) against(‘database’);【會使用全文索引】

語句優化小技巧

 

DDL優化:
1 、通過禁用索引來提供導入數據性能  。 這個操作主要針對有數據庫的表,追加數據
//去除鍵
alter table test3 DISABLE keys;
//批量插入數據
insert into test3 select * from test;
//恢復鍵
alter table test3 ENABLE keys;
 
2、 關閉唯一校驗
set unique_checks=0  關閉
set unique_checks=1  開啓
 
3、修改事務提交方式 (導入)(變多次提交爲一次)
set autocommit=0   關閉
//批量插入
set autocommit=1   開啓
DML優化(變多次提交爲一次)           
insert into test values(1,2);
insert into test values(1,3);
insert into test values(1,4);
//合併多條爲一條
insert into test values(1,2),(1,3),(1,4)
DQL優化
Order by優化
                 1 、多用索引排序
普通結果排序(非索引排序) Filesort 
   group by優化
       是使用order by null,取消默認排序
   子查詢優化
在客戶列表找到不在支付列表的客戶
#在客戶列表找到不在“支付列表”的客戶  , 查詢沒買過東西的客戶
explain
select * from customer where customer_id not in (select DISTINCT customer_id from 
payment); #子查詢       -- 這種是基於 func外鏈
 
explain
select * from customer c left join payment p on(c.customer_id=p.customer_id ) where 
p.customer_id is null   -- 這種是基於“索引”外鏈
   Or優化
在兩個獨立索引上使用 or的性能優於  
1、 or兩邊都是用索引字段做判斷,性能好!!
2、 or兩邊,有一邊不用,性能差
3、 如果employee表的name和email這兩列是一個複合索引,但是如果是  :name='A' OR email='B' 這
種方式,不會用到索引!
  limit優化
select film_id,description from film order by title limit 50,5;
 
select a.film_id,a.description from film a inner join (select film_id from film order 
by title limit 50,5)b on a.film_id=b.film_id

索引使用小技巧

索引弊端
1.佔用磁盤空間。
2.對dml(插入、修改、刪除 )操作有影響,變慢。

使用場景:
a: 肯定在 where條件經常使用 ,如果不做查詢就沒有意義
b: 該字段的內容不是唯一的幾個值 (sex)
c: 字段內容不是頻繁變化 .

具體技巧:
1.對於創建的多列索引(複合索引),不是使用的第一部分就不會使用索引。 
alter table dept add index my_ind (dname,loc); // dname 左邊的列 ,loc就是右邊的列
explain select * from dept where dname='aaa'\G 會使用到索引
explain select * from dept where loc='aaa'\G 就不會使用到索引
 
2. 對於使用like的查詢,查詢如果是’%aaa’不會使用到索引而‘aaa%’會使用到索引。
   explain select * from dept where dname like '%aaa'\G不能使用索引
   explain select * from dept where dname like 'aaa%'\G使用索引 .
所以在 like查詢時, ‘關鍵字 ’的最前面不能使用  % 或者 _這樣的字符 .,如果一定要前面有變化的值,
則考慮使用  全文索引 ->sphinx.
 
如果條件中有 or,有條件沒有使用索引,即使其中有條件帶索引也不會使用 。換言之,就是要求使用的所有
字段,都必須單獨使用時能使用索引. 
   
 
如果列類型是字符串,那一定要在條件中將數據使用引號引用起來。否則不使用索引 。 
expain select * from dept where dname=’111’;
expain select * from dept where dname=111;(數值自動轉字符串)
expain select * from dept where dname=qqq;報錯
也就是,如果列是字符串類型,無論是不是字符串數字就一定要用  ‘’ 把它包括起來 .
如果mysql估計使用全表掃描要比使用索引快,則不使用索引。 
   表裏面只有一條記錄

 數據庫優化之分表?

分表分爲水平(按行)分表和垂直(按列)分表
 
根據經驗, Mysql表數據一般達到百萬級別,查詢效率會很低,容易造成表鎖,甚至堆積很多連接,直接掛掉;
 水平分表能夠很大程度較少這些壓力。
按行數據進行分表。
 
如果一張表中某個字段值非常多(長文本、二進制等 ),而且只有在很少的情況下會查詢 。這時候就可以把字段多
個單獨放到一個表,通過外鍵關聯起來。
     考試詳情,一般我們只關注分數,不關注詳情。
水平分表策略:
1.按時間分表
這種分表方式有一定的侷限性 ,當數據有較強的實效性,如微博發送記錄、微信消息記錄等,這種數據很少有用
戶會查詢幾個月前的數據 ,如就可以按月分表。
2.按區間範圍分表
一般在有嚴格的自增id需求上,如按照 user_id水平分表:
table_1  user_id從1~100w
table_2  user_id從101~200w
table_3  user_id從201~300w
3.hash分表*****
通過一個原始目標的ID或者名稱通過一定的hash算法計算出數據存儲表的表名,然後訪問相應的表。

簡單講一下觸發器使用場景

觸發器,需要有觸發條件,當條件滿足以後做什麼操作。
 
觸發器用處還是很多的,比如校內網、開心網、 Facebook,你發一個日誌,自動通知好友,其實就是在增加日誌
時做一個後觸發 ,再向通知表中寫入條目。因爲觸發器效率高。而 UCH沒有用觸發器,效率和數據處理能力都很
低。每插入一個帖子,都希望將版面表中的最後發帖時間 ,帖子總數 字段進行同步更新,用觸發器做效率就很高。
 
create table board1(id int primary key auto_increment,name varchar(50),ar 
ticleCount int);
 
create table article1(id int primary key auto_increment,title varchar(50)
,bid int references board1(id));
 
delimiter |#把分割符 ;改成|
 
create trigger insertArticle_Trigger after insert on article1 for each ro
w begin
    -> update board1 set articleCount=articleCount+1 where id= NEW.bid;
    -> end;
    -> |
 
delimiter ;
 
insert into board1 value (null,'test',0);
 
insert into article1 value(null,'test',1); 

簡單講一下存儲過程使用場景

數據庫存儲過程具有如下優點:
1、存儲過程只在創建時進行編譯,以後每次執行存儲過程都不需再重新編譯,而一般  SQL 語句每執行一次就編
譯一次,因此使用存儲過程可以大大提高數據庫執行速度。
2、通常,複雜的業務邏輯需要多條 SQL 語句。這些語句要分別地從客戶機發送到服務器,當客戶機和服務器之
間的操作很多時,將產生大量的網絡傳輸。如果將這些操作放在一個存儲過程中,那麼客戶機和服務器之間的網絡
傳輸就會大大減少,降低了網絡負載。
3、存儲過程創建一次便可以重複使用,從而可以減少數據庫開發人員的工作量。
4、安全性高 ,存儲過程可以屏蔽對底層數據庫對象的直接訪問,使用  EXECUTE 權限調用存儲過程,無需擁有
訪問底層數據庫對象的顯式權限。
 
正是由於存儲過程的上述優點,目前常用的數據庫都支持存儲過程,例如  IBM DB2,Microsoft SQL 
Server,Oracle,Access 等,開源數據庫系統  MySQL 也在 5.0 的時候實現了對存儲過程的支持。
 
定義存儲過程 :
create procedure insert_Student (_name varchar(50),_age int ,out _id int) 
begin
         insert into student value(null,_name,_age);
         select max(stuId) into _id from student;
end;
 
call insert_Student('wfz',23,@id);
select @id;
 

用JDBC調用存儲過程

賈璉欲執事
加載驅動
獲取連接
設置參數
執行
釋放連接
 
package com.huawei.interview.lym;
 
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;
 
public class JdbcTest {
 
    /**
     * @param args
     */
    public static void main(String[] args) {
       // TODO Auto-generated method stub
       Connection cn = null;
       CallableStatement cstmt = null;     
       try {
           //這裏最好不要這麼幹,因爲驅動名寫死在程序中了
           Class. forName("com.mysql.jdbc.Driver");
           //實際項目中,這裏應用DataSource數據,如果用框架,
           //這個數據源不需要我們編碼創建,我們只需Datasource ds = context.lookup()
           //cn = ds.getConnection();          
           cn = DriverManager.getConnection("jdbc:mysql:///test","root","root");
           cstmt = cn.prepareCall("{call insert_Student(?,?,?)}");
           cstmt.registerOutParameter(3,Types.INTEGER);
           cstmt.setString(1, "wangwu");
           cstmt.setInt(2, 25);
           cstmt.execute();
           //get第幾個,不同的數據庫不一樣,建議不寫
           System.out.println(cstmt.getString(3));
       } catch (Exception e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       finally
       {
 
           /*try{cstmt.close();}catch(Exception e){}
           try{cn.close();}catch(Exception e){}*/
           try {
              if(cstmt != null)
                  cstmt.close();
              if(cn != null)           
                  cn.close();
           } catch (SQLException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
           }
       }
    }

說一下你對JDBC的理解

Java database connection java數據庫連接 .數據庫管理系統 (mysql oracle等)是很多,每個數據庫管理
系統支持的命令是不一樣的。
 
Java只定義接口,讓數據庫廠商自己實現接口,對於我們者而言。只需要導入對應廠商開發的實現即可。然後以接
口方式進行調用 .(mysql + mysql驅動(實現) +jdbc)
 

JDBC中的PreparedStatement相比Statement的好處  

大多數我們都使用PreparedStatement代替Statement
1:PreparedStatement是預編譯的,比 Statement速度快 
2:代碼的可讀性和可維護性
雖然用PreparedStatement來代替Statement會使代碼多出幾行,但這樣的代碼無論從可讀性還是可維護性上來
說.都比直接用 Statement的代碼高很多檔次:
stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values
 ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");
perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values 
(?,?,?,?)");
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate();
不用我多說,對於第一種方法,別說其他人去讀你的代碼,就是你自己過一段時間再去讀,都會覺得傷心。
 
3:安全性
PreparedStatement可以防止 SQL注入攻擊,而 Statement卻不能。比如說:
String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";
如果我們把 [' or '1' = '1]作爲varpasswd傳入進來.用戶名隨意,看看會成爲什麼 ?
select * from tb_name = '隨意' and passwd = '' or '1' = '1';
因爲'1'='1'肯定成立,所以可以任何通過驗證,更有甚者:
把[';drop table tb_name;]作爲varpasswd傳入進來,則:
select * from tb_name = '隨意' and passwd = '';drop table tb_name;有些數據庫是不會讓你成功
的,但也有很多數據庫就可以使這些語句得到執行。
而如果你使用預編譯語句你傳入的任何內容就不會和原來的語句發生任何匹配的關係,只要全使用預編譯語句你就
用不着對傳入的數據做任何過慮。而如果使用普通的 statement,有可能要對 drop等做費盡心機的判斷和過慮。

數據庫連接池的作用

1、限定數據庫的個數,不會導致由於數據庫連接過多導致系統運行緩慢或崩潰
2、數據庫連接不需要每次都去創建或銷燬,節約了資源
3、數據庫連接不需要每次都去創建,響應時間更快。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章