Java程序員面試筆試寶典筆記

第四章 JAVA基礎知識

 

4.3 關鍵字

  • java和c++的區別:
  1. 都是面向對象的語言,都支持封裝,繼承和多態;
  2. java不提供指針來直接訪問內存,程序內存更加安全;
  3. java的類是單繼承的,c++支持多重繼承;雖然java的類不可以多繼承,但是接口可以多繼承;
  4. java有自動內存管理機制,不需要程序員手動釋放無用內存;
  • 構造器constructor是否可被繼承?

      在講繼承的時候我們就知道父類的私有屬性和構造方法不能被繼承,所以constructor也不能被重寫,但是可以被重載,所以可以看到一個類中有多個構造函數的情況。

  • 重寫和重載的區別?

重載發生在同一類中,方法名必須相同,參數類型不同,個數不同,順序不同,方法返回值和訪問修飾符可以不同,發生在編譯時。

重寫發生在父子類中,方法名,參數列表必須相同,返回值範圍小於等於父類,拋出的異常範圍小於等於父類,訪問修飾符範圍大於等於父類,如果父類的方法訪問修飾符爲private則子類就不能重寫該方法。

 

  • 爲什麼重寫equals時必須重寫hashCode方法?

爲什麼要有 hashCode

我們以“HashSet 如何檢查重複”爲例子來說明爲什麼要有 hashCode:

當你把對象加入 HashSet 時,HashSet 會先計算對象的 hashcode 值來判斷對象加入的位置,同時也會與其他已經加入的對象的 hashcode 值作比較,如果沒有相符的hashcode,HashSet會假設對象沒有重複出現。但是如果發現有相同 hashcode 值的對象,這時會調用 equals()方法來檢查 hashcode 相等的對象是否真的相同。如果兩者相同,HashSet 就不會讓其加入操作成功。如果不同的話,就會重新散列到其他位置。(摘自我的Java啓蒙書《Head first java》第二版)。這樣我們就大大減少了 equals 的次數,相應就大大提高了執行速度。

hashCode()與equals()的相關規定

如果兩個對象相等,則hashcode一定也是相同的

兩個對象相等,對兩個對象分別調用equals方法都返回true

兩個對象有相同的hashcode值,它們也不一定是相等的

因此,equals 方法被覆蓋過,則 hashCode 方法也必須被覆蓋

hashCode() 的默認行爲是對堆上的對象產生獨特值。如果沒有重寫 hashCode(),則該 class 的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數據)

 

 

4.3.1 命名規則

       標識符的第一個字符必須是字母,下劃線,$

4.3.3 final,finally,finalize

     final: 被final修飾的變量不可變(引用不可變,即它只能指向初始時指向的那個對象,而不關心對象內容的變化),被final修飾的變量必須初始化

    final方法: 當一個方法修飾爲final,該方法不允許被任何子類重寫,但子類仍然可以用這個方法

    final參數: 這個參數在這個函數內不允許被修改

    final類: 此類不能被繼承,所有方法不能被重寫,但是可以被重載。

    一個類不能既被聲明爲abstract,又被聲明爲final。abstract抽象類不能被實例化,也就是不能被new

 finally: 作爲異常的一部分,它只能用在try/catch中,並且附帶一個語句塊,表示這段語句最終一定被執行,經常用在需要釋放資源的情況下。

 finalize: 在垃圾回收器執行時會調用被回收對象的finalize()方法。

  • 哪些變量不能被繼承?    final關鍵字修飾的類,String,StringBuffer

4.3.5  static(靜態)作用

1.通過static關鍵字達到全局的效果;實現單例模式

2.static的方法是類的方法,不需要創建對象就可以被調用;

3.不能在成員變量內部定義static變量

4.static方法中不能使用this和super關鍵字

5.變量用static final修飾,表示一旦賦值,就不可以修改

4.3.7  volatile

volatile被設計來修飾不同線程訪問和修改的變量。被volatile定義的變量,系統每次用到它時都是直接從對應的內存中提取,而不會利用緩存。

4.4 基本類型與運算

4.4.1 基本數據類型:

 1字節:byte      boolean

 2字節:char       short

 4字節: int         float

 8字節:long       double

封裝類:由於java是面向對象的,而基本類型不具有對象的性質,爲了讓其具有對象的的特徵,將其封裝。在ArrayList,HashMap中用到。

4.4.2 不可變類

   所有基本類型的包裝類都是不可變類,例如Integer,Float。此外,String也是不可變類
4.7Java IO流的實現機制是什麼?

 - 流分爲字節流和字符流,其中字節流繼承於InputStream和OutputStream,字符流繼承於Reader和Writer。流的主要作用主要是爲了改善程序性能並且使用方便
 - 字節流(8bit)和字符流(16bit)區別:字節流在處理輸入輸出時不會用到緩存,而字符流用到了緩存。
 - Java IO類採用了裝飾器模式,好處在於可以在運行時動態地爲對象添加一些額外的指責,很靈活
 - Java IO(非阻塞IO),與傳統的Socket(套接字)數據交換的方式相比,在處理大量併發請求時,使用NIO要比使用Socket效率高

