java 面試題1

java 大叔發表於2020-11-09

hashCode()和equals()

兩者區別
  • equals()相等的兩個物件他們的hashCode()肯定相等,也就是用equals()對比是絕對可靠的。
  • hashCode()相等的兩個物件他們的equals()不一定相等,也就是hashCode()不是絕對可靠的。
為什麼equals()相等,hashCode就一定要相等,而hashCode相等,卻不要求equals相等?

​ 之所以hashCode相等,卻可以equal不等,就比如ObjectA和ObjectB他們都有屬性name,那麼hashCode都以name計算,所以hashCode一樣,但是兩個物件屬於不同型別,所以equals為false。

為什麼需要hashCode?
  • 通過hashCode可以很快的查到小記憶體塊。
  • hashMap、hashset等hash開頭的資料結構,都要用到hash,來確定儲存位置,理論上一個記憶體位置的資料應該是相等的,但是hashcode演算法可能不是唯一的,就會導致不相同的資料被儲存在一個記憶體位置上。

ArrayList、LinkedList

兩者區別
  1. ArrayList是實現了基於動態陣列的資料結構,LinkedList是基於連結串列結構。
  2. 對於查詢操作,ArrayList要優於LinkedList,因為LinkedList要移動指標。
  3. 對於新增和刪除操作,LinkedList比較佔優勢,因為ArrayList要移動資料。
  4. LinkedList比ArrayList更佔記憶體,因為LinkedList為每一個節點儲存了兩個引用,一個指向前一個元素,一個指向下一個元素。
使用場景

​ ArrayList使用在查詢比較多,但是插入和刪除比較少的情況,而LinkedList用在查詢比較少而插入刪除比較多的情況。

實現原理

​ 略~

HashMap、HashTable、ConcurrentHashMap

HashTable
  1. 底層陣列+連結串列實現,無論key還是value都不能為null,執行緒安全,實現執行緒安全的方式是在修改資料時鎖住整個HashTable,效率低,ConcurrentHashMap做了相關優化。
HashMap
  1. 底層陣列+連結串列實現,可以儲存null鍵和null值,執行緒不安全

  2. HashMap如何保證執行緒安全?

    1. ConcurrentHashMap

      Map concurrentHashMap = new ConcurrentHashMap<>();
      
    2. 通過Collections.synchronizedMap()返回一個新的map。(要習慣基於介面程式設計,因為返回的不是hashmap,而是map的實現)

      Map synchronizedHashMap = Collections.synchronizedMap(new HashMap());
      
ConcurrentHashMap
  1. 底層採用分段的陣列+連結串列實現,執行緒安全
  2. 通過把整個Map分為N個Segment,可以提供相同的執行緒安全,但是效率提升N倍,預設提升16倍。(讀操作不加鎖,由於HashEntry的value變數是 volatile的,也能保證讀取到最新的值。)
  3. Hashtable的synchronized是針對整張Hash表的,即每次鎖住整張表讓執行緒獨佔,ConcurrentHashMap允許多個修改操作併發進行,其關鍵在於使用了鎖分離技術

Hashtable和HashMap都實現了Map介面,但是Hashtable的實現是基於Dictionary抽象類的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴充套件性更好。

HashMap基於雜湊思想,實現對資料的讀寫。當我們將鍵值對傳遞給put()方法時,它呼叫鍵物件的hashCode()方法來計算hashcode,然後找到bucket位置來儲存值物件。當獲取物件時,通過鍵物件的equals()方法找到正確的鍵值對,然後返回值物件。HashMap使用連結串列來解決碰撞問題,當發生碰撞時,物件將會儲存在連結串列的下一個節點中。HashMap在每個連結串列節點中儲存鍵值對物件。當兩個不同的鍵物件的hashcode相同時,它們會儲存在同一個bucket位置的連結串列中,可通過鍵物件的equals()方法來找到鍵值對。

​ 在HashMap中,null可以作為鍵,這樣的鍵只有一個,但可以有一個或多個鍵所對應的值為null。當get()方法返回null值時,即可以表示HashMap中沒有該key,也可以表示該key所對應的value為null。因此,在HashMap中不能由get()方法來判斷HashMap中是否存在某個key,應該用**containsKey()**方法來判斷。而在Hashtable中,無論是key還是value都不能為null。

多執行緒

併發程式設計三要素?

原子性

​ 原子性指的是一個或者多個操作,要麼全部執行並且在執行的過程中不被其他操作打斷,要麼就全部都不執行。

可見性

​ 可見性指多個執行緒操作一個共享變數時,其中一個執行緒對變數進行修改後,其他執行緒可以立即看到修改的結果。

有序性

​ 有序性,即程式的執行順序按照程式碼的先後順序來執行。

實現可見性的方法有哪些?

​ synchronized或者Lock:保證同一個時刻只有一個執行緒獲取鎖執行程式碼,鎖釋放之前把最新的值重新整理到主記憶體,實現可見性。

建立執行緒的有哪些方式?
  1. 繼承Thread類建立執行緒類
  2. 通過Runnable介面建立執行緒類
  3. 通過Callable和Future建立執行緒
  4. 通過執行緒池建立
建立執行緒的三種方式的對比?
  1. 採用實現Runnable、Callable介面的方式建立多執行緒。

    優勢是:

    執行緒類只是實現了Runnable介面或Callable介面,還可以繼承其他類。

    在這種方式下,多個執行緒可以共享同一個target物件,所以非常適合多個相同執行緒來處理同一份資源的情況,從而可以將CPU、程式碼和資料分開,形成清晰的模型,較好地體現了物件導向的思想。

    劣勢是:

    程式設計稍微複雜,如果要訪問當前執行緒,則必須使用Thread.currentThread()方法。

  2. 使用繼承Thread類的方式建立多執行緒

    優勢是:

    編寫簡單,如果需要訪問當前執行緒,則無需使用Thread.currentThread()方法,直接使用this即可獲得當前執行緒。

    劣勢是:

    執行緒類已經繼承了Thread類,所以不能再繼承其他父類。

Runnable和Callable的區別

​ Callable規定(重寫)的方法是call(),Runnable規定(重寫)的方法是run()。 Callable的任務執行後可返回值,而Runnable的任務是不能返回值的。 Call方法可以丟擲異常,run方法不可以。 執行Callable任務可以拿到一個Future物件,表示非同步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。通過Future物件可以瞭解任務執行情況,可取消任務的執行,還可獲取執行結果。

什麼是執行緒池?有哪幾種建立方式?

