java學習筆記(1)

基礎語法

  • switch支持的類型:byte, short, int, char, enumString(jdk7以後支持)css

  • 關鍵字:html

    • final
      • 能夠修飾類、函數、變量
      • 被修飾的類不能夠被繼承;被修飾的方法不能夠被複寫;被修飾的變量是一個常量,只能賦值一次
      • 內部類定義在類中的局部位置上時,只能訪問該局部被final修飾的局部變量
    • synchronized
    • volatile
      • `保證了不一樣線程對這個變量進行讀取時的可見性,即一個線程修改 了某個變量的值 , 這新值對其餘線程來講是當即可見的 (解決了線程間共享變量)
      • 禁止進行指令重排序 ,阻止編譯器對代碼的優化
      • 程序併發正確地執行,必需要保證原子性、可見性以及有序性,鎖保證了原子性,而volatile保證可見性和有序性
  • 在catch中return,finally會不會執行?java

    • 會,會在return以前執行
  • Student s = new Student();在內存中作了哪些事情mysql

    • 加載Student.class文件進內存;
    • 在棧內存爲s開闢空間
    • 在堆內存爲學生對象開闢空間
    • 對學生對象的成員變量進行默認初始化
    • 對學生對象的成員變量進行顯示初始化
    • 經過構造方法對學生對象的成員變量賦值
    • 學生對象初始化完畢,把對象地址賦值給s變量
  • Java異常有哪幾種?Throwble的子類有哪些redis

    • Throwble子類有Error,Exception
    • Error表明了JVM自己的錯誤,不能經過代碼來處理。好比StackOverFlowError
    • Exception表明程序運行時發送的各類不指望發生的事件,能夠被Java異常處理機制使用。好比IOException,RuntimeException
    • 常見的IOException,好比EOFException,FileNotFoundException;常見的RuntimeException,好比NullPointException,ArrayIndexOutOfBoundsExceptions等
  • 序列化serialVersionUID的做用:算法

  • &和&&的區別:sql

    • &&:短路與,當符號左邊表達式的值爲false時,則再也不計算右邊的表單是
    • &:無論左邊表達式的結果爲false或true,都會計算右邊表達式。同時改符號能夠做爲位運算符
  • hashCode和equals方法的關係數據庫

    設計一個類的時候爲須要重寫equals方法,在重寫equals方法的同時,必須重寫hashCode方法。編程

    • 調用equals方法獲得的結果爲true,則兩個對象的hashcode值一定相等
    • 調用equals方法獲得的結果爲false,則兩個對象的hashcode值不必定不一樣
    • 兩個對象的hashcode值不等,則equals方法獲得的結果一定爲false
    • 兩個對象的hashcode值相等,則equals方法獲得的結果未知
  • 什麼是值傳遞和引用傳遞設計模式

    • 值傳遞:傳遞的是值的拷貝,傳遞後就互不相關了
    • 引用傳遞:傳遞的是變量所對應的內存空間的地址
  • 強引用,軟引用和弱引用的區別

    • 強引用:垃圾回收器毫不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足的問題
    • 軟引用:內存空間足夠,垃圾回收器就不會回收它;若是內存空間不足了,就會回收這些對象的內存
    • 弱引用:有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存
  • 數組在內存中如何分配

  • 深刻理解Java中的String

    • String類被final修飾,不能被繼承,而且它的成員方法都默認爲final方法

    • 調用sub,concat,replace方法,會從新生成了一個新的字符串對象,所以原字符串並無被改變

    • 字符串建立方式:(1)使用""引號建立字符串;(2)使用new關鍵字建立字符串.

      • 單獨使用""引號建立的字符串都是常量,編譯期就已經肯定存儲到JVM字符串常量池
      • 使用new String("")建立的對象會存儲到heap中,是運行期新建立的
      • new建立字符串時首先查看池中是否有相同值的字符串,若是有,則拷貝一份到堆中,而後返回堆中的地址;若是池中沒有,則在堆中建立一份,而後返回堆中的地址(注意,此時不須要從堆中複製到池中)
      • 關於equals和==
        • ==做用於基本數據類型的變量,則直接比較其存儲的"值"是否相等,==做用於引用類型的變量,則比較的是所指向的對象的地址
        • equals是基類Object中的方法,用來比較兩個對象的引用是否相等,便是否指向同一個對象。Double,Date,Integer等,都對equals方法進行了重寫用來比較指向的對象所存儲的內容是否相等
      • 字符串拼接符號「+」
        • String s = "a" + "b",由於兩個都是編譯期常量,編譯器在編譯期進行了常量摺疊,即變成String s = "ab"
        • String s = "a" + 變量,至關於運行時new StringBuilder().append(a).append(變量);
      • String str = new String("abc")建立了2個對象解釋:在運行時常量池中建立了一個"abc"對象,而在代碼執行過程當中確實只建立了一個String對象(不理解)

      代碼示例:

      String s0 = "helloworld";// 編譯期肯定
       String s1 = "hello"+"world";// 編譯期肯定,即變成「helloworld」
       String s2 = new String("helloworld");// 運行期肯定
       String s3 = s0 + "";// 運行期肯定,至關於new StringBuilder().append(s0).append("");
       String s4 = "hello" + 1;// 編譯期肯定
       結果:
           s0 == s1 -> true
           s0 == s2 -> false
           s0 == s3 -> false
           s2 == s3 -> false
      複製代碼
  • List集合

    • Arraylist

      • 數據結構:Object數組
      • 增刪查時間複雜度:插入和刪除元素的時間複雜度受元素位置的影響。
        • add(E element)執行的時間複雜度爲O(1)
        • add(int index, E element)執行的時間複雜度爲O(n-i)
        • E remove(int index)執行的時間複雜度爲O(n-i)
        • E get(int index)執行的時間複雜度爲O(1),性能遠高於LinkedList
    • LinkedList

      • 數據結構:雙向循環鏈表
      • 增刪查時間複雜度:查元素的時間複雜度受元素位置的影響
        • add(E element)執行的時間複雜度爲O(1),性能略高於Arraylist,由於Arraylist須要擴容
        • addFirst(E e)執行的時間複雜度爲O(1),性能遠高於Arraylist
        • add(int index, E element)視具體位置而定,index靠前則LinkedList性能會好一點,由於ArrayList性能主要損耗在元素的後移以及擴容上
        • E remove(int index)add(int index, E element)
        • E get(int index)執行的時間複雜度爲O(n/2)
    • Vector:全部的方法都是同步的,相對於Arraylist是線程安全的,

  • Map集合

    • HashMap

      • 數據結構:JDK1.8以前由數組+鏈表組成, JDK1.8以後在解決哈希衝突時有了較大的變化,當鏈表長度大於閾值(默認爲8)時,將鏈表轉化爲紅黑樹,以減小搜索時間(閱讀:(1) java8系列之從新認識HashMap (2) 紅黑樹詳細分析
      • 其它細節
      • 容許null做爲鍵,容許一個或多個鍵所對應的值爲 null
      • 默認的初始化大小爲16。以後每次擴充,容量變爲原來的2倍。若是給了容量的初始值不是2的冪次方大小,會將其擴充爲2的冪次方大小
    • Hashtable

      • 數據結構:數組+鏈表
      • 其它細節
      • 相對於HashMap是線程安全的,使用 synchronized 來保證線程安全
      • 不容許存在null的鍵值
      • 容量初始值默認大小爲11,以後每次擴充,容量變爲原來的2n+1。若是給了容量的初始值,會直接使用該默認值
    • ConcurrentHashMap

      • JDK1.7用分段的數組+鏈表實現;JDK1.8採用數組+鏈表/紅黑二叉樹
      • 實現線程安全的方式:
        • 在JDK1.7的時候,ConcurrentHashMap(分段鎖) 對整個桶數組進行了分割分段(Segment),每一把鎖只鎖容器其中一部分數據,多線程訪問容器裏不一樣數據段的數據,就不會存在鎖競爭,提升併發訪問率(默認分配16個Segment)
        • JDK1.8 的時候已經摒棄了Segment的概念,而是直接用 Node 數組+鏈表+紅黑樹的數據結構來實現,併發控制使用 synchronized 和 CAS 來操做
    • LinkedHashMap:繼承自HashMap,在此基礎上增長了一條雙向鏈表,使得上面的結構能夠保持鍵值對的插入順序。同時經過對鏈表進行相應的操做,實現了訪問順序相關邏輯

    • TreeMap:基於紅黑樹實現,利用紅黑樹的性質,實現了鍵值對排序功能(閱讀:TreeMap源碼分析)

  • Set集合

    • LinkedHashSet:繼承與 HashSet,而且其內部是經過 LinkedHashMap 來實現的
    • HashSet(無序,惟一): 基於 HashMap 實現的,底層採用 HashMap 來保存元素
    • TreeSet(有序,惟一): 紅黑樹(自平衡的排序二叉樹)
  • 線程基礎

    • 生命週期:建立(new),就緒(runnable),運行(running),阻塞(blocked、time waiting、waiting),消亡(dead)

    • 有關線程運行狀態的方法

      • start方法:啓動一個線程,當調用start方法後,系統纔會開啓一個新的線程來執行用戶定義的子任務
      • run方法:經過start方法啓動一個線程以後,當線程得到了CPU執行時間,便進入run方法體去執行具體的任務
      • sleep方法:線程睡眠(線程進入阻塞狀態),交出CPU,讓CPU去執行其餘的任務。(注:不會釋放鎖
      • yield方法:與sleep方法相似,不一樣之處:a. 不能控制具體的交出CPU的時間;b. 只能讓擁有相同優先級的線程有獲取CPU執行時間的機會;c. 不會讓線程進入阻塞狀態,而是讓線程重回就緒狀態,它只須要等待從新獲取CPU執行時間
      • join方法:假如在main主線程中,調用thread.join方法,則main方法會等待thread線程執行完畢或者等待必定的時間。調用join方法是調用了Object的wait方法,wait方法會讓線程進入阻塞狀態,而且會釋放線程佔有的鎖,並交出CPU執行權限
      • interrupt方法:中斷一個正處於阻塞狀態的線程(若是線程不是處於阻塞狀態,則沒法中斷)
    • 如何喚醒處於睡眠狀體的線程

    • 如何釋放處於等待中的線程

    • 什麼叫守護線程,用什麼方法實現守護線程(Thread.setDeamon()的含義)

      在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護線程)用個比較通俗的好比,任何一個守護線程都是整個JVM中全部非守護線程的保姆:只要當前JVM實例中尚存在任何一個非守護線程沒有結束,守護線程就;只有當最後一個非守護線程結束時,守護線程隨着JVM一同結束工做。JVM內部的實現是若是運行的程序只剩下守護線程的話,程序將終止運行,直接結束。因此守護線程是做爲輔助線程存在的,主要的做用是提供計數等等輔助的功能

    • 如何中止一個線程

      • 使用退出標誌,使線程正常退出,也就是當run方法完成後線程終止(標誌用volatile修飾)
      • 使用stop方法能夠強行終止正在運行或掛起的線程(不推薦)
      • 使用interrupt方法中斷線程,使用interrupt方法來終端線程可分爲兩種狀況:
        • 線程處於阻塞狀態,如使用了sleep方法,將拋出一個InterruptedException
        • 使用while(!isInterrupted()){}來判斷線程是否被中斷,線程將直接退出
  • ThreadLocal詳解

    • 做用:每一個線程維護一個本地變量
    • 概述:採用空間換時間,它用於線程間的數據隔離,爲每個使用該變量的線程提供一個副本,每一個線程均可以獨立地改變本身的副本,而不會和其餘線程的副本衝突
    • 原理:ThreadLocal類中維護一個Map,用於存儲每個線程的變量副本,Map中元素的鍵爲線程對象,而值爲對應線程的變量副本
    • 應用:ThreadLocal在Spring中發揮着巨大的做用,在管理Request做用域中的Bean、事務管理、任務調度、AOP等模塊都出現了它的身影。Spring中絕大部分Bean均可以聲明成Singleton做用域,採用ThreadLocal進行封裝,所以有狀態的Bean就可以以singleton的方式在多線程中正常工做了
  • JDK中的動態代理和CGLIB

    • JDK動態代理
      • 概述:經過反射類Proxy以及InvocationHandler回調接口實現的
      • 缺點: JDK中所要進行動態代理的類必需要實現一個接口,也就是說只能對該類所實現接口中定義的方法進行代理,這在實際編程中具備必定的侷限性
    • CGLIB動態代理
      • 原理:動態生成一個要代理類的子類,子類重寫要代理的類的全部不是final的方法。在子類中採用方法攔截的技術攔截全部父類方法的調用,順勢織入橫切邏輯
      • CGLIB底層:使用字節碼處理框架ASM,來轉換字節碼並生成新的類
      • CGLIB優勢:它爲沒有實現接口的類提供代理,爲JDK的動態代理提供了很好的補充。一般可使用Java的動態代理建立代理,但當要代理的類沒有實現接口或者爲了更好的性能,CGLIB是一個好的選擇
      • CGLIB缺點:對於final方法,沒法進行代理

網絡通訊

  • 五層體系結構(TCP/IP協議簇)

    物理層->數據鏈路層->網絡層->傳輸層->應用層

    • 應用層:應用層的任務是經過應用進程間的交互來完成特定網絡應用。應用層協議定義的是應用進程間的通訊和交互的規則。互聯網中的應用層協議,如域名系統DNSHTTP協議
    • 傳輸層:運輸層的主要任務就是負責向兩臺主機進程之間的通訊提供通用的數據傳輸服務。主要有TCP協議UDP協議
    • 網絡層:負責爲分組交換網上的不一樣主機提供通訊服務。在發送數據時,網絡層把運輸層產生的報文段或用戶數據報封裝成分組和包進行傳送
    • 數據鏈路層:兩臺主機之間的數據傳輸,老是在一段一段的鏈路上傳送的,這就須要使用專門的鏈路層的協議。在兩個相鄰節點之間傳送數據時,數據鏈路層將網絡層交下來的IP數據報組裝程幀,在兩個相鄰節點間的鏈路上傳送幀
    • 物理層:實現相鄰計算機節點之間比特流的透明傳送,儘量屏蔽掉具體傳輸介質和物理設備的差別
  • TCP/IP模型

    • 網絡接口層(鏈路層)->網絡層->傳輸層->應用層
  • TCP粘包和拆包產生的緣由

    • 應用程序寫入數據的字節大小大於套接字發送緩衝區的大小
    • 進行MSS大小的TCP分段。MSS是最大報文段長度的縮寫。MSS是TCP報文段中的數據字段的最大長度。數據字段加上TCP首部纔等於整個的TCP報文段。因此MSS並非TCP報文段的最大長度,而是:MSS=TCP報文段長度-TCP首部長度
    • 以太網的payload大於MTU進行IP分片。MTU指:一種通訊協議的某一層上面所能經過的最大數據包大小。若是IP層有一個數據包要傳,並且數據的長度比鏈路層的MTU大,那麼IP層就會進行分片,把數據包分紅若干片,讓每一片都不超過MTU。注意,IP分片能夠發生在原始發送端主機上,也能夠發生在中間路由器上
  • TCP粘包和拆包的解決策略

    • 消息定長。例如100字節
    • 在包尾部增長回車或者空格符等特殊字符進行分割,典型的如FTP協議
    • 將消息分爲消息頭和消息尾
  • tcp,udp區別

    TCP提供面向鏈接的、可靠的數據流傳輸;UDP提供的是非面向鏈接的、不可靠的數據流傳輸

    TCP傳輸單位稱爲TCP報文段,UDP傳輸單位稱爲用戶數據報

    TCP注重數據安全性,UDP數據傳輸快,由於不須要鏈接等待,少了許多操做,可是其安全性卻通常

  • tcp三次握手

    • 第一次握手:創建鏈接時,客戶端發送syn(同步標誌)包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
    • 第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK(確認標誌)包,此時服務器進入SYN_RECV狀態;
    • 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
  • tcp四次揮手

    • 客戶端先發送FIN(結束標誌),進入FIN_WAIT1狀態
    • 服務端收到FIN,發送ACK,進入CLOSE_WAIT狀態,客戶端收到這個ACK,進入FIN_WAIT2狀態
    • 服務端發送FIN,進入LAST_ACK狀態
    • 客戶端收到FIN,發送ACK,進入TIME_WAIT狀態,服務端收到ACK,進入CLOSE狀態
  • 一次完整的HTTP請求過程

    域名解析 --> 發起TCP的3次握手 --> 創建TCP鏈接後發起http請求 --> 服務器響應http請求,瀏覽器獲得html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等) --> 瀏覽器對頁面進行渲染呈現給用戶

  • 講一講長鏈接

    • 基於http協議的長鏈接

    在HTTP1.0和HTTP1.1協議中都有對長鏈接的支持。其中HTTP1.0須要在request中增長」Connection: keep-alive「 header纔可以支持,而HTTP1.1默認支持

    • http1.0請求與服務端的交互過程: a. 客戶端發出帶有包含一個headerConnection:keep-alive的請求;b. 服務端接收到這個請求後,根據http1.0Connection: keep-alive判斷出這是一個長鏈接,就會在response的header中也增長Connection: keep-alive,同是不會關閉已創建的tcp鏈接;c. 客戶端收到服務端的response後,發現其中包含Connection: keep-alive,就認爲是一個長鏈接,不關閉這個鏈接。並用該鏈接再發送request.轉到a
  • TCP如何保證可靠傳輸

  • 詳細介紹http

  • URI和URL的區別

    • URI是統一資源標識符,用來惟一標識一個資源。通常由三部組成:訪問資源的命名機制、存放資源的主機名、由路徑表示資源自身的名稱,着重強調於資源
    • URL是統一資源定位器,用來標識一個資源,並且還指明瞭如何locate這個資源。通常由三部組成協議、存有該資源的主機IP地址(有時也包括端口號)、主機資源的具體地址。如目錄和文件名等
    • URL是URI的子集
  • HTTPS和HTTP的區別

    • https協議須要到CA申請證書,通常免費證書不多,須要交費
    • http是超文本傳輸協議,信息是明文傳輸;https 則是具備安全性的ssl加密傳輸協議
    • http和https使用的是徹底不一樣的鏈接方式,用的端口也不同,前者是80,後者是443
    • http的鏈接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全
  • https是如何保證數據傳輸的安全

    https實際就是在TCP層與http層之間加入了SSL/TLS來爲上層的安全保駕護航,主要用到對稱加密、非對稱加密、證書,等技術進行客戶端與服務器的數據加密傳輸,最終達到保證整個通訊的安全性。

    SSL/TLS協議做用:a. 認證用戶和服務器,確保數據發送到正確的客戶機和服務器;b. 加密數據以防止數據中途被竊取;c. 維護數據的完整性,確保數據在傳輸過程當中不被改變

  • 加密算法

    • 對稱加密
    • 非對稱加密

JVM虛擬機

  • JVM內存區域
    • 運行時數據區域組成
      • 程序計數器(Program Counter Register)
      • Java虛擬機棧(Java Virtual Machine Stacks)
      • 本地方法棧(Native Method Stack)
      • 方法區(Method Area)
      • 運行時常量池(Runtime Constant Pool)
    • 程序計數器
      • 概述:該區域分配了一塊較小的內存空間,他能夠看做是當前線程所執行的字節碼的行號指示器
      • 做用:經過改變計數器的值來指定下一條須要執行的字節碼指令,來恢復中斷前程序運行的位置
      • 特色:
        • 線程私有化:每一個線程都有獨立的程序計數器,來恢復線程前執行的位置
        • 無內存溢出
    • Java虛擬機棧
      • 概述:Java方法執行的內存模型,每一個方法從調用直至執行的過程,對着一個棧幀,在虛擬機棧中入棧到出棧的過程
      • 做用:每一個方法被執行的時候,都會建立一個「棧幀」來存儲局部變量表、操做數棧、動態連接、方法出口等信息
      • 特色:
        • 線程私有化
        • 生命週期與線程執行結束相同
      • 局部變量表
        • 做用:存放編譯期間可知的各類基本數據類型(8種)、對象引用、return Address類型(指向一條字節碼指令的地址)
        • 佔用空間:64位長度的long和double類型佔用2個局部變量空間(Slot),其他數據類型只佔用1個
        • 分配時機:在編譯期間完成分配,當進入一個方法時,這個方法所須要在幀中分配多大的局部變量空間是徹底肯定的,在方法運行期間不會改變局部變量表的大小
    • 本地方法棧
      • 概述:與虛擬機棧相似,是爲虛擬機使用到的Native方法服務的內存區域
      • 區別:
        • 虛擬機棧:爲虛擬機執行Java方法(字節碼)服務
        • 本地方法棧:爲虛擬機使用到的Native方法服務
    • Java堆
      • 建立時間:JVM啓動時建立該區域
      • 佔用空間:該區域是Java虛擬機所管理的內存中最大的一塊區域
      • 做用:用於存放了對象實例及數組(全部new的對象)
      • 特色:
        • 垃圾收集器(GC)做用於該區域,回收不使用的對象的內存空間
        • 各個線程共享(讀和寫)的內存區域
        • 該區域的大小可經過-Xms(最小值)和-Xmx(最大值)參數設置,一般-Xms-Xmx的值設置成同樣(避免在運行時頻繁調整Heap的大小)
    • 方法區
      • 概述:Java虛擬機規範將方法區描述爲堆的一個邏輯部分,可是它卻有一個別名叫作Non-Heap(非堆),目的是與Java堆分開。
      • 佔有空間:默認最小值爲16MB,最大值爲64MB。
      • 做用:用於存儲虛擬機加載的類信息、常量、靜態變量,是各個線程共享的內存區域
      • 特色:
        • 各個線程共享(讀和寫)的內存區域
        • 該區域的大小限制能夠經過 -XX:PermSize-XX:MaxPernSize 參數
    • 運行時常量池
      • 概述:方法區的一部分。Class文件除了有類的(版本、字段、方法、接口)等描述信息外,還有一項信息就是常量池
      • 做用:用於存放編譯期生成的各類字面量和符號引用
      • 動態性:
        • Java語言並不要求常量池必定只有編譯期才能產生,也就是並不是預置入Class文件的常量池內的
        • 容後才能進入方法區的運行時常量池,運行期間也能夠將新的常量放入池中
        • 這種特性用的比較普遍的即是String類的intern方法
  • 類加載過程

    經過類的全限定名來獲取定義此類的二進制字節流,將這個字節流所表明的靜態存儲結構轉換爲方法區的運行時數據結構,在內存中生成一個表明類的數據訪問入口的java.lang.Class對象

    • 驗證過程:確保Class文件的字節流中包含的信息符合當前虛擬機的要求,而且不會危害虛擬機自身的安全
    • 準備過程:正式爲類變量分配內存並設置類變量初始值的階段,只包括類變量而不包括實例變量和final類變量,並且僅僅只是初始化爲0值
    • 解析過程:將常量池內的符號引用轉換爲直接引用的過程。符號引用用一組符號來描述所引用的目標。而直接引用是直接指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄
    • 初始化階段:開始執行Java程序代碼(字節碼),執行類的構造器<clinit>()方法,<clinit>()方法是由編譯器自動收集全部類變量的賦值動做和靜態語句塊的語句合併而成,同一類中的靜態塊與類變量按順序初始化,在同一個加載器下,一個類只會被初始化一次
  • 雙親委派模型

    若是一個類加載器收到類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器完成。每一個類加載器都是如此,只有當父加載器在本身的搜索範圍內找不到指定的類時(即ClassNotFoundException),子加載器纔會嘗試本身去加載

數據結構和算法

  • 二叉樹
  • 棧:先進後出(FILO)
  • 隊列:先進先出(FIFO)
  • 經常使用排序算法
    • 冒泡排序:

      概述:比較相鄰的元素。若是第一個比第二個大,就交換他們兩個。 對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。針對全部的元素重複以上的步驟,除了最後一個。持續每次對愈來愈少的元素重複上面的步驟,直到沒有任何一對數字須要比較

    • 選擇排序:

      概述:在要排序的一組數中,選出最小的一個數與第一個位置的數交換;而後在 剩下的數當中再找最小的與第二個位置的數交換,如此循環到倒數第二個數和最後一個數比較爲止

    • 插入排序

      概述:每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置(從後向前找到合適位置後),直到所有插入排序完爲止

    • 希爾排序

      概述:先將整個待排序的記錄序列分割成爲若干子序列分別進行直接插入排序,待整個序列中的記錄「基本有序」時,再對全體記錄進行依次直接插入排序

    • 歸併排序

      概述:歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個子序列,每一個子序列是有序的。而後再把有序子序列合併爲總體有序序列

    • 快速排序

      概述:經過一趟排序將待排序記錄分割成獨立的兩部分,一部分全小於選取的參考值,另外一部分全大於選取的參考值。這樣分別對兩部分排序以後順序就能夠排好了

    • 堆排序

      概述:將待排序的序列構形成一個大頂堆。此時,整個序列的最大值就是堆頂的根節點。將它移走(其實就是將其與堆數組的末尾元素交換,此時末尾元素就是最大值),而後將剩餘的n-1個序列從新構形成一個堆,這樣就會獲得n個元素中的次最大值。如此反覆執行,就能獲得一個有序序列了

  • 算法習題
    • 由兩個棧實現一個隊列。
    • 由兩個隊列實現一個棧。
    • 判斷出棧順序是否符合要求。
    • 給定一個整數,請寫一個函數判斷該整數的奇偶性
    • 一樣給定一個整數,請寫一個函數判斷該整數是否是2的整數次冪
    • 給定一個整數,請寫一個函數判斷該整數的二進制表示中1的個數
    • 在其餘數都出現兩次的數組中找到只出現一次的那個數
    • 在其餘數都出現兩次的數組中找到只出現一次的那兩個數

設計模式

  • 單例模式

高併發

  • CAS機制
    • 原理:CAS有3個操做數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作
    • 缺點:
      • ABA問題。 由於CAS須要在操做值的時候檢查下值有沒有發生變化,若是沒有發生變化則更新,可是若是一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化
      • 循環時間長開銷大。 自旋CAS若是長時間不成功,會給CPU帶來很是大的執行開銷
      • 只能保證一個共享變量的原子操做
  • 線程池
    • 執行策略ThreadPoolExecutor
      public ThreadPoolExecutor(int corePoolSize,//核心線程池大小
                            int maximumPoolSize,//最大線程池大小
                            long keepAliveTime,//線程池中超過corePoolSize數目的空閒線程最大存活時間;能夠allowCoreThreadTimeOut(true)成爲核心線程的有效時間
                            TimeUnit unit,//keepAliveTime的時間單位
                            BlockingQueue<Runnable> workQueue,//阻塞任務隊列
                            ThreadFactory threadFactory,//線程工廠
                            RejectedExecutionHandler handler) {//當提交任務數超過限制時
      複製代碼
      • 線程數量未達到corePoolSize,則新建一個線程(核心線程)執行任務
      • 線程數量達到了corePoolSize,則將任務移入隊列等待
      • 隊列已滿,新建線程(非核心線程)執行任務
      • 隊列已滿,總線程數又達到了maximumPoolSize,就會由(RejectedExecutionHandler)拋出異常
    • 常見四種線程池
      • Executors.newCachedThreadPool():可緩存線程池
        • 線程數無限制
        • 有空閒線程則複用空閒線程,若無空閒線程則新建線程
        • 必定程序減小頻繁建立/銷燬線程,減小系統開銷
      • Executors.FixedThreadPool():定長線程池
        • 可控制線程最大併發數(同時執行的線程數)
        • 超出的線程會在隊列中等待
      • Executors.ScheduledThreadPool():定時線程池
        • 支持定時及週期性任務執行
      • Executors.SingleThreadExecutor():單線程化的線程池
        • 有且僅有一個工做線程執行任務
        • 全部任務按照指定順序執行,即遵循隊列的入隊出隊規則
    • 四種拒絕策略
      • AbortPolicy:拒絕任務,且還拋出RejectedExecutionException異常,線程池默認策略
      • CallerRunPolicy:拒絕新任務進入,若是該線程池尚未被關閉,那麼這個新的任務在執行線程中被調用
      • DiscardOldestPolicy:若是執行程序還沒有關閉,則位於頭部的任務將會被移除,而後重試執行任務(再次失敗,則重複該過程),這樣將會致使新的任務將會被執行,而先前的任務將會被移除。
      • DiscardPolicy:沒有添加進去的任務將會被拋棄,也不拋出異常。基本上爲靜默模式

數據庫

  • Mysql 中 MyISAM 和 InnoDB 的區別有哪些

    • InnoDB支持事務,MyISAM不支持,對於InnoDB每一條SQL語言都默認封裝成事務,自動提交,這樣會影響速度,因此最好把多條SQL語言放在begin和commit之間,組成一個事務
    • InnoDB支持外鍵,而MyISAM不支持
    • InnoDB是彙集索引,數據文件是和索引綁在一塊兒的,必需要有主鍵,經過主鍵索引效率很高。可是輔助索引須要兩次查詢,先查詢到主鍵,而後再經過主鍵查詢到數據。所以,主鍵不該該過大,由於主鍵太大,其餘索引也都會很大。而MyISAM是非彙集索引,數據文件是分離的,索引保存的是數據文件的指針。主鍵索引和輔助索引是獨立的
    • InnoDB不保存表的具體行數,執行select count(*) from table時須要全表掃描。而MyISAM用一個變量保存了整個表的行數,執行上述語句時只須要讀出該變量便可,速度很快
    • Innodb不支持全文索引,而MyISAM支持全文索引,查詢效率上MyISAM要高
  • 爲何用自增列做爲主鍵

    • 表使用自增主鍵,那麼每次插入新的記錄,記錄就會順序添加到當前索引節點的後續位置,當一頁寫滿,就會自動開闢一個新的頁
    • 若是使用非自增主鍵(若是身份證號或學號等),因爲每次插入主鍵的值近似於隨機,所以每次新紀錄都要被插到現有索引頁得中間某個位置,此時MySQL不得不爲了將新記錄插到合適位置而移動數據,甚至目標頁面可能已經被回寫到磁盤上而從緩存中清掉,此時又要從磁盤上讀回來,這增長了不少開銷,同時頻繁的移動、分頁操做形成了大量的碎片,獲得了不夠緊湊的索引結構,後續不得不經過OPTIMIZE TABLE來重建表並優化填充頁面
  • Mysql中InnoDB的一級索引、二級索引

    每一個InnoDB表具備一個特殊的索引稱爲聚簇索引(也叫彙集索引,聚類索引,簇集索引)。若是表上定義有主鍵,該主鍵索引就是聚簇索引。若是未定義主鍵,MySQL取第一個惟一索引(unique)並且只含非空列(NOT NULL)做爲主鍵,InnoDB使用它做爲聚簇索引。若是沒有這樣的列,InnoDB就本身產生一個這樣的ID值,它有六個字節,並且是隱藏的,使其做爲聚簇索引。表中的聚簇索引(clustered index )就是一級索引,除此以外,表上的其餘非聚簇索引都是二級索引,又叫輔助索引(secondary indexes)

  • B+樹索引和哈希索引的區別

    • B+樹是一個平衡的多叉樹。B+樹從根節點到葉子節點的搜索效率基本至關,不會出現大幅波動
    • 哈希索引採用必定的哈希算法,把鍵值換成新的哈希值,檢索時不須要相似B+樹那樣從根節點逐級查找,只需一次哈希算法便可馬上定位到相應的位置
  • B樹和B+樹的區別

    • B+樹中只有葉子節點會帶有指向記錄的指針(ROWID),而B樹則全部節點都帶有,在內部節點出現的索引項不會再出如今葉子節點中
    • B+樹中全部葉子節點都是經過指針鏈接在一塊兒,而B樹不會
  • Mysql 索引類型有多少種

    主鍵索引(primary key)、 惟一索引(unique index)、普通索引(index)、全文索引(fulltext key)

    alert table student
    add primary key ('stu_id'),     //主鍵索引
    add unique index 'stu_code' ('stu_code'), //惟一索引
    add index 'name_phone' ('name','phone'),  //普通索引,複合索引
    add fulltext index 'stu_desc' ('stu_desc'); //全文索引
    複製代碼
  • Mysql 索引的使用與優化

    • 索引使用原則
      • 列獨立:保證索引包含的字段獨立在查詢語句中,不能是在表達式中

      • 左前綴:like匹配模式左邊不能以通配符開始,才能使用索引

      • 複合索引由左到右生效,例如:創建索引index(a,b,c),以下查詢where條件分析

        where a = 3 # 是,只使用了a
        where a=3 and b=5 # 是,使用了a,b
        where a=3 and b=5 and c=4 # 是,使用了a,b,c
        where b=3 or where c=4 # 否
        where a=3 and c=4 # 是,僅使用了a
        where a=3 and b>10 and c=7 # 是,使用了a,b
        where a=3 and b like '%xx%' and c=7 # 是,使用了a,b???
        複製代碼
    • 可以使用索引的典型應用
      • 匹配全值

        對索引中全部列都指定具體值,便是對索引中的全部列都有等值匹配的條件

      • 匹配值的範圍查詢

        對索引的值可以進行範圍查找

      • 匹配最左前綴

        僅僅使用索引中的最左邊列進行查詢,好比在 col1 + col2 + col3 字段上的聯合索引可以被包含 col一、(col1 + col2)、(col1 + col2 + col3)的等值查詢利用到,但是不可以被 col二、(col二、col3)的等值查詢利用到

      • 僅僅對索引進行查詢

        查詢的列都在索引的字段中時,查詢的效率更高

      • 匹配列前綴

        僅僅使用索引中的第一列,而且只包含索引第一列的開頭一部分進行查找

      • 實現索引匹配部分精確而其餘部分進行範圍匹配
      • 列名是索引,那麼使用 column_name is null 就會使用索引
    • 存在索引但不能使用索引的典型場景
      • 以%開頭的 like 查詢不能利用 B-Tree 索引

        解決方法:1. 使用全文索引;2. 利用 innodb 的表都是聚簇表的特色,採起一種輕量級別的解決方式:通常狀況下,索引都會比表小,掃描索引要比掃描表更快,而Innodb 表上二級索引 idx_last_name 實際上存儲字段 last_name 還有主鍵 actot_id,那麼理想的訪問應該是首先掃描二級索引 idx_last_name 得到知足條件的last_name like '%NI%' 的主鍵 actor_id 列表,以後根據主鍵回表去檢索記錄,這樣訪問避開了全表掃描演員表 actor 產生的大量 IO 請求。查詢sql爲 SELECT * FROM (SELECT actor_id FROM actor WHERE last_name LIKE '%NI%') a, actor b WHERE a.actor_id = b.actor_id

      • 數據類型出現隱式轉換的時候也不會使用索引

        當列的類型是字符串,那麼必定記得在 where 條件中把字符常量值用引號引發來,不然即使這個列上有索引,mysql 也不會用到,由於 MySQL 默認把輸入的常量值進行轉換之後才進行檢索

      • 複合索引的狀況下,假如查詢條件不包含索引列最左邊部分,即不知足最左原則 leftmost,是不會使用複合索引的
      • MySQL 認爲使用索引比全表掃描更慢,則不使用索引
      • 用 or 分割開的條件,若是 or 前的條件中的列有索引,然後面的列中沒有索引,那麼涉及的索引都不會被用到
    • 使用索引的小技巧
      • 字符串字段權衡區分度與長度的技巧
      • 左前綴不易區分的字段索引創建方法

        解決辦法:1. 倒過來存儲並創建索引;2. 新增僞hash字段 把字符串轉化爲整型

      • 索引覆蓋

        若是查詢的列剛好是索引的一部分,那麼查詢只須要在索引文件上進行,不須要回行到磁盤,這種查詢,速度極快

      • 延遲關聯

        在根據條件查詢數據時,若是查詢條件不能用的索引,能夠先查出數據行的id,再根據id去取數據行

      • 索引排序

        排序的字段上加入索引,能夠提升速度

      • 重複索引和冗餘索引

        重複索引:在同一列或者相同順序的幾個列創建了多個索引,成爲重複索引,沒有任何意義,刪掉; 冗餘索引:兩個或多個索引所覆蓋的列有重疊,好比對於列m,n ,加索引index m(m),indexmn(m,n),稱爲冗餘索引

      • 索引碎片與維護

        在數據表長期的更改過程當中,索引文件和數據文件都會產生空洞,造成碎片。修復表的過程十分耗費資源,能夠用比較長的週期修復表

        //清理方法
        alert table xxx engine innodb; 
        //或
        optimize table xxx;
        複製代碼
      • innodb引擎的索引注意事項

        Innodb 表要儘可能本身指定主鍵,若是有幾個列都是惟一的,要選擇最常做爲訪問條件的列做爲主鍵,另外,Innodb 表的普通索引都會保存主鍵的鍵值,因此主鍵要儘量選擇較短的數據類型,能夠有效的減小索引的磁盤佔用,提升索引的緩存效果。

  • 爲何使用數據索引能提升效率

    • 數據索引的存儲是有序的
    • 在有序的狀況下,經過索引查詢一個數據是無需遍歷索引記錄的
    • 極端狀況下,數據索引的查詢效率爲二分法查詢效率,趨近於 log2(N)
  • mysql聯合索引

    • 聯合索引是兩個或更多個列上的索引。對於聯合索引:Mysql從左到右的使用索引中的字段,一個查詢能夠只使用索引中的一部份,但只能是最左側部分。例如索引是key index (a,b,c). 能夠支持a、a,b、a,b,c3種組合進行查找,但不支持b,c進行查找 .當最左側字段是常量引用時,索引就十分有效
    • 利用索引中的附加列,您能夠縮小搜索的範圍,但使用一個具備兩列的索引 不一樣於使用兩個單獨的索引。複合索引的結構與電話簿相似,人名由姓和名構成,電話簿首先按姓氏對進行排序,而後按名字對有相同姓氏的人進行排序。若是您知道姓,電話簿將很是有用;若是您知道姓和名,電話簿則更爲有用,但若是您只知道名不姓,電話簿將沒有用處
  • 什麼狀況下應不建或少建索引

    • 表記錄太少
    • 常常插入、刪除、修改的表
    • 數據重複且分佈平均的表字段
    • 常常和主字段一塊查詢但主字段索引值比較多的表字段
  • MySQL分區

  • 四種隔離級別

    • Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生
    • Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生
    • Read committed (讀已提交):可避免髒讀的發生
    • Read uncommitted (讀未提交):最低級別,任何狀況都沒法保證
  • MySQL優化

    • 開啓查詢緩存,優化查詢
    • explain你的select查詢,分析你的查詢語句或是表結構的性能瓶頸
    • 當只要一行數據時使用limit 1,MySQL數據庫引擎會在找到一條數據後中止搜索,而不是繼續日後查少下一條符合記錄的數據
    • 爲搜索字段建索引
    • 垂直分表,選擇正確的存儲引擎
  • explain性能分析

    +----+-------------+-------+------------+------+---------------+-----+---------+------+------+----------+-------+
    | id | select_type | table | partitions | type | possible_keys | key | key_len | ref  | rows | filtered | Extra |
    +----+-------------+-------+------------+------+---------------+-----+---------+------+------+----------+-------+
    
    id:select 查詢的序列號,包含一組能夠重複的數字,表示查詢中執行sql語句的順序。id不一樣時,值大的優先執行;
    相同時,執行順序由上而下
    select_type:查詢類型,主要是用於區別普通查詢,聯合查詢,嵌套的複雜查詢。
        simple:簡單的select 查詢,查詢中不包含子查詢或者union
        primary:查詢中若包含任何複雜的子查詢,最外層查詢則被標記爲primary
        subquery:在select或where 列表中包含了子查詢
        derived:在from列表中包含的子查詢被標記爲derived(衍生)MySQL會遞歸執行這些子查詢,把結果放在臨時表裏
        union:若第二個select出如今union以後,則被標記爲union,若union包含在from子句的子查詢中,外層select將
                被標記爲:derived
        union result:從union表獲取結果的select
        partitions:表所使用的分區,若是要統計十年公司訂單的金額,能夠把數據分爲十個區,每年表明一個區。
        這樣能夠大大的提升查詢效率
    * type:鏈接類型,共有8個級別。性能從最優到最差的排序 system > const > eq_ref > ref > range > index > all
        all:(full table scan)全表掃描無疑是最差,如果百萬千萬級數據量,全表掃描會很是慢。
        index:(full index scan)全索引文件掃描比all好不少,畢竟從索引樹中找數據,比從全表中找數據要快。
        range:只檢索給定範圍的行,使用索引來匹配行。範圍縮小了,固然比全表掃描和全索引文件掃描要快。
                sql語句中通常會有between,in,>,< 等查詢。
        ref:非惟一性索引掃描,本質上也是一種索引訪問,返回全部匹配某個單獨值的行。好比查詢公司全部屬於研發團隊
        的同事,匹配的結果是多個並不是惟一值。
        eq_ref:惟一性索引掃描,對於每一個索引鍵,表中有一條記錄與之匹配。好比查詢公司的CEO,匹配的結果只多是
        一條記錄,
        const:表示經過索引一次就能夠找到,const用於比較primary key 或者unique索引。由於只匹配一行數據,因此很快,
        若將主鍵至於where列表中,MySQL就能將該查詢轉換爲一個常量。
        system:表只有一條記錄(等於系統表),這是const類型的特列,平時不會出現,瞭解便可
    * possible_keys:顯示查詢語句可能用到的索引(一個或多個或爲null),不必定被查詢實際使用。僅供參考使用
    * key:顯示查詢語句實際使用的索引。若爲null,則表示沒有使用索引
    * key_len:顯示索引中使用的字節數,可經過key_len計算查詢中使用的索引長度
    * ref:顯示索引的哪一列或常量被用於查找索引列上的值
    * rows:根據表統計信息及索引選用狀況,大體估算出找到所需的記錄所須要讀取的行數,值越大越很差
    * filtered:一個百分比的值,和rows列的值一塊兒使用,能夠估計出查詢執行計劃(QEP)中的前一個表的結果集,從而肯定
        join操做的循環次數。小表驅動大表,減輕鏈接的次數
    * extra
        Using filesort: 說明MySQL會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中沒法利
        用索引完成的排序操做稱爲「文件排序」 。出現這個就要馬上優化sql。
        Using temporary: 使用了臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序 order by 和
        分組查詢 group by。 出現這個更要馬上優化sql。
        Using index: 表示相應的select 操做中使用了覆蓋索引(Covering index),避免訪問了表的數據行,效果不錯!
        若是同時出現Using where,代表索引被用來執行索引鍵值的查找。若是沒有同時出現Using where
        ,表示索引用來讀取數據而非執行查找動做。覆蓋索引(Covering Index) :也叫索引覆蓋,就是select 
        的數據列只用從索引中就可以取得,沒必要讀取數據行,MySQL能夠利用索引返回select列表中的字段,而沒必要根據索引再
        次讀取數據文件。
        Using index condition: 在5.6版本後加入的新特性,優化器會在索引存在的狀況下,經過符合RANGE範圍的條數 和
        總數的比例來選擇是使用索引仍是進行全表遍歷。
        Using where: 代表使用了where 過濾
        Using join buffer: 代表使用了鏈接緩存
        impossible wherewhere 語句的值老是false,不可用,不能用來獲取任何元素
        distinct: 優化distinct操做,在找到第一匹配的元組後即中止找一樣值的動做
    複製代碼

Spring總結

  • IOC容器

  • SpringMVC處理請求的完整過程

  • 源碼相關

    // bean建立以前修改bean的定義屬性,例如修改scope值從singleton改成prototype
    // 注意:不要在BeanFactoryPostProcessor進行可能觸發bean實例化的操做
    public interface BeanFactoryPostProcessor {
        void postProcessBeanFactory(ConfigurableListableBeanFactory var1);
    }
    複製代碼
    // bean的初始化時,設置了屬性以後回調
    public interface InitializingBean {
        void afterPropertiesSet() throws Exception;
    }
    複製代碼
    // bean實例化以後,在執行bean的初始化方法先後,添加一些本身的處理邏輯
    public interface BeanPostProcessor {
        Object postProcessBeforeInitialization(Object var1, String var2);
        Object postProcessAfterInitialization(Object var1, String var2);
    }
    複製代碼
    // 實現類獲取Spring的應用上下文ApplicationContext
    public interface ApplicationContextAware extends Aware {
        void setApplicationContext(ApplicationContext var1);
    }
    複製代碼
    // 實現類獲取BeanFactory
     public interface BeanFactoryAware extends Aware {
        void setBeanFactory(BeanFactory var1) throws BeansException;
    }
    複製代碼
    // 實現註冊事件
    public abstract class ApplicationEvent extends EventObject {
        private final long timestamp = System.currentTimeMillis();
        public ApplicationEvent(Object source) {
            super(source);
        }
        public final long getTimestamp() {
            return this.timestamp;
        }
    }
    // 實現類獲取ApplicationEventPublisher發佈事件
    public interface ApplicationEventPublisherAware extends Aware {
        void setApplicationEventPublisher(ApplicationEventPublisher var1);
    }
    // 實現類對發佈的事件監聽
    public interface ApplicationListener<E extends ApplicationEvent> 
            extends EventListener 
    {
        void onApplicationEvent(E var1);
    }
    複製代碼

    參考文章:

Spring的ORM知識點

  • Mybatis 是如何執行 sql 語句的

  • JDBC 獲取數據庫鏈接的大概過程

  • PreparedStatement 和 Statement 區別

  • MyBatis的Api接口

    public interface SqlSessionFactory {
        SqlSession openSession();
    
        SqlSession openSession(boolean var1);
    
        SqlSession openSession(Connection var1);
    
        SqlSession openSession(TransactionIsolationLevel var1);
    
        SqlSession openSession(ExecutorType var1);
    
        SqlSession openSession(ExecutorType var1, boolean var2);
    
        SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);
    
        SqlSession openSession(ExecutorType var1, Connection var2);
    
        Configuration getConfiguration();
    }
    複製代碼
    public interface SqlSession extends Closeable {
        <T> T selectOne(String var1);
    
        <T> T selectOne(String var1, Object var2);
    
        <E> List<E> selectList(String var1);
    
        <E> List<E> selectList(String var1, Object var2);
    
        <E> List<E> selectList(String var1, Object var2, RowBounds var3);
    
        <K, V> Map<K, V> selectMap(String var1, String var2);
    
        <K, V> Map<K, V> selectMap(String var1, Object var2, String var3);
    
        <K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4);
    
        <T> Cursor<T> selectCursor(String var1);
    
        <T> Cursor<T> selectCursor(String var1, Object var2);
    
        <T> Cursor<T> selectCursor(String var1, Object var2, RowBounds var3);
    
        void select(String var1, Object var2, ResultHandler var3);
    
        void select(String var1, ResultHandler var2);
    
        void select(String var1, Object var2, RowBounds var3, ResultHandler var4);
    
        int insert(String var1);
    
        int insert(String var1, Object var2);
    
        int update(String var1);
    
        int update(String var1, Object var2);
    
        int delete(String var1);
    
        int delete(String var1, Object var2);
    
        void commit();
    
        void commit(boolean var1);
    
        void rollback();
    
        void rollback(boolean var1);
    
        List<BatchResult> flushStatements();
    
        void close();
    
        void clearCache();
    
        Configuration getConfiguration();
    
        <T> T getMapper(Class<T> var1);
    
        Connection getConnection();
    }
    複製代碼

netty知識點

  • JAVA中的BIO、NIO和AIO

redis詳解

  • 單線程:指的是網絡請求模塊使用了一個線程(因此不需考慮併發安全性),即一個線程處理全部網絡請求,其餘模塊仍用了多個線程

  • redis可以快速執行緣由

    • 絕大部分請求是純粹的內存操做
    • 採用單線程,避免了沒必要要的上下文切換和競爭條件
    • 非阻塞IO - IO多路複用
  • Redis的 5 種基礎數據結構

    • String(字符串):將用戶信息結構體使用 JSON 序列化成字符串,而後將序列化後的字符串塞進 Redis 來緩存。一樣,取用戶信息會通過一次反序列化的過程

      Redis 的字符串是動態字符串,是能夠修改的字符串,內部結構實現上相似於 Java 的 ArrayList,採用預分配冗餘空間的方式來減小內存的頻繁分配,如圖中所示,內部爲當前字符串實際分配的空間 capacity 通常要高於實際字符串長度 len。當字符串長度小於 1M 時,擴容都是加倍現有的空間,若是超過 1M,擴容時一次只會多擴 1M 的空間。須要注意的是字符串最大長度爲 512M。

      • 經常使用指令
        # (批量)設置值
        set key value
        setnx key value
        mset key1 value1 key2 value2
        # 獲取值
        get key
        mget key1 key2
        # 判斷值是否存在
        exists key
        # 刪除值
        del key
        # 設置key設置過時時間,到點自動刪除
        expire key seconds
        setex key seconds value
        # value值是整數,可計數。自增範圍是signed long的最大值和最小值
        incr numberKey
        incrby numberKey stepLenth
        複製代碼
    • List(列表):至關於LinkedList,底層實現的數據結構是快速列表,數據結構是鏈表而不是數組。插入和刪除操做很是快,時間複雜度爲 O(1),可是索引定位很慢,時間複雜度爲 O(n)

      底層存儲快速鏈表: 首先在列表元素較少的狀況下會使用一塊連續的內存存儲,這個結構是ziplist,也便是壓縮列表。它將全部的元素緊挨着一塊兒存儲,分配的是一塊連續的內存。當數據量比較多的時候纔會改爲quicklist。由於普通的鏈表須要的附加指針空間太大,會比較浪費空間,並且會加劇內存的碎片化。好比這個列表裏存的只是int類型的數據,結構上還須要兩個額外的指針prevnext。因此 Redis 將鏈表和 ziplist 結合起來組成了 quicklist。也就是將多個ziplist使用雙向指針串起來使用。這樣既知足了快速的插入刪除性能,又不會出現太大的空間冗餘

      • 經常使用指令
        # 將值value插入到列表key的表尾(最右邊)
        rpush key value1 value2 value3
        # 將值value插入到列表key的表頭(最左邊)
        lpush key value1 value2 value3
        # 返回列表key的長度
        llen key
        # 移除並返回列表 key 的頭元素
        lpop key
        # 移除並返回列表 key 的尾元素
        rpop key
        複製代碼
    • hash (字典):至關於HashMap,它是無序字典。內部實現是數組+鏈表二維結構,與HashMap不一樣的是rehash的方式不同,由於JavaHashMap在字典很大時,rehash是個耗時的操做,須要一次性所有rehash。Redis爲了高性能,不能堵塞服務,因此採用了漸進式rehash策略。

      漸進式 rehash: 在 rehash 的同時,保留新舊兩個 hash 結構,查詢時會同時查詢兩個 hash 結構,而後在後續的定時任務中以及 hash 操做指令中,按部就班地將舊 hash 的內容一點點遷移到新的hash結構中。當搬遷完成了,就會使用新的 hash 結構取而代之

      • 經常使用指令
      # 將哈希表 key 中的域 field 的值設爲 value
      hset key field value
      hmset key field value [field value ...]
      # 返回哈希表 key 中,全部的域和值
      hgetall key
      # 返回哈希表 key 中域的數量
      hlen key
      # 返回哈希表 key 中給定域 field 的值
      hget key field
      hmget key field [field ...]
      複製代碼
    • set (集合):至關於HashSet,它內部的鍵值對是無序的惟一的。它的內部實現至關於一個特殊的字典,字典中全部的value都是一個值NULL

      • 經常使用指令
      # 將元素加入到集合key當中
      sadd key member [member ...]
      # 判斷 member 元素是否集合 key 的成員
      sismember key member
      # 返回集合 key 的基數(集合中元素的數量)
      scard key
      # 移除並返回集合中的一個隨機元素
      spop key
      複製代碼
    • zset(有序列表):至關於 SortedSetHashMap的結合體,一方面它是一個set,保證了內部value的惟一性,另外一方面它能夠給每一個 value 賦予一個score,表明這個value的排序權重。它的內部實現用的是一種叫作「跳躍列表」的數據

      跳躍列表:

      • 經常使用指令
        # 將member元素及其score值加入到有序集key當中
        zadd key score1 member1 score2 member2
        # 返回有序集key中(按score值從小到大),指定區間內的成員(位置數表示倒數第n位)
        zrange key start stop [withscores]
        # 返回有序集 key(按score值從大到小) 中,指定區間內的成員
        zrevrange key start stop [withscores]
        # 返回有序集 key 的數量
        zcard key
        # 返回有序集 key 中,成員 member 的 score 值
        zscore key member
        # 返回有序集 key 中成員 member 的排名(按 score 值遞增)
        zrank key member
        # 返回有序集key中,全部score值介於min和max之間(包括等於 min 或 max )的成員(按 score 值遞增)
        zrangebyscore key min max [withscores] [limit offset count]
        # 移除有序集 key 中的一個或多個成員
        zrem key member1 member2...
        複製代碼
    • list/set/hash/zset這四種數據結構是容器型數據結構,共享規則:a. 若是容器不存在,那就建立一個,再進行操做;b. 若是容器裏元素沒有了,數據結構自動刪除,內存被回收

  • Redis分佈式鎖

    # 只在鍵不存在時,纔對鍵進行設置操做,同時設置過時時間
    > set lock:lockkey true ex 5 nx
        ...處理邏輯...
    > del lock:lockkey
    # 結合ThreadLocal實現鎖的可重入性
    private ThreadLocal<Map<String, Integer>> lockers = new ThreadLocal<>();
    private Map<String, Integer> currentLockers() {
        Map<String, Integer> refs = lockers.get();
        if (refs != null) {
          return refs;
        }
        lockers.set(new HashMap<>());
        return lockers.get();
    }
    public boolean lock(String key) {
        Map<String, Integer> refs = currentLockers();
        Integer refCnt = refs.get(key);
        if (refCnt != null) {
          refs.put(key, refCnt + 1);
          return true;
        }
        
        boolean ok = this._lock(key);
        if (!ok) {
          return false;
        }
        refs.put(key, 1);
        return true;
    }
    複製代碼
  • redis隊列思路

    • 使用rpush/lpush操做入隊列;
    • 使用阻塞讀的指令blpop/brpop,在隊列沒有數據的時候,會當即進入休眠狀態,一旦數據到來,則馬上醒過來。消息的延遲幾乎爲零
    • 空閒鏈接問題:若是線程一直阻塞在哪裏,Redis的客戶端鏈接就成了閒置鏈接,閒置太久,服務器通常會主動斷開鏈接,減小閒置資源佔用。這個時候blpop/brpop會拋出異常。所以須要注意捕獲異常和重試
    • 延時隊列:經過 Redis 的 zset(有序列表)來實現。將消息序列化成一個字符串做爲 zset 的 value ,這個消息的到期處理時間做爲 score ,而後用多個線程輪詢 zset 獲取到期的任務進行處理
  • redis位圖

    位數組的順序和字符的位順序是相反的。應用場景距離:用戶簽到記錄

    # 對 key 所儲存的字符串值,設置或清除指定偏移量上的位(bit)。value值能夠是0也能夠是1
    setbit key offset value
    # 計算給定字符串中,被設置爲 1 的比特位的數量
    bitcount key [start] [end]
    # 返回string的二進制中第一個0或1的位置
    bitpos key bit [start] [end]
    # 
    bitfield key [get type offset] [set type offset value] [incrby type offset increment] [overflow wrap|sat|fail]
    # 示例:hello的二進制表示爲01101000(h) 01100101(e) 01101100(l) 01101100(l) 01101111(o),最左邊的是第一位
    > setbit s 1 1 (零存)
    > setbit s 2 1
    > setbit s 4 1
    > setbit s 9 1
    > setbit s 10 1
    > setbit s 13 1
    > setbit s 15 1
    > get s # 輸出結果爲he(整取)
    > get bit s 1(零取)
    > set w h (整存)
    > getbit w 2(零取)
    
    > set w hello
    > bitcount w # 21,返回字符串hello中1的個數
    > bitcount w 0 1 # 7,返回字符串hello前兩個字符中1的個數
    > bitpos w 0 # 0,返回字符串hello中的第一個0位
    > bitpos w 1 # 1,返回字符串hello中的第一個1位
    > bitpos w 1 1 1 # 9,字符串hello從第二個字符算起,第一個1位
    > bitpos w 1 2 2 # 17,字符串hello從第三個字符算起,第一個1位
    複製代碼
  • HyperLogLog統計

    提供不精確的去重計數方案,雖然不精確可是也不是很是不精確,標準偏差是0.81%。應用距離:記錄用戶一天以內每一個網頁天天的UV數據(用戶重複訪問須要去重)

    # 將任意數量的元素添加到指定的 HyperLogLog 裏面
     pfadd key element [element ...]
     # 返回儲存在給定鍵的 HyperLogLog 的近似基數,多個鍵返回並集的近似基數
     pfcount key [key ...]
     # 將多個 HyperLogLog 合併(merge)爲一個 HyperLogLog
     pfmerge destkey sourcekey [sourcekey ...]
     > pfadd codehole user1
     > pfcount codehole
     > pfadd codehole user1 user2 user3
     > pfcount codehole # 結果 3
    複製代碼
  • redis布隆過濾器(4.0新特性)

    布隆過濾器是一個不怎麼精確的set結構,會有小小的誤判率,例如當布隆過濾器說某個值存在時,這個值可能不存在;當它說不存在時,那就確定不存在。使用場景:客戶端新聞推薦系統不重複推送

    原理:向布隆過濾器中添加key時,會使用多個hash函數對key進行hash算得一個整數索引值而後對位數組長度進行取模運算獲得一個位置,每一個hash函數都會算得一個不一樣的位置。再把位數組的這幾個位置都置爲1就完成了add。key 是否存在時,跟 add 同樣,也會把 hash 的幾個位置都算出來,看看位數組中這幾個位置是否都位 1,只要有一個位爲 0,那麼說明布隆過濾器中這個 key 不存在

    空間佔用估計:布隆過濾器有兩個參數,第一個是預計元素的數量 n,第二個是錯誤率f。公式根據這兩個輸入獲得兩個輸出,第一個輸出是位數組的長度 l,也就是須要的存儲空間大小 (bit),第二個輸出是 hash 函數的最佳數量 k。hash 函數的數量也會直接影響到錯誤率,最佳的數量會有最低的錯誤率

    k=0.7*(l/n)  # 約等於
    f=0.6185^(l/n)  # ^ 表示次方計算,也就是 math.pow
    結論:
        1. 位數組相對越長 (l/n),錯誤率 f 越低,這個和直觀上理解是一致的
        2. 位數組相對越長 (l/n),hash 函數須要的最佳數量也越多,影響計算效率
        3. 當一個元素平均須要 1 個字節 (8bit) 的指紋空間時 (l/n=8),錯誤率大約爲 2%
        4. 錯誤率爲 10%,一個元素須要的平均指紋空間爲 4.792 個 bit,大約爲 5bit
        5. 錯誤率爲 1%,一個元素須要的平均指紋空間爲 9.585 個 bit,大約爲 10bit
        6. 錯誤率爲 0.1%,一個元素須要的平均指紋空間爲 14.377 個 bit,大約爲 15bit
    複製代碼
    > bf.add codehole user1 # 添加單個元素
    > bf.madd codehole user2 user3 user4 # 批量添加元素
    > bf.exists codehole user1 # 查詢元素是否存在
    > f.mexists codehole user4 user5 # 一次查詢多個元素是否存在
    複製代碼
  • 簡單限流

    原理:zset數據結構的key記錄用戶的行爲歷史,value用毫秒時間戳,經過滑動窗口與閾值max_count進行比較就能夠得出當前的行爲是否容許

    • 代碼實現
      public class SimpleRateLimiter {
      
        private Jedis jedis;
      
        public SimpleRateLimiter(Jedis jedis) {
          this.jedis = jedis;
        }
      
        public boolean isActionAllowed(String userId, String actionKey, int period, int maxCount) {
          String key = String.format("hist:%s:%s", userId, actionKey);
          long nowTs = System.currentTimeMillis();
          Pipeline pipe = jedis.pipelined();
          pipe.multi();
          pipe.zadd(key, nowTs, "" + nowTs);
          pipe.zremrangeByScore(key, 0, nowTs - period * 1000);
          Response<Long> count = pipe.zcard(key);
          pipe.expire(key, period + 1);
          pipe.exec();
          pipe.close();
          return count.get() <= maxCount;
        }
      }
      複製代碼
  • 漏斗限流

    • java版實現單機漏斗算法
      public class FunnelRateLimiter {
      
        static class Funnel {
          int capacity;  
          float leakingRate;
          int leftQuota;
          long leakingTs;
      
          public Funnel(int capacity, float leakingRate) {
            this.capacity = capacity; # 漏斗容量
            this.leakingRate = leakingRate; # 漏嘴流水速率
            this.leftQuota = capacity; # 漏斗剩餘空間
            this.leakingTs = System.currentTimeMillis();# 上一次漏水時間
          }
      
          void makeSpace() {
            long nowTs = System.currentTimeMillis();
            long deltaTs = nowTs - leakingTs; # 距離上一次漏水過去了多久
            int deltaQuota = (int) (deltaTs * leakingRate);# 根據時間差計算出能夠騰出的空間
            if (deltaQuota < 0) { # 間隔時間太長,整數數字過大溢出
              this.leftQuota = capacity;
              this.leakingTs = nowTs;
              return;
            }
            if (deltaQuota < 1) { # 騰出空間過小,最小單位是1
              return;
            }
            this.leftQuota += deltaQuota; # 增長漏水容量
            this.leakingTs = nowTs; # 重置漏水時間
            if (this.leftQuota > this.capacity) {
              this.leftQuota = this.capacity;
            }
          }
      
          boolean watering(int quota) {
            makeSpace();
            if (this.leftQuota >= quota) { # 判斷剩餘空間是否足夠
              this.leftQuota -= quota;
              return true;
            }
            return false;
          }
        }
      
        private Map<String, Funnel> funnels = new HashMap<>();
      
        public boolean isActionAllowed(String userId, String actionKey, int capacity, float leakingRate) {
          String key = String.format("%s:%s", userId, actionKey);
          Funnel funnel = funnels.get(key);
          if (funnel == null) {
            funnel = new Funnel(capacity, leakingRate);
            funnels.put(key, funnel);
          }
          return funnel.watering(1); // 須要1個quota
        }
      }
      複製代碼
    • 漏斗限流Redis-Cell(Redis 4.0特性)
      # 建立quota個容量爲capacity,漏水速率leakingRate,閾值爲quota的名稱未key的漏斗
      cl.throttle key capacity, leakingRate quota
      > cl.throttle laoqian:reply 15 30 60
      1) (integer) 0   # 0 表示容許,1表示拒絕
      2) (integer) 15  # 漏斗容量capacity
      3) (integer) 14  # 漏斗剩餘空間left_quota
      4) (integer) -1  # 若是拒絕了,須要多長時間後再試(漏斗有空間了,單位秒)
      5) (integer) 2   # 多長時間後,漏斗徹底空出來(left_quota==capacity,單位秒)
      複製代碼
    • GEOHash座標計算
  • Redis主從複製

    過程原理:

    • 當從庫和主庫創建MS關係後,會向主數據庫發送SYNC命令
    • 主庫接收到SYNC命令後會開始在後臺保存快照(RDB持久化過程),並將期間接收到的寫命令緩存起來
    • 當快照完成後,主Redis會將快照文件和全部緩存的寫命令發送給從Redis
    • 從Redis接收到後,會載入快照文件而且執行收到的緩存的命令以後,主Redis每當接收到寫命令時就會將命令發送從Redis,從而保證數據的一致
  • redis兩種持久化方式的優缺點

    • RDB 持久化能夠在指定的時間間隔內生成數據集的時間點快照
    • AOF 持久化記錄服務器執行的全部寫操做命令,並在服務器啓動時,經過從新執行這些命令來還原數據集
    • Redis 還能夠同時使用 AOF 持久化和 RDB 持久化。當redis重啓時,它會有限使用AOF文件來還原數據集,由於AOF文件保存的數據集一般比RDB文件所保存的數據集更加完整
    • RDB的優勢:1. RDB 是一個很是緊湊(compact)的文件,它保存了 Redis 在某個時間點上的數據集。 這種文件很是適合用於進行備份: 好比說,你能夠在最近的 24 小時內,每小時備份一次 RDB 文件,而且在每月的每一天,也備份一個 RDB 文件。 這樣的話,即便趕上問題,也能夠隨時將數據集還原到不一樣的版本。2. RDB 很是適用於災難恢復(disaster recovery):它只有一個文件,而且內容都很是緊湊,能夠(在加密後)將它傳送到別的數據中心,或者亞馬遜 S3 中。3. RDB 能夠最大化 Redis 的性能:父進程在保存 RDB 文件時惟一要作的就是 fork 出一個子進程,而後這個子進程就會處理接下來的全部保存工做,父進程無須執行任何磁盤 I/O 操做。4. RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快
相關文章
相關標籤/搜索