4.5.1  字符串創建

   new String("abc")創建了幾個對象?    一個或者兩個,如果常量池中原來有abc,那麼只創建一個對象;如果常量池中原來沒有字符串abc,那麼創建兩個對象。

4.5.2 ==和equal

==:比較變量對應的內存中存儲的數值是否相同,要比較兩個基本類型的數據或兩個引用變量是否相等,只能用==;對於指向對象類型的變量,如果要比較兩個對象是否指向同一塊存儲空間,用==。

equals:對於對象類型的變量,要比較兩個對象的內容是否相等,用equals

如果一個類沒有自己定義equals()方法,它默認使用從Object類繼承的==,此時equals和==是一樣的效果,如果編寫的類希望比較改類創建的兩個實例對象的內容是否相等,那麼必須覆蓋equals方法。

4.5.6 length

length針對數組,length()針對字符串

4.6異常處理

4.6.1 finally塊的代碼什麼時候被執行?

        當程序進入try語句之前出現異常,會直接結束,不會執行finally塊中的代碼;當程序在try塊中強制退出時也不會去執行finally 塊中的代碼

  1.   finally塊裏的代碼在return之前執行;
  2. 當finally塊中有return語句時,將會覆蓋函數中其他return語句;
  3. 出現在java程序中的finally塊不是一定會被執行:

 4.6.2 異常處理的原理是什麼?

 異常:包括Error(不可恢復)和Exception(可恢復,編譯器可捕獲),它們的父類是Throwable

 - 運行時異常和普通異常區別:
 1.檢查異常:發生在編譯階段,java編譯器強制程序去捕獲此類型的異常
   2.運行時異常:編譯器沒有強制對其進行捕獲並處理

4.7 輸入輸出流

 1.java中有幾種類型的流?

      常見的流有兩種,字節流(8bit)和字符流(16bit)。  字節流繼承於InputStream與OutputStream,字符流繼承於Reader和Writer。流的主要作用是改善程序性能和使用方便。

4.7.3 Socket

套接字:網絡上兩個程序通過一個雙向的通信連接實現數據交換,這個雙向鏈路的一端稱爲一個Socket。

Socket可以分爲兩種類型:面向連接的Socket通信協議(TCP,傳輸控制協議)

                                           面向無連接的Socket協議(UDP,用戶數據報協議)


4.8 Java平臺與內存管理

  •  Java程序----編譯----生成字節碼文件(.class 是一種中間碼)------通過類加載器(ClassLoader和它的子類)將類加載到JVM中------JVM負責將字節碼翻譯爲硬件平臺能執行的代碼
  • 爲什麼說Java是平臺獨立性語言?      平臺獨立性是指可以在一個平臺上編寫和編譯程序,而在其他平臺上運行。保證java具有平臺獨立性的機制爲中間碼和java虛擬機。java程序被編譯後不是生成在硬件平臺上可執行的代碼,而是生成中間碼。不同的硬件平臺會安裝不同的JVM,由JVM來負責把中間碼翻譯成硬件平臺能執行的代碼。
  • 類加載過程:當運行程序時,JVM會將編譯生成的.class文件按照一定需求和一定的規則加載到內存中,並組織成爲一個完整的Java應用程序。由類加載器來完成,類加載器本身也是一個類,其實質是把類文件從硬盤讀取到內存中。
  • 在Java語言中,類的加載是動態的,它並不會一次性將所有類全部加載後再運行,而是保證程序運行的基礎類完全被加載到JVM中,至於其他類,則在需要時才加載。
  •  3種類加載器:負責加載系統類(Bootstrap Loader),負責加載擴展類(ExtClassLoader ),負責加載應用類(AppClassLoader).這三個類是如何讓協調工作來完成類的加載的?它們通過委託的方式實現。當有類需要被加載時,類加載器會請求父類來完成這個載入工作,父類會使用其自己的搜索路徑來搜索需要被載入的類,如果搜索不到,纔會由子類按照其搜索路徑來搜索待加載的類。
  • 類加載的主要步驟:

            1.裝載。根據查找路徑找到對應的.class文件,然後導入。
            2.鏈接。鏈接又分爲3個小步驟:
                (1)檢查。檢查待加載的class文件的正確性。
                (2)準備。給類中的靜態變量分配存儲空間。
                (3)解析。將符號引用轉換成直接引用(這一步是可選的)。
           3.初始化。對靜態變量和靜態代碼塊執行初始化工作。        

  •     對於垃圾回收器而言,使用有向圖來記錄和管理堆內存中的所有對象,通過這個有向圖就可以識別哪些對象是可達的,哪些是不可達的,所有不可達對象都是課被回收的。
  • 垃圾回收算法:
  1. 引用計數法: 堆中每個對象都有一個引用計數器;當對象被引用時,計數器加1,當引用被置爲空或離開作用域時,引用計數減1,由於這種方法無法解決相互引用的問題,因此JVM沒有采用這種算法。
  2. 追蹤回收算法:利用JVM維護的對象引用圖,從根節點開始遍歷對象的應用圖,同時標記遍歷到的對象。當遍歷結束後,未被標記的對象就是目前不使用的,可以被回收。
  3. 標記整理算法:把堆中活動的對象移動到堆的一端,這樣就會在堆中另外一端留出很大的空閒區域,相當於對堆中的碎片進行了集中處理。
  4. 複製回收算法:把堆分成兩個大小相同的區域,在任何時刻,只有其中的一個區域被使用,直到這個區域被消耗完爲止,此時垃圾回收器會中斷程序的執行,通過遍歷的方式把所有活動的對象複製到另一個區域中,在複製的過程中它們是緊挨着的,從而可以消除內存碎片。當複製過程結束後程序會接着執行,直到這塊區域被使用完,然後再使用上面的方法繼續進行垃圾回收。 
  5. 分代回收算法:把堆分成兩個或者多個子堆,每一個子堆被視爲一代。算法在運行的過程中優先收集那些年幼的對象,如果一個對象經過多次收集任然存活,那麼就可以把這個對象轉移到高一級的堆裏,減少對其的掃描次數。     
  •     內存泄漏: 是指一個不再被程序使用的對象或者變量還在內存中佔有存儲空間。
  •    內存泄漏主要有兩種情況:

             1.在堆中申請的空間沒有被釋放;
             2.對象已不再被使用,但還仍然在內存中保留着。
               垃圾回收機制的引入可以有效地解決第一種情況;而對於第二種情況,垃圾回收機制則無法保證不再使用的對象會被釋放。

  •  堆和棧的區別:

         1.棧內存主要用來存放基本數據類型和引用變量。每當有函數調用時,都會通過壓棧方式    創建新的棧幀,每當函數調用結束後都會通過彈棧的方式釋放棧。
       2.堆內存是用來存放運行時創建的對象。一般來講,通過new關鍵字創建出來的對象都存放在堆內存中。

  •   java中引用的用法:

   在堆中產生一個數組或對象後,可以在棧中定義一個特殊的變量,讓棧中這個變量的取值等於數組或對象在堆內存中的首地址,棧中的這個變量就成了數組或對象的引用變量。引用變量就相當於爲數組或對象起的一個名稱,以後就可以在程序中使用棧中的引用變量來訪問堆中的數組或對象。   P132