​ 執行緒池就是提前建立若干個執行緒,如果有任務需要處理,執行緒池裡的執行緒就會處理任務,處理完之後執行緒並不會被銷燬,而是等待下一個任務。由於建立和銷燬執行緒都是消耗系統資源的,所以當你想要頻繁的建立和銷燬執行緒的時候就可以考慮使用執行緒池來提升系統的效能。

​ java 提供了一個 java.util.concurrent.Executor介面的實現用於建立執行緒池。

四種執行緒池的建立
  1. newCachedThreadPool建立一個可快取執行緒池
  2. newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數。
  3. newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。
  4. newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務。
執行緒池構造方法中的引數
  • corePoolSize:執行緒池的大小。執行緒池建立之後不會立即去建立執行緒,而是等待執行緒的到來。噹噹前執行的執行緒數大於改值是,執行緒會加入到緩衝佇列;
  • maximumPoolSize:執行緒池中建立的最大執行緒數;
  • keepAliveTime:空閒的執行緒多久時間後被銷燬。預設情況下,改值線上程數大於corePoolSize時,對超出corePoolSize值得這些執行緒起作用。
  • unit:TimeUnit列舉型別的值,代表keepAliveTime時間單位,可以取下列值: TimeUnit.DAYS; //天  TimeUnit.HOURS; //小時  TimeUnit.MINUTES; //分鐘  TimeUnit.SECONDS; //秒  TimeUnit.MILLISECONDS; //毫秒  TimeUnit.MICROSECONDS; //微妙  TimeUnit.NANOSECONDS; //納秒
  • workQueue:阻塞佇列,用來儲存等待執行的任務,決定了執行緒池的排隊策略,有以下取值:  ArrayBlockingQueue;  LinkedBlockingQueue;  SynchronousQueue;  threadFactory:執行緒工廠,是用來建立執行緒的。預設new Executors.DefaultThreadFactory();
  • handler:執行緒拒絕策略。當建立的執行緒超出maximumPoolSize,且緩衝佇列已滿時,新任務會拒絕,有以下取值  ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecutionException異常。  ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不丟擲異常。  ThreadPoolExecutor.DiscardOldestPolicy:丟棄佇列最前面的任務,然後重新嘗試執行任務(重複此過程)  ThreadPoolExecutor.CallerRunsPolicy:由呼叫執行緒處理該任務
執行緒池的優點?
  1. 重用存在的執行緒,減少物件建立銷燬的開銷。
  2. 可有效的控制最大併發執行緒數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
  3. 提供定時執行、定期執行、單執行緒、併發數控制等功能。
常用的併發工具類有哪些?

​ CountDownLatch、CyclicBarrier、Semaphore、Exchanger

什麼是CAS

​ CAS是compare and swap的縮寫,即我們所說的比較交換。

​ cas是一種基於鎖的操作,而且是樂觀鎖。在java中鎖分為樂觀鎖和悲觀鎖。悲觀鎖是將資源鎖住,等一個之前獲得鎖的執行緒釋放鎖之後,下一個執行緒才可以訪問。而樂觀鎖採取了一種寬泛的態度,通過某種方式不加鎖來處理資源,比如通過給記錄加version來獲取資料,效能較悲觀鎖有很大的提高。

​ CAS 操作包含三個運算元 —— 記憶體位置(V)、預期原值(A)和新值(B)。如果記憶體地址裡面的值和A的值是一樣的,那麼就將記憶體裡面的值更新成B。CAS是通過無限迴圈來獲取資料的,若果在第一輪迴圈中,a執行緒獲取地址裡面的值被b執行緒修改了,那麼a執行緒需要自旋,到下次迴圈才有可能機會執行。

java.util.concurrent.atomic 包下的類大多是使用CAS操作來實現的( AtomicInteger,AtomicBoolean,AtomicLong)
ReadWriteLock是什麼

​ 首先明確一下,不是說ReentrantLock不好,只是ReentrantLock某些時候有侷限。如果使用ReentrantLock,可能本身是為了防止執行緒A在寫資料、執行緒B在讀資料造成的資料不一致,但這樣,如果執行緒C在讀資料、執行緒D也在讀資料,讀資料是不會改變資料的,沒有必要加鎖,但是還是加鎖了,降低了程式的效能。

​ 因為這個,才誕生了讀寫鎖ReadWriteLock。ReadWriteLock是一個讀寫鎖介面,ReentrantReadWriteLock是ReadWriteLock介面的一個具體實現,實現了讀寫的分離,讀鎖是共享的,寫鎖是獨佔的,讀和讀之間不會互斥,讀和寫、寫和讀、寫和寫之間才會互斥,提升了讀寫的效能。

什麼是樂觀鎖和悲觀鎖

​ **樂觀鎖:**就像它的名字一樣,對於併發間操作產生的執行緒安全問題持樂觀狀態,樂觀鎖認為競爭不總是會發生,因此它不需要持有鎖,將比較-替換這兩個動作作為一個原子操作嘗試去修改記憶體中的變數,如果失敗則表示發生衝突,那麼就應該有相應的重試邏輯。

​ **悲觀鎖:**還是像它的名字一樣,對於併發間操作產生的執行緒安全問題持悲觀狀態,悲觀鎖認為競爭總是會發生,因此每次對某資源進行操作時,都會持有一個獨佔的鎖,就像synchronized,不管三七二十一,直接上了鎖就操作資源了。

執行緒B怎麼知道執行緒A修改了變數
  1. volatile修飾變數
  2. synchronized修飾修改變數的方法
  3. wait/notify
  4. while輪詢
ThreadLocal是什麼?有什麼用?

​ ThreadLocal是一個本地執行緒副本變數工具類。主要用於將私有執行緒和該執行緒存放的副本物件做一個對映,各個執行緒之間的變數互不干擾,在高併發場景下,可以實現無狀態的呼叫,特別適用於各個執行緒依賴不通的變數值完成操作的場景。

​ 簡單說ThreadLocal就是一種以空間換時間的做法,在每個Thread裡面維護了一個以開地址法實現的ThreadLocal.ThreadLocalMap,把資料進行隔離,資料不共享,自然就沒有執行緒安全方面的問題了。

多執行緒同步有哪幾種方法?

​ Synchronized關鍵字,Lock鎖實現,分散式鎖等。

死鎖的原因
  1. 是多個執行緒涉及到多個鎖,這些鎖存在著交叉,所以可能會導致了一個鎖依賴的閉環。

    例如:執行緒在獲得了鎖A並且沒有釋放的情況下去申請鎖B,這時,另一個執行緒已經獲得了鎖B,在釋放鎖B之前又要先獲得鎖A,因此閉環發生,陷入死鎖迴圈。

  2. 預設的鎖申請操作是阻塞的。

    所以要避免死鎖,就要在一遇到多個物件鎖交叉的情況,就要仔細審查這幾個物件的類中的所有方法,是否存在著導致鎖依賴的環路的可能性。總之是儘量避免在一個同步方法中呼叫其它物件的延時方法和同步方法。

