switch支持的類型:byte
, short
, int
, char
, enum
,String(jdk7以後支持)
css
關鍵字:html
final
- 能夠修飾類、函數、變量
- 被修飾的類不能夠被繼承;被修飾的方法不能夠被複寫;被修飾的變量是一個常量,只能賦值一次
- 內部類定義在類中的局部位置上時,只能訪問該局部被final修飾的局部變量
synchronized
volatile
- `保證了不一樣線程對這個變量進行讀取時的可見性,即一個線程修改 了某個變量的值 , 這新值對其餘線程來講是當即可見的 (解決了線程間共享變量)
- 禁止進行指令重排序 ,阻止編譯器對代碼的優化
- 程序併發正確地執行,必需要保證原子性、可見性以及有序性,鎖保證了原子性,而volatile保證可見性和有序性
在catch中return,finally會不會執行?java
Student s = new Student();在內存中作了哪些事情mysql
Java異常有哪幾種?Throwble的子類有哪些redis
序列化serialVersionUID的做用:算法
&和&&的區別:sql
hashCode和equals方法的關係數據庫
設計一個類的時候爲須要重寫equals方法,在重寫equals方法的同時,必須重寫hashCode方法。編程
- 調用equals方法獲得的結果爲true,則兩個對象的hashcode值一定相等
- 調用equals方法獲得的結果爲false,則兩個對象的hashcode值不必定不一樣
- 兩個對象的hashcode值不等,則equals方法獲得的結果一定爲false
- 兩個對象的hashcode值相等,則equals方法獲得的結果未知
什麼是值傳遞和引用傳遞設計模式
強引用,軟引用和弱引用的區別
數組在內存中如何分配
深刻理解Java中的String
String類被final修飾,不能被繼承,而且它的成員方法都默認爲final方法
調用sub,concat,replace
方法,會從新生成了一個新的字符串對象,所以原字符串並無被改變
字符串建立方式:(1)使用""引號建立字符串;(2)使用new關鍵字建立字符串.
代碼示例:
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
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)
,性能遠高於Arraylistadd(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
- 容許null做爲鍵,容許一個或多個鍵所對應的值爲 null
- 默認的初始化大小爲16。以後每次擴充,容量變爲原來的2倍。若是給了容量的初始值不是2的冪次方大小,會將其擴充爲2的冪次方大小
Hashtable
- 相對於HashMap是線程安全的,使用 synchronized 來保證線程安全
- 不容許存在null的鍵值
- 容量初始值默認大小爲11,以後每次擴充,容量變爲原來的2n+1。若是給了容量的初始值,會直接使用該默認值
ConcurrentHashMap
- 在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)
有關線程運行狀態的方法
如何喚醒處於睡眠狀體的線程
如何釋放處於等待中的線程
什麼叫守護線程,用什麼方法實現守護線程(Thread.setDeamon()的含義)
在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護線程)用個比較通俗的好比,任何一個守護線程都是整個JVM中全部非守護線程的保姆:只要當前JVM實例中尚存在任何一個非守護線程沒有結束,守護線程就;只有當最後一個非守護線程結束時,守護線程隨着JVM一同結束工做。JVM內部的實現是若是運行的程序只剩下守護線程的話,程序將終止運行,直接結束。因此守護線程是做爲輔助線程存在的,主要的做用是提供計數等等輔助的功能
如何中止一個線程
- 使用退出標誌,使線程正常退出,也就是當run方法完成後線程終止(標誌用volatile修飾)
- 使用stop方法能夠強行終止正在運行或掛起的線程(不推薦)
- 使用interrupt方法中斷線程,使用interrupt方法來終端線程可分爲兩種狀況:
- 線程處於阻塞狀態,如使用了sleep方法,將拋出一個InterruptedException
- 使用
while(!isInterrupted()){}
來判斷線程是否被中斷,線程將直接退出
ThreadLocal詳解
JDK中的動態代理和CGLIB
五層體系結構(TCP/IP協議簇)
物理層->數據鏈路層->網絡層->傳輸層->應用層
TCP/IP模型
TCP粘包和拆包產生的緣由
TCP粘包和拆包的解決策略
tcp,udp區別
TCP提供面向鏈接的、可靠的數據流傳輸;UDP提供的是非面向鏈接的、不可靠的數據流傳輸
TCP傳輸單位稱爲TCP報文段,UDP傳輸單位稱爲用戶數據報
TCP注重數據安全性,UDP數據傳輸快,由於不須要鏈接等待,少了許多操做,可是其安全性卻通常
tcp三次握手
tcp四次揮手
一次完整的HTTP請求過程
域名解析 --> 發起TCP的3次握手 --> 創建TCP鏈接後發起http請求 --> 服務器響應http請求,瀏覽器獲得html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等) --> 瀏覽器對頁面進行渲染呈現給用戶
講一講長鏈接
在HTTP1.0和HTTP1.1協議中都有對長鏈接的支持。其中HTTP1.0須要在request中增長」Connection: keep-alive「 header纔可以支持,而HTTP1.1默認支持
- http1.0請求與服務端的交互過程: a. 客戶端發出帶有包含一個header
Connection:keep-alive
的請求;b. 服務端接收到這個請求後,根據http1.0
和Connection: keep-alive
判斷出這是一個長鏈接,就會在response的header中也增長Connection: keep-alive
,同是不會關閉已創建的tcp鏈接;c. 客戶端收到服務端的response後,發現其中包含Connection: keep-alive
,就認爲是一個長鏈接,不關閉這個鏈接。並用該鏈接再發送request.轉到a
TCP如何保證可靠傳輸
詳細介紹http
URI和URL的區別
HTTPS和HTTP的區別
https是如何保證數據傳輸的安全
https實際就是在TCP層與http層之間加入了SSL/TLS來爲上層的安全保駕護航,主要用到對稱加密、非對稱加密、證書,等技術進行客戶端與服務器的數據加密傳輸,最終達到保證整個通訊的安全性。
SSL/TLS協議做用:a. 認證用戶和服務器,確保數據發送到正確的客戶機和服務器;b. 加密數據以防止數據中途被竊取;c. 維護數據的完整性,確保數據在傳輸過程當中不被改變
加密算法
-Xms
(最小值)和-Xmx
(最大值)參數設置,一般-Xms
與-Xmx
的值設置成同樣(避免在運行時頻繁調整Heap的大小)-XX:PermSize
和 -XX:MaxPernSize
參數經過類的全限定名來獲取定義此類的二進制字節流,將這個字節流所表明的靜態存儲結構轉換爲方法區的運行時數據結構,在內存中生成一個表明類的數據訪問入口的java.lang.Class對象
<clinit>()
方法,<clinit>()
方法是由編譯器自動收集全部類變量的賦值動做和靜態語句塊的語句合併而成,同一類中的靜態塊與類變量按順序初始化,在同一個加載器下,一個類只會被初始化一次若是一個類加載器收到類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器完成。每一個類加載器都是如此,只有當父加載器在本身的搜索範圍內找不到指定的類時(即ClassNotFoundException),子加載器纔會嘗試本身去加載
概述:比較相鄰的元素。若是第一個比第二個大,就交換他們兩個。 對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。針對全部的元素重複以上的步驟,除了最後一個。持續每次對愈來愈少的元素重複上面的步驟,直到沒有任何一對數字須要比較
概述:在要排序的一組數中,選出最小的一個數與第一個位置的數交換;而後在 剩下的數當中再找最小的與第二個位置的數交換,如此循環到倒數第二個數和最後一個數比較爲止
概述:每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置(從後向前找到合適位置後),直到所有插入排序完爲止
概述:先將整個待排序的記錄序列分割成爲若干子序列分別進行直接插入排序,待整個序列中的記錄「基本有序」時,再對全體記錄進行依次直接插入排序
概述:歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個子序列,每一個子序列是有序的。而後再把有序子序列合併爲總體有序序列
概述:經過一趟排序將待排序記錄分割成獨立的兩部分,一部分全小於選取的參考值,另外一部分全大於選取的參考值。這樣分別對兩部分排序以後順序就能夠排好了
概述:將待排序的序列構形成一個大頂堆。此時,整個序列的最大值就是堆頂的根節點。將它移走(其實就是將其與堆數組的末尾元素交換,此時末尾元素就是最大值),而後將剩餘的n-1個序列從新構形成一個堆,這樣就會獲得n個元素中的次最大值。如此反覆執行,就能獲得一個有序序列了
public ThreadPoolExecutor(int corePoolSize,//核心線程池大小
int maximumPoolSize,//最大線程池大小
long keepAliveTime,//線程池中超過corePoolSize數目的空閒線程最大存活時間;能夠allowCoreThreadTimeOut(true)成爲核心線程的有效時間
TimeUnit unit,//keepAliveTime的時間單位
BlockingQueue<Runnable> workQueue,//阻塞任務隊列
ThreadFactory threadFactory,//線程工廠
RejectedExecutionHandler handler) {//當提交任務數超過限制時
複製代碼
Executors.newCachedThreadPool()
:可緩存線程池
Executors.FixedThreadPool()
:定長線程池
Executors.ScheduledThreadPool()
:定時線程池
Executors.SingleThreadExecutor()
:單線程化的線程池
AbortPolicy
:拒絕任務,且還拋出RejectedExecutionException異常,線程池默認策略CallerRunPolicy
:拒絕新任務進入,若是該線程池尚未被關閉,那麼這個新的任務在執行線程中被調用DiscardOldestPolicy
:若是執行程序還沒有關閉,則位於頭部的任務將會被移除,而後重試執行任務(再次失敗,則重複該過程),這樣將會致使新的任務將會被執行,而先前的任務將會被移除。DiscardPolicy
:沒有添加進去的任務將會被拋棄,也不拋出異常。基本上爲靜默模式Mysql 中 MyISAM 和 InnoDB 的區別有哪些
爲何用自增列做爲主鍵
- 表使用自增主鍵,那麼每次插入新的記錄,記錄就會順序添加到當前索引節點的後續位置,當一頁寫滿,就會自動開闢一個新的頁
- 若是使用非自增主鍵(若是身份證號或學號等),因爲每次插入主鍵的值近似於隨機,所以每次新紀錄都要被插到現有索引頁得中間某個位置,此時MySQL不得不爲了將新記錄插到合適位置而移動數據,甚至目標頁面可能已經被回寫到磁盤上而從緩存中清掉,此時又要從磁盤上讀回來,這增長了不少開銷,同時頻繁的移動、分頁操做形成了大量的碎片,獲得了不夠緊湊的索引結構,後續不得不經過OPTIMIZE TABLE來重建表並優化填充頁面
Mysql中InnoDB的一級索引、二級索引
每一個InnoDB表具備一個特殊的索引稱爲聚簇索引(也叫彙集索引,聚類索引,簇集索引)。若是表上定義有主鍵,該主鍵索引就是聚簇索引。若是未定義主鍵,MySQL取第一個惟一索引(unique)並且只含非空列(NOT NULL)做爲主鍵,InnoDB使用它做爲聚簇索引。若是沒有這樣的列,InnoDB就本身產生一個這樣的ID值,它有六個字節,並且是隱藏的,使其做爲聚簇索引。表中的聚簇索引(clustered index )就是一級索引,除此以外,表上的其餘非聚簇索引都是二級索引,又叫輔助索引(secondary indexes)
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)的等值查詢利用到
查詢的列都在索引的字段中時,查詢的效率更高
僅僅使用索引中的第一列,而且只包含索引第一列的開頭一部分進行查找
解決方法: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 默認把輸入的常量值進行轉換之後才進行檢索
解決辦法:1. 倒過來存儲並創建索引;2. 新增僞hash字段 把字符串轉化爲整型
若是查詢的列剛好是索引的一部分,那麼查詢只須要在索引文件上進行,不須要回行到磁盤,這種查詢,速度極快
在根據條件查詢數據時,若是查詢條件不能用的索引,能夠先查出數據行的id,再根據id去取數據行
排序的字段上加入索引,能夠提升速度
重複索引:在同一列或者相同順序的幾個列創建了多個索引,成爲重複索引,沒有任何意義,刪掉; 冗餘索引:兩個或多個索引所覆蓋的列有重疊,好比對於列m,n ,加索引index m(m),indexmn(m,n),稱爲冗餘索引
在數據表長期的更改過程當中,索引文件和數據文件都會產生空洞,造成碎片。修復表的過程十分耗費資源,能夠用比較長的週期修復表
//清理方法
alert table xxx engine innodb;
//或
optimize table xxx;
複製代碼
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 where: where 語句的值老是false,不可用,不能用來獲取任何元素
distinct: 優化distinct操做,在找到第一匹配的元組後即中止找一樣值的動做
複製代碼
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);
}
複製代碼
參考文章:
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();
}
複製代碼
單線程:指的是網絡請求模塊使用了一個線程(因此不需考慮併發安全性),即一個線程處理全部網絡請求,其餘模塊仍用了多個線程
redis可以快速執行緣由
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
類型的數據,結構上還須要兩個額外的指針prev
和next
。因此 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
的方式不同,由於Java
的 HashMap
在字典很大時,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(有序列表):至關於 SortedSet
和HashMap
的結合體,一方面它是一個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隊列思路
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;
}
}
複製代碼
漏斗限流
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
}
}
複製代碼
# 建立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,單位秒)
複製代碼
Redis主從複製
過程原理:
- 當從庫和主庫創建MS關係後,會向主數據庫發送SYNC命令
- 主庫接收到SYNC命令後會開始在後臺保存快照(RDB持久化過程),並將期間接收到的寫命令緩存起來
- 當快照完成後,主Redis會將快照文件和全部緩存的寫命令發送給從Redis
- 從Redis接收到後,會載入快照文件而且執行收到的緩存的命令以後,主Redis每當接收到寫命令時就會將命令發送從Redis,從而保證數據的一致
redis兩種持久化方式的優缺點