4.9  容器

  •  Collections: 是整個集合框架的基礎,它裏面存儲一組對象,表示不同類型的Collections,它的作用只是提供維護一組對象的基本接口而已。

 

  •   ArrayList,Vector,LinkedList有什麼區別:

        1.均爲可伸縮數組,即可以動態改變長度的數組。
        2.ArrayList和Vector都是基於存儲元素的Object[] array來實現的,它們會在內存中開闢一塊連續的空間來存儲,由於數據存儲是連續的,因此,它們支持下標來訪問元素,索引速度塊,但在插入元素時須移動容器中元素,插入慢。擴容時,Vector默認擴充爲原來2倍,ArrayList擴充爲原來1.5倍。  這兩者最大的區別是synchronization的使用,ArrayList不同步,而Vector的絕大多數方法是同步的,所以Vector線程安全ArrayList不是線程安全。正是由於Vector提供線程安全機制,其性能遜色於ArrayList。
       3 . LinkedList採用雙向鏈表實現,對數據的索引需要從列表頭開始遍歷,因此隨機訪問效率低但是插入元素時不需要對數據進行移動,因此插入效率高

    在實際使用時,對數據主要操作爲索引或者在集合末端進行增加,刪除時,使用ArrayList或Vector效率高;當對數據的操作主要爲爲制定位置插入和刪除時,使用LinkedList;當在多線程中使用容器時,選用Vector安全。

 

  • HashMap和Hashtable區別:

       1.HashMap是Hashtable的輕量級實現,都完成了Map接口。HashMap允許一條空(null)鍵值(key),而HashTable不允許。

       2.Hashtable繼承自Dictionary類,而HashMap是java1.2引進的Map interface的一個實現。

       3.Hashtable線程安全,而HashMap不支持線程的同步,所以是非線程安全的。在多個線程訪問Hashtable時,不需要開發人員對它進行同步,而對於HashMap,開發人員必須提供額外的同步機制。

  •  HashMap裏面存入的鍵值對在取出時沒有固定的順序,一般而言,在Map中插入,刪除和定位元素,HashMap是最好的選擇。而TreeMap實現了SortMap接口,能夠把它保存的記錄根據鍵排序,因此取出來的是排序後的鍵值對。如果需要按自然順序或自定義順序遍歷鍵,那麼TreeMap更好。LinkedHashMap是HashMap的一個子類,如果需要輸出的順序和輸入的相同,那麼用LinkedHashMap。WeakHashMap和HashMap類似,不同在於WeakHashMap中key採用的是弱引用。

 

  • 使用自定義類作爲HashMap的key時,注意:

      1.如果向根據對象的相關屬性來自定義對象是否相等的邏輯,此時需要重寫equals()方法,一旦重寫了equals(),那麼就必須重寫hashCode()方法。

      2.當自定義類的多項作爲HashMap(Hashtable)的key時,最好把這個類設計爲不可變類。

      3.從HashMap的工作原理可以看出,如果兩個對象相等,那麼這兩個對象有相同的hashCode,反之則不成立。

 

  • Collection和Collections區別:

     1.  Collection:集合接口。   提供了對集合對象進行基本操作的通用接口方法。   實現該接口的類有List和Set,  該接口的設計目標是爲各種具體的集合提供最大化的統一的操作方式

     2.Colleactions: 針對集合類的包裝類, 提供了一系列靜態方法以實現對各種集合的搜索,排序,線性安全化等操作。  Coolections類不能實例化,如同一個工具類,服務於Collection框架。

 