如果你提交任務時,執行緒池佇列已滿,這時會發生什麼

這裡區分一下:

  1. 如果使用的是無界佇列LinkedBlockingQueue,也就是無界佇列的話,沒關係,繼續新增任務到阻塞佇列中等待執行,因為LinkedBlockingQueue可以近乎認為是一個無窮大的佇列,可以無限存放任務

  2. 如果使用的是有界佇列比如ArrayBlockingQueue,任務首先會被新增到ArrayBlockingQueue中,ArrayBlockingQueue滿了,會根據maximumPoolSize的值增加執行緒數量,如果增加了執行緒數量還是處理不過來,ArrayBlockingQueue繼續滿,那麼則會使用拒絕策略RejectedExecutionHandler處理滿了的任務,預設是AbortPolicy

Java中用到的執行緒排程演算法是什麼

​ 搶佔式。一個執行緒用完CPU之後,作業系統會根據執行緒優先順序、執行緒飢餓情況等資料算出一個總的優先順序並分配下一個時間片給某個執行緒執行。

什麼是自旋

​ 很多synchronized裡面的程式碼只是一些很簡單的程式碼,執行時間非常快,此時等待的執行緒都加鎖可能是一種不太值得的操作,因為執行緒阻塞涉及到使用者態和核心態切換的問題。既然synchronized裡面的程式碼執行得非常快,不妨讓等待鎖的執行緒不要被阻塞,而是在synchronized的邊界做忙迴圈,這就是自旋。如果做了多次忙迴圈發現還沒有獲得鎖,再阻塞,這樣可能是一種更好的策略。

同步方法和同步塊,哪個是更好的選擇?

​ 同步塊,這意味著同步塊之外的程式碼是非同步執行的,這比同步整個方法更提升程式碼的效率。請知道一條原則:同步的範圍越小越好。

Java執行緒數過多會造成什麼異常?
  1. 執行緒的生命週期開銷非常高

  2. 消耗過多的CPU資源

    如果可執行的執行緒數量多於可用處理器的數量,那麼有執行緒將會被閒置。大量空閒的執行緒會佔用許多記憶體,給垃圾回收器帶來壓力,而且大量的執行緒在競爭CPU資源時還將產生其他效能的開銷。

  3. 降低穩定性

    JVM在可建立執行緒的數量上存在一個限制,這個限制值將隨著平臺的不同而不同,並且承受著多個因素制約,包括JVM的啟動引數、Thread建構函式中請求棧的大小,以及底層作業系統對執行緒的限制等。如果破壞了這些限制,那麼可能丟擲OutOfMemoryError異常

JVM

什麼情況下會發生棧記憶體溢位

​ 棧是執行緒私有的,他的生命週期與執行緒相同,每個方法在執行的時候都會建立一個棧幀,用來儲存區域性變數表,運算元棧,動態連結,方法出口等資訊。區域性變數表又包含基本資料型別,物件引用型別 如果執行緒請求的棧深度大於虛擬機器所允許的最大深度,將丟擲StackOverflowError異常,方法遞迴呼叫產生這種結果。 如果Java虛擬機器棧可以動態擴充套件,並且擴充套件的動作已經嘗試過,但是無法申請到足夠的記憶體去完成擴充套件,或者在新建立執行緒的時候沒有足夠的記憶體去建立對應的虛擬機器棧,那麼Java虛擬機器將丟擲一個OutOfMemory 異常。(執行緒啟動過多) 引數 -Xss 去調整JVM棧的大小

詳解JVM記憶體模型

img

​ 程式計數器:當前執行緒所執行的位元組碼的行號指示器,用於記錄正在執行的虛擬機器位元組指令地址,執行緒私有。

​ Java虛擬棧:存放基本資料型別、物件的引用、方法出口等,執行緒私有。

​ Native方法棧:和虛擬棧相似,只不過它服務於Native方法,執行緒私有。

​ Java堆:java記憶體最大的一塊,所有物件例項、陣列都存放在java堆,GC回收的地方,執行緒共享。

​ 方法區:存放已被載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼資料等。(即永久帶),回收目標主要是常量池的回收和型別的解除安裝,各執行緒共享

新生代中為什麼要分為Eden和Survivor
  • 如果沒有Survivor,Eden區每進行一次Minor GC,存活的物件就會被送到老年代。老年代很快被填滿,觸發Major GC.老年代的記憶體空間遠大於新生代,進行一次Full GC消耗的時間比Minor GC長得多,所以需要分為Eden和Survivor。
  • Survivor的存在意義,就是減少被送到老年代的物件,進而減少Full GC的發生,Survivor的預篩選保證,只有經歷16次Minor GC還能在新生代中存活的物件,才會被送到老年代。
  • 設定兩個Survivor區最大的好處就是解決了碎片化,剛剛新建的物件在Eden中,經歷一次Minor GC,Eden中的存活物件就會被移動到第一塊survivor space S0,Eden被清空;等Eden區再滿了,就再觸發一次Minor GC,Eden和S0中的存活物件又會被複制送入第二塊survivor space S1(這個過程非常重要,因為這種複製演算法保證了S1中來自S0和Eden兩部分的存活物件佔用連續的記憶體空間,避免了碎片化的發生)

SQL

SQL優化
  1. 語句優化,要儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引
  2. 語句優化,用select field_1,field_2,field_3… form table代替select * from table
  3. 語句優化,使用連線(JOIN)代替子查詢
  4. 語句優化,使用聯合(UNION)代替手動建立的臨時表
  5. 建表優化,儘量減少欄位寬度,使用ENUM儲存固定資料,例如性別
  6. 語句優化,避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描
  7. 建表優化,欄位選用優先順序,整形>date,time>enum,char>varchar>blob,text
  8. 語句優化,儘量避免在 where 子句中使用 != 或 <> 操作符,否則將引擎放棄使用索引而進行全表掃描
  9. 語句優化,儘量使用全文檢索代替like查詢
  10. 語句優化,Update語句儘量只set需要修改的欄位,否則帶來沒必要的開銷
  11. 語句優化,資料量大時(1萬以上)儘量避免使用,遊標
  12. 持續更新中。。。

Spring