4.10    多線程

  • 線程和進程:

      線程:指程序在執行過程中,能夠執行程序代碼的一個執行單元

      進程:指一段正在執行的程序

    線程又被稱爲輕量級進程,它是程序執行的最小單元,一個進程可以擁有多個線程,各個線程之間共享程序的內存空間(代碼段,數據段,堆空間)及一些進程的資源,但是各個線程擁有自己的棧空間。

 

  • 實現多線程的方式:

    1. 繼承Thread類,重寫run()方法:   Thread本質上也是實現了Runnable接口的一個實例,它代表一個線程的實例,並且啓動線程的唯一方法是通過Thread類的start()方法。

 

class MyThread extends Thread{   //繼承Thread類
    public void run(){
      ...         //重寫run()方法
    }
}
public class Test{
   public static void main(String[] args){
       Mythread thread = new Mythread();
       thread.start(); //開啓線程
   }
}
    

    2. 實現Runnable接口,並實現該接口的run()方法:    自定義類並實現Runnable接口,實現run()方法   ----->      創建Thread對象,用實現Runnable接口的對象作爲參數實例化該Thread對象   -------->     調用Thread的start()方法。

class MyThread implements Runnable{ //實現Runnable接口
   public void run(){
      ...    //實現run()方法
   }
}
public class Test{
   public static void main(String[] args){
      MyThread thread = new MyThread();
      Thread t = new Thread(thread);
      t.start(); //開啓線程
   }
}
   

    3. 實現Callable接口,重寫call()方法

public class CallableAndFuture{
   public static class CallableTest implement Callable<String>{ //實現Callable接口
       public String call() throws Exception{
          ...  //重寫call()方法
       }
   }
   public static void main(String[] args){
       ExecutorService threadPool = Executors.newSingleThreadExecutor(); 
       Future<String> future = threadPool.submit(new CallableTest());//啓動線程
       try{
           ...
           System.out.print(future.get()); //等待線程結束,並獲取返回結果
       }catch(Exeception e){
           e.printStackTrace();
       }
   }
}

 

  •  run()和start()區別:

       start():啓動一個線程,此時該線程屬於就緒狀態,而非運行狀態,就意味着整個線程可以被JVM來調度。

       run():在調度過程中,JVM通過調用線程類的run()方法來完成實際的操作,當run()方法結束後,此線程就會終止。

 

  • 多線程同步的實現方法有哪些?

      1. synchronized()關鍵字:    該鎖表明對象在任何時候只允許被一個線程擁有,當一個線程調用對象的一段synchronized代碼時,需要先獲得這個鎖,然後去執行響應的代碼,執行結束後,釋放鎖。

      2. wait()方法與notify()方法:   在synhronized代碼被執行期間,線程可以調用對象的wait()方法,釋放對象鎖,進入等待狀態,並且可以調用notify()或者notifyAll()通知正在等待的其他線程。

      3. Lock:    

 

  • sleep()方法和wait()方法區別:

       1. 原理不同:sleep()是Thread類的靜態方法,它使此線程暫時執行一段時間,而把執行機會讓給其他線程,等計時時間一到,此線程會自動甦醒。  wait()使Object類的方法,用於線程間的通信,這個方法使當前擁有該對象鎖的進程等待,直到其他線程調用notify()方法。

      2. 對鎖的處理機制不同:   sleep()方法不會釋放鎖;    wait()線程會釋放掉它所佔用的鎖,從而使線程所在對象中的其他synchronized數據可被別的線程調用。

      3. 使用區域不同:   wait()必須放在同步控制方法或同步語句塊中使用,sleep()可在任何地方使用。

  • 終止線程的方法有哪些?

      stop()和suspend()方法,用Thread.stop()來終止線程時,它會釋放已經鎖定的所有監視資源。suspend()方法容易造成死鎖。所以一般採用的方法是讓線程自行結束進入Dead狀態。

  • synchronized和lock區別:

       1.用法:  synchronized既可以加在方法上,也可以加在特定代碼塊中;  而Lock需要顯式地指定起始位置和終止位置。

       2. 性能:  在資源不是很激烈的情況下,synchronized性能優於ReetrantLock, 在資源競爭很激烈的情況下,synchronized性能下降很快,而ReetrantLock性能保持不變。

       3.機制:   synchronized,獲得多個鎖時,必須以相反順序釋放,自動解鎖,不會引文出現異常引發死鎖;   Lock,手動解鎖,必須在finally塊中釋放,否則引起死鎖問題。

  • join方法:

     join()方法的作用是讓調用該方法的線程在執行完run()方法後,再執行join方法後面的代碼。簡單來說,就是將兩個線程合併,實現同步功能。

 

 

第五章 JAVA WEB

 

5.1  Servlet與JSP

  • 客戶端:  用戶和瀏覽器
  • 服務端: 服務器

 

  • 瀏覽器作用: 

          1. 完成與服務器端的交互

          2. 完成HTML的解析,從而實現把用戶需要查看的資源信息以直觀的形式展示出來

  • 服務器端作用:   用來接收客戶端發來的請求,並對該請求進行處理,找到客戶端請求的資源,最後把請求的資源返回給客戶端

 

  • 最基本的頁面訪問處理流程:

           1. 用戶通過瀏覽器輸入鏈接地址來請求所需的資源

           2. 瀏覽器接受用戶的請求,並把該請求組裝成指定的格式發送給服務器端,客戶端與服務器端通過HTTP來完成具體的交互。請求的數據流中包括:HTTP請求方法,請求的網址URL, 參數

           3. 服務器收到客戶端發來的請求,並查找用戶所需要的資源

           4. 服務器找到用戶請求的資源後,把該資源返回給客戶端

           5.服務器通過把響應消息組裝成特定的消息格式後返回給客戶端,這個過程通過HTTP來完成。響應的數據流主要包括:狀態編碼,Comment-type,響應消息的內容

  • Servlet:    生成動態頁面

 

  • cookie:    在HTTP下,服務器或腳本可以i維護客戶工作站上信息的一種方法。它是由Web服務器保存在用戶瀏覽器上的小文件,可以包含有關用戶的信息
  • session:  用來在客戶端與服務器端之間保持狀態的解決方案以及存儲結構
  • cookie和session的區別:

       1. cookie採用的是在客戶端保持狀態的方案, 數據存放在客戶的瀏覽器上;  session機制採用的是在服務器端保持狀態的方案,數據放在服務器上。

        2.cookie安全性不夠     由於cookie信息存放在客戶端,其他人可以很容易地得到存放在本地的cookie,並進行cookie欺騙;而session信息存放在服務器端,比較安全。

        3.cookie性能更高     由於session會在一定時間內保存在服務器上,因此當訪問量增多時,會降低服務器的性能。

        4.單個cookie保存的數據不能超過4KB,很多瀏覽器都限制一個站點最多保存20個cookie;而session不存在此問題。

 

  • SpringMVC工作原理了解麼?
  1. 客戶端(瀏覽器)發送請求,直接請求到DispatcherServelet(前端控制器)
  2. DispatcherServelet根據請求信息調用HandlerMapping(處理映射器),解析請求對應的Handler
  3. 解析到的對應的Handler(也就是我們平常說的Controller控制器)後,開始由HandlerAdapter(處理適配器)處理
  4. HandlerAdapter會根據Handler來調用真正的處理器處理請求,並處理響應的業務邏輯
  5. 處理器處理完業務之後,會返回一個ModelAndView對象,Model時返回的數據對象,View是邏輯上的View
  6. ViewResolver會根據邏輯View查找實際的View
  7. DispaterServlet(前端控制器)把返回的Model傳給View(視圖渲染)
  8. 把View返回給請求者(瀏覽器)

 

 

 

 

第六章 數據庫原理

 

  • SQL操作  
select 選擇符合條件的記錄 select* from table where 條件語句
insert 插入一條記錄 insert into table (字段)values (值)
update 更新 update table set 字段名=字段值 where 條件表達式
delete 刪除記錄 delete from table where 條件表達式
create 數據表的建立 create table tablename 字段
drop 數據表的刪除 drop table tablename
grant 爲用戶授予系統權限  
revoke 收回系統權限

 

EXPLAIN 用於描述表的細節
CONCAT Mysql 數據庫可以使用CONCAT或者CONCAT_WS兩種函數進行拼接
insert ignore into 表名 values(...) 批量插入數據,如果數據已經存在,請忽略

 

create unique index 索引名 on 表名(字段) 建立唯一索引
force index

MySQL中,使用 FORCE INDEX 語句進行強制索引查詢

alter 表名 ADD 字段 向已存在的表插入新字段
create trigger  

1、用 CREATE TRIGGER 語句構造觸發器,用 BEFORE或AFTER 來指定在執行後面的SQL語句之前或之後來觸發TRIGGER

2、觸發器執行的內容寫出 BEGIN與END 之間

3、可以使用 NEW與OLD 關鍵字訪問觸發後或觸發前的employees_test表單記錄