列舉一些重要的Spring模組?
  • Spring Core: 基礎,可以說 Spring 其他所有的功能都需要依賴於該類庫。主要提供 IOC 依賴注入功能。
  • Spring Aspects : 該模組為與AspectJ的整合提供支援。
  • Spring AOP :提供了面向方面的程式設計實現。
  • Spring JDBC : Java資料庫連線。
  • Spring JMS :Java訊息服務。
  • Spring ORM : 用於支援Hibernate等ORM工具。
  • Spring Web : 為建立Web應用程式提供支援。
  • Spring Test : 提供了對 JUnit 和 TestNG 測試的支援。
IOC

​ IoC(Inverse of Control:控制反轉)是一種設計思想,就是 將原本在程式中手動建立物件的控制權,交由Spring框架來管理。 IoC 容器是 Spring 用來實現 IoC 的載體, IoC 容器實際上就是個Map(key,value),Map 中存放的是各種物件。

Spring IOC的初始化過程:

img

AOP

​ AOP(Aspect-Oriented Programming:面向切面程式設計)能夠將那些與業務無關,卻為業務模組所共同呼叫的邏輯或責任(例如事務處理、日誌管理、許可權控制等)封裝起來,便於減少系統的重複程式碼降低模組間的耦合度,並有利於未來的可擴充性和可維護性

Spring AOP就是基於動態代理的,如果要代理的物件,實現了某個介面,那麼Spring AOP會使用JDK Proxy,去建立代理物件,而對於沒有實現介面的物件,就無法使用 JDK Proxy 去進行代理了,這時候Spring AOP會使用Cglib ,這時候Spring AOP會使用 Cglib 生成一個被代理物件的子類來作為代理

關於面向切面程式設計的一些術語

  • 切面(Aspect): 切面用於組織多個Advice,Advice放在切面中定義
  • 連線點(Joinpoint): 程式執行過程中明確的點,如方法的呼叫,或者異常的丟擲。在Spring AOP中,連線點總是方法的呼叫
  • 增強處理(Advice): AOP框架在特定的切入點執行的增強處理。處理有“around”、“before”和“after”等型別
  • 切入點(Pointcut): 可以插入增強處理的連線點。簡而言之,當某個連線點滿足指定要求時,該連線點將被新增增強處理,該連線點也就變成了切入點Spring的AOP支援
Spring 中的單例 bean 的執行緒安全問題了解嗎?

​ 大部分時候我們並沒有在系統中使用多執行緒,所以很少有人會關注這個問題。單例 bean 存線上程問題,主要是因為當多個執行緒操作同一個物件的時候,對這個物件的非靜態成員變數的寫操作會存線上程安全問題。

常見的有兩種解決辦法:

  1. 在Bean物件中儘量避免定義可變的成員變數(不太現實)。
  2. 在類中定義一個ThreadLocal成員變數,將需要的可變成員變數儲存在 ThreadLocal 中(推薦的一種方式)。
什麼是ThreadLocal?

​ ThreadLocal類顧名思義可以理解為執行緒本地變數。也就是說如果定義了一個ThreadLocal,每個執行緒往這個ThreadLocal中讀寫是執行緒隔離,互相之間不會影響的。它提供了一種將可變資料通過每個執行緒有自己的獨立副本從而實現執行緒封閉的機制。

大致的實現思路:

​ Thread類有一個型別為ThreadLocal.ThreadLocalMap的例項變數threadLocals,也就是說每個執行緒有一個自己的ThreadLocalMap。ThreadLocalMap有自己的獨立實現,可以簡單地將它的key視作ThreadLocal,value為程式碼中放入的值(實際上key並不是ThreadLocal本身,而是它的一個弱引用)。每個執行緒在往某個ThreadLocal裡塞值的時候,都會往自己的ThreadLocalMap裡存,讀也是以某個ThreadLocal作為引用,在自己的map裡找對應的key,從而實現了執行緒隔離。

SpringMVC 工作原理了解嗎?
  1. 客戶端(瀏覽器)傳送請求,直接請求到 DispatcherServlet
  2. DispatcherServlet 根據請求資訊呼叫 HandlerMapping,解析請求對應的 Handler
  3. 解析到對應的 Handler(也就是我們平常說的 Controller 控制器)後,開始由 HandlerAdapter 介面卡處理。
  4. HandlerAdapter 會根據 Handler來呼叫真正的處理器開處理請求,並處理相應的業務邏輯。
  5. 處理器處理完業務後,會返回一個 ModelAndView 物件,Model 是返回的資料物件,View 是個邏輯上的 View
  6. ViewResolver 會根據邏輯 View 查詢實際的 View
  7. DispaterServlet 把返回的 Model 傳給 View(檢視渲染)。
  8. View 返回給請求者(瀏覽器)
Spring 框架中用到了哪些設計模式?
  • 工廠設計模式 : Spring使用工廠模式通過 BeanFactoryApplicationContext 建立 bean 物件。
  • 代理設計模式 : Spring AOP 功能的實現。
  • 單例設計模式 : Spring 中的 Bean 預設都是單例的。
  • 模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate 等以 Template 結尾的對資料庫操作的類,它們就使用到了模板模式。
  • 包裝器設計模式 : 我們的專案需要連線多個資料庫,而且不同的客戶在每次訪問中根據需要會去訪問不同的資料庫。這種模式讓我們可以根據客戶的需求能夠動態切換不同的資料來源。
  • 觀察者模式: Spring 事件驅動模型就是觀察者模式很經典的一個應用。
  • 介面卡模式 :Spring AOP 的增強或通知(Advice)使用到了介面卡模式、spring MVC 中也是用到了介面卡模式適配Controller
  • ……
@Component 和 @Bean 的區別是什麼?
  1. 作用物件不同: @Component 註解作用於類,而@Bean註解作用於方法。

  2. @Component通常是通過類路徑掃描來自動偵測以及自動裝配到Spring容器中(我們可以使用 @ComponentScan 註解定義要掃描的路徑從中找出標識了需要裝配的類自動裝配到 Spring 的 bean 容器中)。@Bean 註解通常是我們在標有該註解的方法中定義產生這個 bean,@Bean告訴了Spring這是某個類的例項,當我需要用它的時候還給我。

  3. @Bean 註解比 Component 註解的自定義性更強,而且很多地方我們只能通過 @Bean 註解來註冊bean。比如當我們引用第三方庫中的類需要裝配到 Spring容器時,則只能通過 @Bean來實現。

    @Bean註解使用示例:

    @Configuration
    public class AppConfig {
        @Bean
            public TransferService transferService() {
                return new TransferServiceImpl();
            }
    }
    

    上面的程式碼相當於下面的 xml 配置

    <beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
    </beans>    
    

    下面這個例子是通過 @Component 無法實現的。

    @Bean
    public OneService getService(status) {
        case (status)  {
            when 1:
            return new serviceImpl1();
            when 2:
            return new serviceImpl2();
            when 3:
            return new serviceImpl3();
        }
    }                                                    
    