replace replace(字段,“需要替換的值”,“替換後的值”)
修改表名 alter table 表名 rename 新表名
添加外鍵 alter  table 表名  add  foreign  key .. .references. ..
求交集 intersect
substr(X,Y) 其中X是要截取的字符串Y是字符串的起始位置(注意第一個字符的位置爲1,而不爲0),取值範圍是±(1~length(X)),當Y等於length(X)時,則截取最後一個字符;當Y等於負整數-n時,則從倒數第n個字符處截取。
group_concat(要連接的字段 SEPARATOR 連接時使用的符號) 集合函數,起連接作用,此函數必須與group by配合使用
limit m,n 從第m+1條開始取n條
   
   
   

 

  • SQLite數據庫中一個特殊的名叫 SQLITE_MASTER 上執行一個SELECT查詢以獲得所有表的索引。每一個 SQLite 數據庫都有一個叫 SQLITE_MASTER 的表, 它定義數據庫的模式。 SQLITE_MASTER 表看起來如下:

    CREATE TABLE sqlite_master ( 
    type TEXT, 
    name TEXT, 
    tbl_name TEXT, 
    rootpage INTEGER, 
    sql TEXT 
    ); 
    對於表來說,type 字段永遠是 ‘table’,name 字段永遠是表的名字。所以,要獲得數據庫中所有表的列表, 使用下列SELECT語句:

    SELECT name FROM sqlite_master 
    WHERE type=’table’ 
    ORDER BY name; 
    對於索引,type 等於 ‘index’, name 則是索引的名字,tbl_name 是該索引所屬的表的名字。

 

  •  inner join : 兩邊表同時有對應的數據,即任意一邊缺失數據就不顯示

     left join:會讀取左邊數據表的全部數據,即便右邊表無對應數據

     right join:會讀取右邊數據表的全部數據,即便左邊表無對應數據

     group by 語句用來與聚合函數聯合使用,having語句的存在彌補了where關鍵字不能與聚合函數聯合使用的不足

  • select ... 
    from ...as... inner join ...as...
    on ...
    

     

    not in 不在什麼裏面
    MAX() as ... 選最大
    distinct 不重複
    格式: 

     

  • delete和truncate區別:

          1.用delete操作後,被刪除的數據佔用的內存空間還在,可以恢復,delete執行的過程是每次從表中刪除一行數據,同時將地喊出的操作以日誌的形式保存,一遍將來進行回滾操作。        truncate操作刪除數據後,被刪除的數據會立即釋放佔用的存儲空間,一旦執行不能被回滾,被刪除的數據不能恢復

          2.truncate速度快於delete

 

  • 內鏈接和外連接區別:

       內鏈接只顯示符合連接條件的記錄,外連接除了顯示符合連接條件的記錄外,還顯示錶中的記錄。外連接有左外連接,右外連接,全連接

  • 事務: 是數據庫中一個單獨的執行單元,它通常由高級數據庫操作語言或編程語言編寫的用戶程序的執行所引起。當在數據庫中更改數據成功時,在事務中更改的數據便會提交,不再改變。否則,事務就取消或者回滾,更改無效。
  • 事務的屬性
  1. 原子性: 事務是一個不可分割的整體,當數據修改時,要麼全執行,要麼不執行
  2. 一致性:一個事務執行之前或者執行之後,數據庫數據必須保持一致性狀態。 例如銀行轉賬前後兩個賬戶金額之和應保持不變
  3. 隔離性: 當兩個或多個事務併發執行時,爲了保證數據的安全性,將一個事務內部的操作與事務的操作隔離起來,不被其他正在進行的事務看到。實現隔離性是解決臨時更新與消除級聯回滾問題的一種方式。
  4. 持久性: 事務完成以後,增刪查改保證它對數據庫中的數據的修改是永久性的,當系統或介質發生故障時,該修改也永久保存。持久性一般通過數據庫備份和恢復來保證。
  • 範式:
  1. 第一範式 1NF: 無重複的列
  2. 第二範式 2NF:   要求數據庫表中的每個實例或行必須可以被唯一區分,即如果關係模式R爲第一範式,並且R中的每一個非主屬性完全函數依賴於R的某個候選鍵。
  3. 第三範式 3NF: 如果關係模式R是第二範式,且每個非主屬性都不傳遞依賴於R的候選鍵。
  4. BCNF: 建立在第三範式上,關係模式是1NF,且每個屬性都不傳遞依賴於R的候選鍵。
  5. 4NF: 只有一個1:多
  • union和union all區別?

      union在進行表求並集後會去掉重複的元素,回對所產生的結果集進行排序運算; union all只是簡單的將兩個結果集合合併後舊返回結果。

 