將一個類宣告為Spring的 bean 的註解有哪些?

​ 我們一般使用 @Autowired 註解自動裝配 bean,要想把類標識成可用於 @Autowired註解自動裝配的 bean 的類,採用以下註解可實現:

  • @Component :通用的註解,可標註任意類為 Spring 元件。如果一個Bean不知道屬於拿個層,可以使用@Component 註解標註。
  • @Repository : 對應持久層即 Dao 層,主要用於資料庫相關操作。
  • @Service : 對應服務層,主要涉及一些複雜的邏輯,需要用到 Dao層。
  • @Controller : 對應 Spring MVC 控制層,主要使用者接受使用者請求並呼叫 Service 層返回資料給前端頁面。
Spring 管理事務的方式有幾種?
  1. 程式設計式事務,在程式碼中硬編碼。(不推薦使用)
  2. 宣告式事務,在配置檔案中配置(推薦使用)

宣告式事務又分為兩種:

  1. 基於XML的宣告式事務
  2. 基於註解的宣告式事務
Spring 事務中的隔離級別有哪幾種?

​ DEFAULT 這是一個PlatfromTransactionManager預設的隔離級別,使用資料庫預設的事務隔離級別. 未提交讀(read uncommited) :髒讀,不可重複讀,虛讀都有可能發生 已提交讀 (read commited):避免髒讀。但是不可重複讀和虛讀有可能發生 可重複讀 (repeatable read) :避免髒讀和不可重複讀.但是虛讀有可能發生. 序列化的 (serializable) :避免以上所有讀問題. Mysql 預設:可重複讀 Oracle 預設:讀已提交

Spring 事務中哪幾種事務傳播行為?
  • 保證同一個事務中 PROPAGATION_REQUIRED 支援當前事務,如果不存在 就新建一個(預設) PROPAGATION_SUPPORTS 支援當前事務,如果不存在,就不使用事務 PROPAGATION_MANDATORY 支援當前事務,如果不存在,丟擲異常
  • 保證沒有在同一個事務中 PROPAGATION_REQUIRES_NEW 如果有事務存在,掛起當前事務,建立一個新的事務 PROPAGATION_NOT_SUPPORTED 以非事務方式執行,如果有事務存在,掛起當前事務 PROPAGATION_NEVER 以非事務方式執行,如果有事務存在,丟擲異常 PROPAGATION_NESTED 如果當前事務存在,則巢狀事務執行

Spring Boot

什麼是springboot
  1. 用來簡化spring應用的初始搭建以及開發過程 使用特定的方式來進行配置(約定大於配置properties或yml檔案)
  2. 建立獨立的spring引用程式 main方法執行
  3. 嵌入的servlet容器(Tomcat) 無需部署war檔案
  4. 簡化maven配置
  5. 自動配置spring新增對應功能starter自動化配置
Spring Boot有哪些優點?
  • 獨立執行
  • 簡化配置
  • 自動配置
  • 無程式碼生成和XML配置
  • 應用監控
  • 上手容易
Spring Boot自動配置的原理

​ 在Spring程式main方法中 新增@SpringBootApplication或者@EnableAutoConfiguration。會自動去maven中讀取每個starter中的spring.factories檔案,該檔案裡配置了所有需要被建立spring容器中的bean。

​ Spring Boot 在啟動時掃描專案所依賴的JAR包,尋找包含spring.factories檔案的JAR,根據spring.factories配置載入AutoConfigure類,根據@Conditional註解的條件,進行自動配置並將Bean注入Spring Context

SpringCloud

什麼是 spring cloud?

​ spring cloud 是一系列框架的有序集合。它利用 spring boot 的開發便利性巧妙地簡化了分散式系統基礎設施的開發,如服務發現註冊、配置中心、訊息匯流排、負載均衡、斷路器、資料監控等,都可以用 spring boot 的開發風格做到一鍵啟動和部署。

spring cloud 的核心元件有哪些?
  • Eureka:服務註冊於發現。
  • Feign:基於動態代理機制,根據註解和選擇的機器,拼接請求 url 地址,發起請求。
  • Ribbon:實現負載均衡,從一個服務的多臺機器中選擇一臺。
  • Hystrix:提供執行緒池,不同的服務走不同的執行緒池,實現了不同服務呼叫的隔離,避免了服務雪崩的問題。
  • Zuul:閘道器管理,由 Zuul 閘道器轉發請求給對應的服務。
spring cloud 斷路器的作用是什麼?

​ 在分散式架構中,斷路器模式的作用也是類似的,當某個服務單元發生故障(類似用電器發生短路)之後,通過斷路器的故障監控(類似熔斷保險絲),向呼叫方返回一個錯誤響應,而不是長時間的等待。這樣就不會使得執行緒因呼叫故障服務被長時間佔用不釋放,避免了故障在分散式系統中的蔓延

​ 當一個服務呼叫另一個服務由於網路原因或自身原因出現問題,呼叫者就會等待被呼叫者的響應 當更多的服務請求到這些資源導致更多的請求等待,發生連鎖效應(雪崩效應) 斷路器有完全開啟狀態:一段時間內 達到一定的次數無法呼叫 並且多次監測沒有恢復的跡象 斷路器完全開啟 那麼下次請求就不會請求到該服務 半開:短時間內 有恢復跡象 斷路器會將部分請求發給該服務,正常呼叫時 斷路器關閉 關閉:當服務一直處於正常狀態 能正常呼叫

什麼是服務熔斷?什麼是服務降級?

​ 在複雜的分散式系統中,微服務之間的相互呼叫,有可能出現各種各樣的原因導致服務的阻塞,在高併發場景下,服務的阻塞意味著執行緒的阻塞,導致當前執行緒不可用,伺服器的執行緒全部阻塞,導致伺服器崩潰,由於服務之間的呼叫關係是同步的,會對整個微服務系統造成服務雪崩

​ 為了解決某個微服務的呼叫響應時間過長或者不可用進而佔用越來越多的系統資源引起雪崩效應就需要進行服務熔斷和服務降級處理。

​ 所謂的服務熔斷指的是某個服務故障或異常一起類似顯示世界中的“保險絲"當某個異常條件被觸發就直接熔斷整個服務,而不是一直等到此服務超時。

​ 服務熔斷就是相當於我們電閘的保險絲,一旦發生服務雪崩的,就會熔斷整個服務,通過維護一個自己的執行緒池,當執行緒達到閾值的時候就啟動服務降級,如果其他請求繼續訪問就直接返回fallback的預設值

什麼是Hystrix?

​ 防雪崩利器,具備服務降級,服務熔斷,依賴隔離,監控(Hystrix Dashboard) 服務降級 雙十一 提示 哎喲喂,被擠爆了。 app秒殺 網路開小差了,請稍後再試。 優先核心服務,非核心服務不可用或弱可用。通過HystrixCommand註解指定。

負載均衡的意義是什麼?

​ 在計算中,負載均衡可以改善跨計算機,計算機叢集,網路連結,中央處理單元或磁碟驅動器等多種計算資源的工作負載分佈。負載均衡旨在優化資源使用,最大吞吐量,最小響應時間並避免任何單一資源的過載。使用多個元件進行負載均衡而不是單個元件可能會通過冗餘來提高可靠性和可用性。負載平衡通常涉及專用軟體或硬體,例如多層交換機或域名系統服務程式。

springcloud如何實現服務的註冊?
  1. 服務釋出時,指定對應的服務名,將服務註冊到 註冊中心(eureka zookeeper)
  2. 註冊中心加@EnableEurekaServer,服務用@EnableDiscoveryClient,然後用ribbon或feign進行服務直接的呼叫發現。
eureka自我保護機制是什麼?

​ 當Eureka Server 節點在短時間內丟失了過多例項的連線時(比如網路故障或頻繁啟動關閉客戶端)節點會進入自我保護模式,保護註冊資訊,不再刪除註冊資料,故障恢復時,自動退出自我保護模式。

什麼是Ribbon?

​ ribbon是一個負載均衡客戶端,可以很好的控制htt和tcp的一些行為。feign預設整合了ribbon。

什麼是feigin?它的優點是什麼?
  1. feign採用的是基於介面的註解
  2. feign整合了ribbon,具有負載均衡的能力
  3. 整合了Hystrix,具有熔斷的能力
Ribbon和Feign的區別?
  1. Ribbon都是呼叫其他服務的,但方式不同。
  2. 啟動類註解不同,Ribbon是@RibbonClient feign的是@EnableFeignClients
  3. 服務指定的位置不同,Ribbon是在@RibbonClient註解上宣告,Feign則是在定義抽象方法的介面中使用@FeignClient宣告。
  4. 呼叫方式不同,Ribbon需要自己構建http請求,模擬http請求然後使用RestTemplate傳送給其他服務,步驟相當繁瑣。Feign需要將呼叫的方法定義成抽象方法即可。
什麼是Spring Cloud Bus?(rabbimq)

​ spring cloud bus 將分散式的節點用輕量的訊息代理連線起來,它可以用於廣播配置檔案的更改或者服務直接的通訊,也可用於監控。 如果修改了配置檔案,傳送一次請求,所有的客戶端便會重新讀取配置檔案。

為什麼要使用MQ訊息中介軟體?

解耦、非同步、削峰

  1. 解耦 傳統模式:

img

​ 傳統模式的缺點:

​ 系統間耦合性太強,如上圖所示,系統A在程式碼中直接呼叫系統B和系統C的程式碼,如果將來D系統接入,系統A還需要修改程式碼,過於麻煩!

中介軟體模式:

中介軟體模式的的優點:

將訊息寫入訊息佇列,需要訊息的系統自己從訊息佇列中訂閱,從而系統A不需要做任何修改。

  1. 非同步 傳統模式:

​ 傳統模式的缺點:

​ 一些非必要的業務邏輯以同步的方式執行,太耗費時間。

中介軟體模式:

img

​ 中介軟體模式的的優點:

​ 將訊息寫入訊息佇列,非必要的業務邏輯以非同步的方式執行,加快響應速度

  1. 削峰 傳統模式

​ 傳統模式的缺點:

​ 併發量大的時候,所有的請求直接懟到資料庫,造成資料庫連線異常

中介軟體模式:

img

​ 中介軟體模式的的優點:

​ 系統A慢慢的按照資料庫能處理的併發量,從訊息佇列中慢慢拉取訊息。在生產中,這個短暫的高峰期積壓是允許的。

使用了訊息佇列會有什麼缺點?

系統可用性降低: 你想啊,本來其他系統只要執行好好的,那你的系統就是正常的。

現在你非要加個訊息佇列進去,那訊息佇列掛了,你的系統不是呵呵了。因此,系統可用性降低

系統複雜性增加: 要多考慮很多方面的問題,比如一致性問題、如何保證訊息不被重複消費,如何保證保證訊息可靠傳輸。

因此,需要考慮的東西更多,系統複雜性增大。

如何保證訊息不被重複消費?

​ 因為網路傳輸等等故障,確認資訊沒有傳送到訊息佇列,導致訊息佇列不知道自己已經消費過該訊息了,再次將該訊息分發給其他的消費者。

如何解決?這個問題針對業務場景來答分以下幾點

  1. 比如,你拿到這個訊息做資料庫的insert操作。

    ​ 那就容易了,給這個訊息做一個唯一主鍵,那麼就算出現重複消費的情況,就會導致主鍵衝突,避免資料庫出現髒資料。

  2. 再比如,你拿到這個訊息做redis的set的操作

    ​ 那就容易了,不用解決。因為你無論set幾次結果都是一樣的,set操作本來就算冪等操作。

  3. 如果上面兩種情況還不行,上大招。

    ​ 準備一個第三方介質,來做消費記錄。以redis為例,給訊息分配一個全域性id,只要消費過該訊息,將以K-V形式寫入redis。那消費者開始消費前,先去redis中查詢有沒消費記錄即可。

什麼是SpringCloudConfig?

​ 在分散式系統中,由於服務數量巨多,為了方便服務配置檔案統一管理,實時更新,所以需要分散式配置中心元件。在Spring Cloud中,有分散式配置中心元件spring cloud config ,它支援配置服務放在配置服務的記憶體中(即本地),也支援放在遠端Git倉庫中。在spring cloud config 元件中,分兩個角色,一是config server,二是config client。

微服務的優缺點分別是什麼?說下你在專案開發中碰到的坑

優點

  • 每一個服務足夠內聚,程式碼容易理解
  • 開發效率提高,一個服務只做一件事
  • 微服務能夠被小團隊單獨開發
  • 微服務是鬆耦合的,是有功能意義的服務
  • 可以用不同的語言開發,面向介面程式設計
  • 易於與第三方整合
  • 微服務只是業務邏輯的程式碼,不會和HTML,CSS或者其他介面組合

​ 開發中,兩種開發模式 前後端分離 全棧工程師

  • 可以靈活搭配,連線公共庫/連線獨立庫

缺點

  • 分散式系統的負責性
  • 多服務運維難度,隨著服務的增加,運維的壓力也在增大
  • 系統部署依賴
  • 服務間通訊成本
  • 資料一致性
  • 系統整合測試
  • 效能監控