第7章  設計模式

  • 單例模式: 保證在整個應用程序的生命週期中,任何一個時刻,單例類的實例都只存在一個。

  • 工廠模式:  工廠模式專門負責實例化大量公共接口的類。 工廠模式可以動態地決定將哪一個類實例化,而不必事先知道每次要實例化哪一個類。工廠類負責創建抽象產品的具體子類的實例。
  1.  簡單工廠模式: 簡單工廠模式的工廠類是根據提供給它的參數,返回的是幾個可能產品中的一個類的實例。
  2. 工廠方法模式:  工廠方法模式是類的創建模式,其用意是定義一個用於創建產品對象的工廠的接口而將實際創建工作推遲到工廠接口的子類中。
  3. 抽象工廠模式: 抽象工廠模式可以向用戶端提供一個接口,使用戶端在不必指定產品的具體情況下,創建多個產品族中的產品對象。
  • 適配器模式: 把一個類的接口轉換成客戶端所期望的另一個接口,從而使原本因接口不匹配而無法一起工作的兩個類能夠一起工作。
  • 觀察者模式:一個對象通過添加一個方法(該方法允許觀察者註冊自己)使本身變得可觀察。當可觀察的對象更改時,它會將信息發送到已註冊的觀察者。

數據庫:

  •   數據庫執行順序

      

  • 不建議用查詢緩存:因爲在一個表上有更新的時候,跟這個表有關的查詢緩存就會失效,所以這條語句就會把表上所有緩存結果都清空。
  • 日誌模塊:  redo log(重做日誌)---InnoDB    binlog(歸檔日誌)----Server
  1. redo log: 先寫日誌,再寫磁盤;  有了redo log,InnoDB就可以保存即使數據庫發生異常重啓,之前提交的記錄都不會丟失,這個能力稱爲crash-safe。 redo log中記錄數據頁的更新細節,支持崩潰恢復。循環寫,這樣歷史日誌沒法保留。
  2. bin log:支持歸檔
  3. 不同點:   redo log是InnoDB引擎特有的;binlog是MySQL的Server層實現的,所有引擎都可以使用。   

                          redo log是物理日誌,記錄的是在某個數據頁上做了什麼修改;binlog是邏輯日誌,記錄的是這個語句的原始邏輯,比如給ID=2這一行的c字段加1

                          redo log循環寫,空間固定會用完;binlog可追加寫,不會覆蓋以前的日誌

 

 

  • 簡單來說,事務就是要保證一組數據庫操作,要麼全部成功,要麼全部失敗; 事務可以保證中間結果不被別的事務讀到,因此修改計數值和插入新紀錄的順序是不影響邏輯結果的
  • 經過索引優化,避免回表過程?由於覆蓋索引可以減少樹的搜索次數,顯著提升查詢性能,所以使用覆蓋索引是一個常用的性能優化手段。
  •  根據加鎖的範圍,Mysql裏面的鎖大致可以分爲 全局鎖,表級鎖,行鎖
  1. 全局鎖:加鎖方法 Flush tables with read lock(整個庫處於只讀狀態);典型使用場景--做全局邏輯備份;
  2. 行鎖:在InnoDB事務中,行鎖是在需要時候加上的,但並不是不需要了就立即釋放,而是要等到事務結束後才釋放。這就是兩階段鎖協議。     如果你的事務中需要鎖多個行,要把最可能造成鎖衝突,最可能影響併發度的鎖儘量往後放。

大多數情況下優化器都能找到正確的索引,但偶爾還是會碰到原本可以執行的很快的SQL語句,執行速度卻比預期的慢很多

  • 索引選擇異常和處理:
  1. 採用force index強行選擇一個索引。 Mysql會根據詞法解析的結果分析出可能使用的索引作爲候選項,然後再候選列表中依次判斷每個索引需要掃描多少行。如果force index指定的索引在候選索引列表中,就直接選擇這個索引,不再評估其他索引的執行代價。
  2. 考慮修改語句,引導Mysql使用我們期望的索引。
  3. 在有些場景下,我們可以新建一個更適合的索引,來提供給優化器做選擇,或刪掉無用的索引。
  • 如果某次寫入使用了change buffer機制,之後主機異常重啓,是否會丟失change buffer和數據?    

不會丟失。雖然只更新內存,但是事務提交的時候,我們把change buffer的操作也記錄到了redo log裏了,所以崩潰恢復的時候,change buffer也能找回來。

  • 字符串創建索引的方式:
  1. 直接創建完整做因,這樣可能比較佔用空間
  2. 創建前綴索引,節省空間,但會增加查詢掃描次數,並且不能使用覆蓋索引
  3. 倒序存儲,再創建前綴索引,可以繞過字符串本身前綴的區分度不夠的問題
  4. 創建hash字段索引,查詢性能穩定,有額外的存儲和計算消耗,和第三種方式一樣,都不支持範圍掃描。
  5. 當內存數據頁跟磁盤數據頁內容不一致的時候,我們稱這個內存頁爲“髒頁”,內存數據寫入到磁盤後,內存和磁盤上的數據頁內容就一致了,稱爲乾淨頁。無論是髒頁還是乾淨頁,都在內存中。
  6. InnoDB的刷盤速度考慮兩個因素: 髒頁比例,redo log寫盤速度
  7. 收縮表空間的方法:

要收縮一個表,只是delete掉表裏面不用的數據的話,表文件的大小是不會變的,你還要通過alter table命令來重建表,才能達到表文件變小的目的。