微服務之間是如何獨立通訊的
  1. 遠端過程呼叫(Remote Procedure Invocation)

​ 也就是我們常說的服務的註冊與發現

​ 直接通過遠端過程呼叫來訪問別的service。

優點:

 簡單,常見,因為沒有中介軟體代理,系統更簡單

缺點:

​ 只支援請求/響應的模式,不支援別的,比如通知、請求/非同步響應、釋出/訂閱、釋出/非同步響應

​ 降低了可用性,因為客戶端和服務端在請求過程中必須都是可用的

  1. 訊息

​ 使用非同步訊息來做服務間通訊。服務間通過訊息管道來交換訊息,從而通訊。

優點:

​ 把客戶端和服務端解耦,更鬆耦合

​ 提高可用性,因為訊息中介軟體快取了訊息,直到消費者可以消費

​ 支援很多通訊機制比如通知、請求/非同步響應、釋出/訂閱、釋出/非同步響應

缺點:

​ 訊息中介軟體有額外的複雜

Eureka和ZooKeeper都可以提供服務註冊與發現的功能,請說說兩個的區別
  1. ZooKeeper保證的是CP,Eureka保證的是AP

    ZooKeeper在選舉期間註冊服務癱瘓,雖然服務最終會恢復,但是選舉期間不可用的

    Eureka各個節點是平等關係,只要有一臺Eureka就可以保證服務可用,而查詢到的資料並不是最新的

    自我保護機制會導致

    Eureka不再從註冊列表移除因長時間沒收到心跳而應該過期的服務

    Eureka仍然能夠接受新服務的註冊和查詢請求,但是不會被同步到其他節點(高可用)

    當網路穩定時,當前例項新的註冊資訊會被同步到其他節點中(最終一致性)

    Eureka可以很好的應對因網路故障導致部分節點失去聯絡的情況,而不會像ZooKeeper一樣使得整個註冊系統癱瘓

  2. ZooKeeper有Leader和Follower角色,Eureka各個節點平等

  3. ZooKeeper採用過半數存活原則,Eureka採用自我保護機制解決分割槽問題

  4. Eureka本質上是一個工程,而ZooKeeper只是一個程式

SpringBoot和SpringCloud

​ SpringBoot是Spring推出用於解決傳統框架配置檔案冗餘,裝配元件繁雜的基於Maven的解決方案,旨在快速搭建單個微服務 而SpringCloud專注於解決各個微服務之間的協調與配置,服務之間的通訊,熔斷,負載均衡等 技術維度並相同,並且SpringCloud是依賴於SpringBoot的,而SpringBoot並不是依賴與SpringCloud,甚至還可以和Dubbo進行優秀的整合開發

總結:

  • SpringBoot專注於快速方便的開發單個個體的微服務
  • SpringCloud是關注全域性的微服務協調整理治理框架,整合並管理各個微服務,為各個微服務之間提供,配置管理,服務發現,斷路器,路由,事件匯流排等整合服務
  • SpringBoot不依賴於SpringCloud,SpringCloud依賴於SpringBoot,屬於依賴關係
  • SpringBoot專注於快速,方便的開發單個的微服務個體,SpringCloud關注全域性的服務治理框架
微服務架構?

​ 在微服務架構中,需要幾個基礎的服務治理元件,包括服務註冊與發現、服務消費、負載均衡、斷路器、智慧路由、配置管理等,由這幾個基礎元件相互協作,共同組建了一個簡單的微服務系統

​ 在Spring Cloud微服務系統中,一種常見的負載均衡方式是,客戶端的請求首先經過負載均衡(zuul、Ngnix),再到達服務閘道器(zuul叢集),然後再到具體的服。,服務統一註冊到高可用的服務註冊中心叢集,服務的所有的配置檔案由配置服務管理,配置服務的配置檔案放在git倉庫,方便開發人員隨時改配置。

Redis

Redis支援的資料型別?

String字串

​ 格式: set key value

​ string型別是二進位制安全的。意思是redis的string可以包含任何資料。比如jpg圖片或者序列化的物件 。

​ string型別是Redis最基本的資料型別,一個鍵最大能儲存512MB。

Hash(雜湊)

​ 格式: hmset name key1 value1 key2 value2

​ Redis hash 是一個鍵值(key=>value)對集合。

​ Redis hash是一個string型別的field和value的對映表,hash特別適合用於儲存物件。

List(列表)

​ Redis 列表是簡單的字串列表,按照插入順序排序。你可以新增一個元素到列表的頭部(左邊)或者尾部(右邊)

​ 格式: lpush name value

​ 在 key 對應 list 的頭部新增字串元素

​ 格式: rpush name value

​ 在 key 對應 list 的尾部新增字串元素

​ 格式: lrem name index

​ key 對應 list 中刪除 count 個和 value 相同的元素

​ 格式: llen name

​ 返回 key 對應 list 的長度

Set(集合)

​ 格式: sadd name value

​ Redis的Set是string型別的無序集合。

​ 集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度都是O(1)。

zset(sorted set:有序集合)

​ 格式: zadd name score value

​ Redis zset 和 set 一樣也是string型別元素的集合,且不允許重複的成員。

​ 不同的是每個元素都會關聯一個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。

​ zset的成員是唯一的,但分數(score)卻可以重複。

什麼是Redis持久化?Redis有哪幾種持久化方式?優缺點是什麼?

​ 持久化就是把記憶體的資料寫到磁碟中去,防止服務當機了記憶體資料丟失。

​ Redis 提供了兩種持久化方式:RDB(預設) 和AOF

RDB:

​ rdb是Redis DataBase縮寫

​ 功能核心函式rdbSave(生成RDB檔案)和rdbLoad(從檔案載入記憶體)兩個函式

img

AOF:

​ Aof是Append-only file縮寫

img

​ 每當執行伺服器(定時)任務或者函式時flushAppendOnlyFile 函式都會被呼叫, 這個函式執行以下兩個工作

aof寫入儲存:

​ WRITE:根據條件,將 aof_buf 中的快取寫入到 AOF 檔案

​ SAVE:根據條件,呼叫 fsync 或 fdatasync 函式,將 AOF 檔案儲存到磁碟中

兩者比較

  1. aof檔案比rdb更新頻率高,優先使用aof還原資料。
  2. aof比rdb更安全也更大
  3. rdb效能比aof好
  4. 如果兩個都配了優先載入AOF
Redis 有哪些架構模式?講講各自的特點

單機版

img

特點:簡單

問題:

  1. 記憶體容量有限
  2. 處理能力有限
  3. 無法高可用