重建表的兩種實現方式:Online DDL的方式是可以考慮再業務低峯期使用的,而Mysql5.5及以前的版本,這個命令是會阻塞DML的。

  • 如果一個高配的機器,redo log設置太小,會發生什麼?

        每次事務都要提交redo log,如果設置太小,很快會被寫滿,這時候系統不得不停止所有更新,去推進checkpoint。磁盤壓力很小,但是數據庫出現間歇性的性能下跌。

  • 如果現在有一個頁面經常要顯示交易系統的操作記錄總數,那隻能自己計數。自己計數的方法和優缺點:
  1. 用緩存系統保存計數: 

    可以用一個redis服務來保存這個表的總行數。表每插入一行redis計數加1,每被刪除一行redis計數減1;這種方式下,讀和更新的操作很快,但是緩存系統可能會丟失更新,即使redis正常工作,這個值還是邏輯上不精確的,因爲在併發系統裏,我們無法精確控制不同線程的執行時刻。

    2.  在數據庫保存計數:(把計數直接放到數據庫裏單獨一張計數表)

     解決了崩潰丟失的問題,InnoDB是支持崩潰恢復不丟數據的;解決了一致性視圖的問題。用事務來確保計數準確,由於事務可以保證中間結果不被別的事務讀到,因此修改數值和插入新紀錄的順序是不影響邏輯結果的。

  • 什麼時候使用alter table t engine = InnoDB會讓一個表佔用的空間反而變大?

     在DDL期間,如果剛好有外部的DML在執行,這期間可能會引起一些新的空洞。將表t重建依次,插入一部分數據,但插入的這些數據用掉了一部分預留空間(再重建表的時候,InnoDB不會把整張表佔滿,每個頁留了1/16給後續的更新用),這種情況下,再重建一次表t,就可能出現問題中的現象。

  • 18. 爲什麼這些SQL語句邏輯相同,性能卻差異巨大?

        對於索引字段做函數操作,可能會破壞索引值的有序性,因此優化器就決定放棄走樹搜索功能。在這個例子中,優化器放棄了樹搜索功能,優化器可以選擇遍歷主鍵索引和選擇遍歷索引t_modified,優化器對比索引大小後發現,索引t_modified更小,遍歷這個索引比遍歷主鍵索引來的快,因此最終選擇索引t_modified。

       例子中,由於在t_modified字段加了month()函數操作,導致了全索引掃描。爲了能夠用上索引的快速定位能力,我們需要把sql語句改成基於字段本身的範圍查詢。

 

 

在mysql中,字符串和數字做比較的話,是將字符串轉換成數字。

 

 

 

結果是:優化器會現在交易記錄表tradelog上用主鍵索引查到id=2的行,但是沒有用上交易詳情表trade_detail上的tradeid索引,而是用主鍵id進行了全表掃描。

原因:兩個表的字符集不同,一個是utf8,一個是utf8m64,所以做表連接查詢的時候用不上關聯字段的索引。

字符集utf8m64是utf8的超集,所以當這兩個類型的字符串做比較的時候,mysql內部的操作是,先把utf8轉換爲utf8m64字符集,在做比較。-----> 字符集不同只是條件之一,連接過程中要求在被驅動表的索引字段上加函數操作,是直接導致對被驅動表做全表掃描的原因

小結:對索引字段做函數操作,可能會破壞索引值的有序性,因此優化器就決定放棄走樹搜索功能

  • 爲什麼我只查一行的語句,頁執行這麼慢?

第一類---------查詢長時間不返回     :這種情況大概率是表被鎖住了,用show processlist命令分析原因。

1.等MDL鎖----命令查看顯示爲Waiting for table metadata lock  :出現這個狀態表示的是,現在有一個線程正在表上請求或者持有MDL寫鎖,把select語句堵住了。這類問題的處理方式,就是找到誰持有MDL寫鎖,然後把它kill掉。

2.等flush---waiting for table flush  :這個情況可能是有一個flush tables命令被別的語句堵住了,然後它又堵住了我們的select語句

3.等行鎖---

第二類---查詢慢:

1.由於字段上沒有索引,因此查詢語句只能走id主鍵順序掃描

2.帶lock in share mode的sql語句,是當前讀,因此會直接讀到最新的結果,所以速度很快;但是沒有加事務的語句,是一致性讀,需要依次執行undo log,所以慢。

  •  20.幻讀:

幻讀是指一個事務在前後兩次查詢同一個範圍的時候,後一次查詢看到了前一次查詢沒有看到的行。幻讀專指新插入的行

  • 23    redo log寫入機制:

    redo log的三種狀態:

        1.存在redo log buffer中,物理上是在MySQL進程內存中;

         2.寫到磁盤,但是沒有持久化,物理上是在文件系統的page cache裏面;

         3.持久化到磁盤,對應的是hard disk;

1.兩個場景讓一個沒有提交事務的redo log寫到磁盤中:

     1.redo log buffer佔用的空間即將達到innodb_log_buffer_size一半的適合,後臺線程會主動寫盤;

     2.並行的事務提交的適合,順帶將這個事務的redo log budder持久化到磁盤

 

 

補充:redis

 

 

 

 

      

 

 

 

 

 

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