主從複製

img

​ Redis 的複製(replication)功能允許使用者根據一個 Redis 伺服器來建立任意多個該伺服器的複製品,其中被複制的伺服器為主伺服器(master),而通過複製建立出來的伺服器複製品則為從伺服器(slave)。 只要主從伺服器之間的網路連線正常,主從伺服器兩者會具有相同的資料,主伺服器就會一直將發生在自己身上的資料更新同步 給從伺服器,從而一直保證主從伺服器的資料相同。

特點:

  1. master/slave 角色
  2. master/slave 資料相同
  3. 降低 master 讀壓力在轉交從庫

問題:

​ 無法保證高可用

​ 沒有解決 master 寫的壓力

哨兵

img

​ Redis sentinel 是一個分散式系統中監控 redis 主從伺服器,並在主伺服器下線時自動進行故障轉移。其中三個特性:

​ 監控(Monitoring): Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。

​ 提醒(Notification): 當被監控的某個 Redis 伺服器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程式傳送通知。

​ 自動故障遷移(Automatic failover): 當一個主伺服器不能正常工作時, Sentinel 會開始一次自動故障遷移操作。

特點:

  1. 保證高可用

  2. 監控各個節點

  3. 自動故障遷移

    缺點:

    主從模式,切換需要時間丟資料

    沒有解決 master 寫的壓力

叢集(proxy 型):

img

​ Twemproxy 是一個 Twitter 開源的一個 redis 和 memcache 快速/輕量級代理伺服器; Twemproxy 是一個快速的單執行緒代理程式,支援 Memcached ASCII 協議和 redis 協議。

特點:

  1. 多種 hash 演算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins
  2. 支援失敗節點自動刪除
  3. 後端 Sharding 分片邏輯對業務透明,業務方的讀寫方式和操作單個 Redis 一致

缺點:

​ 增加了新的 proxy,需要維護其高可用。

​ failover 邏輯需要自己實現,其本身不能支援故障的自動轉移可擴充套件性差,進行擴縮容都需要手動干預

叢集(直連型)

img

​ 從redis 3.0之後版本支援redis-cluster叢集,Redis-Cluster採用無中心結構,每個節點儲存資料和整個叢集狀態,每個節點都和其他所有節點連線。

特點:

  1. 無中心架構(不存在哪個節點影響效能瓶頸),少了 proxy 層。
  2. 資料按照 slot 儲存分佈在多個節點,節點間資料共享,可動態調整資料分佈。
  3. 可擴充套件性,可線性擴充套件到 1000 個節點,節點可動態新增或刪除。
  4. 高可用性,部分節點不可用時,叢集仍可用。通過增加 Slave 做備份資料副本
  5. 實現故障自動 failover,節點之間通過 gossip 協議交換狀態資訊,用投票機制完成 Slave到 Master 的角色提升。

缺點:

  1. 資源隔離性較差,容易出現相互影響的情況。
  2. 資料通過非同步複製,不保證資料的強一致性
Redis常用命令?
  • Keys pattern

    *表示區配所有

    以bit開頭的

    檢視Exists key是否存在

  • Set

    設定 key 對應的值為 string 型別的 value。

  • setnx

    設定 key 對應的值為 string 型別的 value。如果 key 已經存在,返回 0,nx 是 not exist 的意思。

    刪除某個key

    第一次返回1 刪除了 第二次返回0

    Expire 設定過期時間(單位秒)

  • Setex

    設定 key 對應的值為 string 型別的 value,並指定此鍵值對應的有效期。

  • Mset

    一次設定多個 key 的值,成功返回 ok 表示所有的值都設定了,失敗返回 0 表示沒有任何值被設定。

  • Getset

    設定 key 的值,並返回 key 的舊值。

  • Mget

    一次獲取多個 key 的值,如果對應 key 不存在,則對應返回 nil。

  • Incr

    對 key 的值做加加操作,並返回新的值。注意 incr 一個不是 int 的 value 會返回錯誤,incr 一個不存在的 key,則設定 key 為 1

  • incrby

    同 incr 類似,加指定值 ,key 不存在時候會設定 key,並認為原來的 value 是 0

  • Decr

    對 key 的值做的是減減操作,decr 一個不存在 key,則設定 key 為-1

  • Decrby

    同 decr,減指定值。

  • Append

    給指定 key 的字串值追加 value,返回新字串值的長度。

  • Strlen

    取指定 key 的 value 值的長度。

  • persist xxx(取消過期時間)

    選擇資料庫(0-15庫)

  • Select 0 //選擇資料庫

  • move age 1//把age 移動到1庫

  • Randomkey隨機返回一個key

  • Rename重新命名

  • Type 返回資料型別

使用過Redis分散式鎖麼,它是怎麼實現的?

​ 先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放

如果在setnx之後執行expire之前程式意外crash或者要重啟維護了,那會怎麼樣?

​ set指令有非常複雜的引數,這個應該是可以同時把setnx和expire合成一條指令來用的

使用過Redis做非同步佇列麼,你是怎麼用的?有什麼缺點?

​ 一般使用list結構作為佇列,rpush生產訊息,lpop消費訊息。當lpop沒有訊息的時候,要適當sleep一會再重試。

缺點:

在消費者下線的情況下,生產的訊息會丟失,得使用專業的訊息佇列如rabbitmq等。

什麼是快取穿透?如何避免?什麼是快取雪崩?何如避免?

快取穿透

​ 一般的快取系統,都是按照key去快取查詢,如果不存在對應的value,就應該去後端系統查詢(比如DB)。一些惡意的請求會故意查詢不存在的key,請求量很大,就會對後端系統造成很大的壓力。這就叫做快取穿透。

如何避免?

  1. 對查詢結果為空的情況也進行快取,快取時間設定短一點,或者該key對應的資料insert了之後清理快取。
  2. 對一定不存在的key進行過濾。可以把所有的可能存在的key放到一個大的Bitmap中,查詢時通過該bitmap過濾。

快取雪崩

​ 當快取伺服器重啟或者大量快取集中在某一個時間段失效,這樣在失效的時候,會給後端系統帶來很大壓力。導致系統崩潰。

如何避免?

  1. 在快取失效後,通過加鎖或者佇列來控制讀資料庫寫快取的執行緒數量。比如對某個key只允許一個執行緒查詢資料和寫快取,其他執行緒等待。
  2. 做二級快取,A1為原始快取,A2為拷貝快取,A1失效時,可以訪問A2,A1快取失效時間設定為短期,A2設定為長期
  3. 不同的key,設定不同的過期時間,讓快取失效的時間點儘量均勻。

相關文章