基礎(chǔ)問題

HashMap的hash算法和尋址算法的優(yōu)化

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

原h(huán)ash值與右移后的hash值進行異或運算(一樣就是0,不一樣就是1)
原h(huán)ash值 1111 1111 1111 1111 1111 1010 0111 1100
右移的值 0000 0000 0000 0000 1111 1111 1111 1111
異或后的值1111 1111 1111 1111 0000 0101 1000 0011 轉(zhuǎn)換成int值返回
重點:從異或運算的結(jié)果看:hash值高位值不變,低位值是原低位值與原高位置的異或后的結(jié)果。
尋址優(yōu)化
找到key所處的數(shù)組索引并不是通過取模來計算的 而是通過(n - 1) & hash來計算的(這是一個數(shù)學(xué)問題,類似于定理,就是當n是2的指數(shù)方那么(n - 1) & hash和取模運算的效果是一樣的,但是前者效率要高很多),這也是為什么HashMap的長度一定是2的n次方的原因。

然后我們再來看(n - 1) & hash 這個確定key在數(shù)組的位置的算法(尋址算法)。n是HashMap的長度,我們想一想n轉(zhuǎn)換為2進制的結(jié)果基本上是高位值全為0,很少有長度大于2的16次方的長度 對不對?這帶來的結(jié)果就是 ,在尋址算法中高位值基本相當于不參與運算。如果不使用上面低位 = 原高位異或原低位的運算的話,那么帶來的結(jié)果是有可能大量的key所定位到的數(shù)組位置是一樣的,使得數(shù)據(jù)在HashMap中的分布不均勻。而經(jīng)過異或運算后的低位值就相當于同時集成了原高位置和原低位值的屬性 換句話說就是高位值和低位值都參與了運算

1111 1111 1111 1111 0000 0101 1000 0011(經(jīng)過優(yōu)化和二進制位運算的新的hash值)
0000 0000 0000 0000 0000 0000 0000 1111 (n-1的2進制值,其高位值大多為0)

HashMap的hash沖突

兩個key,多個key,他們算出來的hash的值,與n-1,與運算之后,發(fā)現(xiàn)定位出來的數(shù)組的位置還是一樣的。這就是hash碰撞也叫hash沖突。
因為有上面的hash沖突,所以數(shù)組的每一個元素是鏈表或者紅黑樹。為什么要引入紅黑樹?因為當鏈表長度太長的時候 遍歷的效率會變低O(n),紅黑樹O(logn)要高一些

HashMap擴容的時候的rehash

HashMap底層是一個數(shù)組,當這個數(shù)組滿了之后,他就會自動進行擴容,變成一個更大的數(shù)組,讓你在里面可以去放更多的元素,下面演示HashMap從16擴容到32的一個例子。
n - 1 0000 0000 0000 0000 0000 0000 0000 1111
hash1 1111 1111 1111 1111 0000 1111 0000 0101
&結(jié)果 0000 0000 0000 0000 0000 0000 0000 0101 = 5(index = 5的位置)

n - 1 0000 0000 0000 0000 0000 0000 0000 1111
hash2 1111 1111 1111 1111 0000 1111 0001 0101
&結(jié)果 0000 0000 0000 0000 0000 0000 0000 0101 = 5(index = 5的位置)
在數(shù)組長度為16的時候,他們兩個hash值的位置是一樣的,用鏈表來處理,出現(xiàn)一個hash沖突的問題,如果數(shù)組的長度擴容之后 = 32,重新對每個hash值進行尋址,也就是用每個hash值跟新數(shù)組的length - 1進行與操作
n-1 0000 0000 0000 0000 0000 0000 0001 1111
hash1 1111 1111 1111 1111 0000 1111 0000 0101
&結(jié)果 0000 0000 0000 0000 0000 0000 0000 0101 = 5(index = 5的位置)

n-1 0000 0000 0000 0000 0000 0000 0001 1111
hash2 1111 1111 1111 1111 0000 1111 0001 0101
&結(jié)果 0000 0000 0000 0000 0000 0000 0001 0101 = 21(index = 21的位置)
判斷二進制結(jié)果中是否多出一個bit的1,如果沒多,那么就是原來的index,如果多了出來,那么就是index + oldCap(21 = 5 + 16),通過這個方式,就避免了rehash的時候,用每個hash對新數(shù)組.length取模,取模性能不高,位運算的性能比較高

ConcurrentHashMap線程安全原理

JDK 1.7時 ConcurrentHashMap的數(shù)據(jù)結(jié)構(gòu)是由一個Segment數(shù)組和多個HashEntry組成,Segment數(shù)組的意義就是將一個大的table分割成多個小的table來進行加鎖,也就是上面的提到的鎖分離技術(shù),而每一個Segment元素存儲的是HashEntry數(shù)組+鏈表,這個和HashMap的數(shù)據(jù)存儲結(jié)構(gòu)一樣

image.png

JDK 1.8以后,優(yōu)化細粒度 采用了 CAS + synchronized 來保證并發(fā)安全性,數(shù)組里每個元素進行put操作,都是有一個不同的鎖,剛開始進行put的時候,如果兩個線程都是在數(shù)組[5]這個位置進行put,這個時候,對數(shù)組[5]這個位置進行put的時候,采取的是CAS的策略,如果你是對數(shù)組里同一個位置的元素進行操作,才會加鎖串行化處理;如果是對數(shù)組不同位置的元素操作,此時大家可以并發(fā)執(zhí)行的

AQS的實現(xiàn)原理是什么

AQS,Abstract Queue Synchronizer,抽象隊列同步器,其實就是CAS加鎖的時候,如果加鎖失敗,就將該線程放到等待隊列里面去,鎖的公平與非公平也就在于這個隊列,公平的意思就是新線程如果隊列不為空 才去嘗試CAS,公平的意思就是新線程過來,二話不說 直接先去嘗試CAS,如果CAS失敗 再去隊列里排隊


image.png
Java內(nèi)存模型的理解
image.png

對象是在堆內(nèi)存中創(chuàng)建的,包括實例變量

happens-before原則

程序次序規(guī)則:一個線程內(nèi),按照代碼順序,書寫在前面的操作先行發(fā)生于書寫在后面的操作

鎖定規(guī)則:一個unLock操作先行發(fā)生于后面對同一個鎖的lock操作,比如說在代碼里有先對一個lock.lock(),lock.unlock(),lock.lock()

volatile變量規(guī)則:對一個volatile變量的寫操作先行發(fā)生于后面對這個volatile變量的讀操作,volatile變量寫,再是讀,必須保證是先寫,再讀

傳遞規(guī)則:如果操作A先行發(fā)生于操作B,而操作B又先行發(fā)生于操作C,則可以得出操作A先行發(fā)生于操作C

線程啟動規(guī)則:Thread對象的start()方法先行發(fā)生于此線程的每個一個動作,thread.start(),thread.interrupt()

線程中斷規(guī)則:對線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測到中斷事件的發(fā)生

線程終結(jié)規(guī)則:線程中所有的操作都先行發(fā)生于線程的終止檢測,我們可以通過Thread.join()方法結(jié)束、Thread.isAlive()的返回值手段檢測到線程已經(jīng)終止執(zhí)行

對象終結(jié)規(guī)則:一個對象的初始化完成先行發(fā)生于他的finalize()方法的開始

這些規(guī)則寫的非常的拗口,晦澀難懂,在面試的時候比如面試官問你,happens-before原則,你必須把8條規(guī)則都背出來,反問,沒有任何一個人可以隨意把這個規(guī)則背出來的

volatile底層是如何基于內(nèi)存屏障保證可見性和有序性的?

底層使用的是大量的內(nèi)存屏障來實現(xiàn)的。具體說不清楚

Spring的 IOC 機制的初步理解
image.png

比如在我們的一個tomcat+servlet的這樣的一個很low的系統(tǒng)里,有幾十個地方,都是直接用MyService myService = new MyServiceImpl(),直接創(chuàng)建、引用和依賴了一個MyServiceImpl這樣的一個類的對象。

假設(shè)現(xiàn)在不想使用MyServiceImpl,我們想用 NewServiceManagerImpl implements MyService,所有的實現(xiàn)邏輯都不同了,此時我們很麻煩,我們需要在很low的系統(tǒng)里,幾十個地方,都去修改對應(yīng)的MyServiceImpl這個類,切換為NewServiceManagerImpl這個類,改動代碼成本很大,改動完以后的測試的成本很大,改動的過程中可能很復(fù)雜,出現(xiàn)一些bug,此時就會很痛苦,歸根結(jié)底,代碼里,各種類之間完全耦合在一起,出現(xiàn)任何一丁點的變動,都需要改動大量的代碼,重新測試,可能還會有bug,
使用Spring之后,將對象的創(chuàng)建權(quán)交給Spring容器,實現(xiàn)組件解耦

說說你對Spring的AOP機制的理解可以嗎?

以事務(wù)為例,就是反射動態(tài)生成代理對象,然后在調(diào)用業(yè)務(wù)代碼前后加上統(tǒng)一的事務(wù)處理機制(執(zhí)行前開啟事務(wù),執(zhí)行后如果正常則提交事務(wù),異常就回滾事務(wù))

cglib動態(tài)代理與jdk動態(tài)代理

優(yōu)先是jdk動態(tài)代理,其次是cglib動態(tài)代理,網(wǎng)上搜一下兩種動態(tài)代理的代碼示例
其實就是動態(tài)的創(chuàng)建一個代理類出來,創(chuàng)建這個代理類的實例對象,在這個里面引用你真正自己寫的類,所有的方法的調(diào)用,都是先走代理類的對象,他負責(zé)做一些代碼上的增強,再去調(diào)用你寫的那個類。
如果你的類是實現(xiàn)了某個接口的,spring aop會使用jdk動態(tài)代理,生成一個跟你實現(xiàn)同樣接口的一個代理類,構(gòu)造一個實例對象出來,jdk動態(tài)代理,他其實是在你的類有接口的時候,就會來使用。
很多時候我們可能某個類是沒有實現(xiàn)接口的,spring aop會改用cglib來生成動態(tài)代理,他是生成你的類的一個子類,他可以動態(tài)生成字節(jié)碼,覆蓋你的一些方法,在方法里加入增強的代碼。

Spring中的Bean是線程安全的嗎?

Spring容器中的bean可以分為5個范圍:
(1)singleton:默認,每個容器中只有一個bean的實例
(2)prototype:為每一個bean請求提供一個實例
一般來說下面幾種作用域,在開發(fā)的時候一般都不會用,99.99%的時候都是用singleton單例作用域
(3)request:為每一個網(wǎng)絡(luò)請求創(chuàng)建一個實例,在請求完成以后,bean會失效并被垃圾回收器回收
(4)session:與request范圍類似,確保每個session中有一個bean的實例,在session過期后,bean會隨之失效
(5)global-session
答案是否定的,絕對不可能是線程安全的,spring bean默認來說,singleton,都是線程不安全的,java web系統(tǒng),一般來說很少在spring bean里放一些實例變量,一般來說他們都是多個組件互相調(diào)用,最終去訪問數(shù)據(jù)庫的。

Spring事務(wù)傳播機制
TCP/IP 的四層網(wǎng)絡(luò)模型和 OSI 七層網(wǎng)絡(luò)模型

1 物理層:
物理層就指的這個,就是怎么把各個電腦給聯(lián)結(jié)起來,形成一個網(wǎng)絡(luò),物理層負責(zé)傳輸0和1的電路信號,是最底的一層。

image.png

2 數(shù)據(jù)鏈路層:
物理層給各個電腦連接起來了,還傳輸最底層的0和1電路信號,這還不行,得定義清楚哪些0和1分為一組,這才能進行通信。所以數(shù)據(jù)鏈路層就干這事兒,定義一下電路信號咋分組,分組的意義是 將數(shù)據(jù)分離,以區(qū)別哪些數(shù)據(jù)是描述性信息(頭),哪些是數(shù)據(jù)實體(數(shù)據(jù))。描述性的信息包括發(fā)送者、接收者和數(shù)據(jù)類型之類的等等。
數(shù)據(jù)從一臺電腦到另外一臺電腦,必須要通過網(wǎng)卡來完成。以太網(wǎng)協(xié)議規(guī)定了,接入網(wǎng)絡(luò)里的所有設(shè)備,都得有個網(wǎng),每個網(wǎng)卡必須得包含一個mac地址,mac地址就是這個網(wǎng)卡的唯一標識,所以在以太網(wǎng)里傳輸數(shù)據(jù)包的時候,必須指定接收者的mac地址才能傳輸數(shù)據(jù)。
但是以太網(wǎng)的數(shù)據(jù)包怎么從一個mac地址發(fā)送到另一個mac地址?這個不是精準推送的,以太網(wǎng)里面,如果一個電腦發(fā)個數(shù)據(jù)包出去,會廣播給局域網(wǎng)內(nèi)的所有電腦設(shè)備的網(wǎng)卡,然后每臺電腦都從數(shù)據(jù)包里獲取接收者的mac地址,跟自己的mac地址對比一下,如果一樣,就說明這是發(fā)給自己的數(shù)據(jù)包。這種廣播的方式,僅僅針對一個子網(wǎng)(局域網(wǎng))內(nèi)的電腦,會廣播,否則一個電腦不能廣播數(shù)據(jù)包給全世界所有的其他電腦吧。
image.png

3 網(wǎng)絡(luò)層:
子網(wǎng)內(nèi)的電腦,通過以太網(wǎng)發(fā)個數(shù)據(jù)包,對局域網(wǎng)內(nèi)的電腦,是廣播出去的。那么怎么知道哪些電腦在一個子網(wǎng)內(nèi)呢?這就得靠網(wǎng)絡(luò)層了。
網(wǎng)絡(luò)層里有IP協(xié)議,IP協(xié)議定義的地址就叫做IP地址。IP地址有IPv4和IPv6兩個版本,目前廣泛使用的是IPv4,是32個二進制數(shù)字組成的,但是一般用4個十進制數(shù)字表示,范圍從0.0.0.0到255.255.255.255之間。
如果幾臺電腦是一個子網(wǎng)的,那么前面的3個十進制數(shù)字一定是一樣的。舉個例子:大家平時做實驗,玩兒虛擬機吧,自己win上開幾個linux虛擬機,你會發(fā)現(xiàn),win上的ip地址可能是192.168.0.103,然后幾個虛擬機的ip地址是192.168.0.182,192.168.0.125,192.168.0.106,類似這樣的。
這個win機器和幾個虛擬機,前面3個十進制數(shù)字都是192.168.0,就代表大家是一個子網(wǎng)內(nèi)的,最后那個數(shù)字是這個子網(wǎng)的不同主機的編號

但是實際上上面就是舉個例子,其實單單從ip地址是看不出來哪些機器是一個子網(wǎng)的,因為從10進制是判斷不出來的。需要通過ip地址的二進制來判斷,結(jié)合一個概念來判斷,叫做子網(wǎng)掩碼
比如說ip地址是192.168.56.1,子網(wǎng)掩碼是255.255.255.0。知道了子網(wǎng)掩碼之后,如果要判斷兩個ip地址是不是一個子網(wǎng)的,就分別把兩個ip地址和自己的子網(wǎng)掩碼進行二進制的與運算,與運算之后,比較一下結(jié)果里面的代表網(wǎng)絡(luò)的那部分,如果一樣,代表是一個子網(wǎng)的。
192.168.56.1和192.168.32.7,判斷是不是一個子網(wǎng)的,拿子網(wǎng)掩碼255.255.255.0,跟兩個ip地址的二進制做與運算,如果兩臺機器的子網(wǎng)掩碼一致的,那么上面說的就成立了
11000000.10101000.00111000.00000001
11111111.11111111.11111111.00000000
如果兩臺電腦在同一個子網(wǎng)內(nèi),那么他們可以直接通信,如果不在呢? 那么這個時候就需要路由器和交換機來登場了。家里的路由器是包含了交換機和路由的兩個功能的。
路由器負責(zé)將多個子網(wǎng)進行連接,每個電腦都可以搞多個網(wǎng)卡的,不是只有一個網(wǎng)卡,一般筆記本電腦都有以太網(wǎng)網(wǎng)卡和wifi網(wǎng)卡,發(fā)送數(shù)據(jù)包的時候要決定走哪個網(wǎng)卡。路由器,其實就是配置了多個網(wǎng)卡的一個專用設(shè)備,可以通過不同的網(wǎng)卡接入不同的網(wǎng)絡(luò)。
網(wǎng)絡(luò)交換機,也是一種設(shè)備,是工作在數(shù)據(jù)鏈路層的,路由器是工作在網(wǎng)路層的。
網(wǎng)絡(luò)交換機是通過mac地址來尋址和傳輸數(shù)據(jù)包的;但是路由器是通過ip地址尋址和傳輸數(shù)據(jù)包的。網(wǎng)絡(luò)交換機主要用在局域網(wǎng)的通信,一般你架設(shè)一個局域網(wǎng),里面的電腦通信是通過數(shù)據(jù)鏈路層發(fā)送數(shù)據(jù)包,通過mac地址來廣播的,廣播的時候就是通過網(wǎng)絡(luò)交換機這個設(shè)備來把數(shù)據(jù)廣播到局域網(wǎng)內(nèi)的其他機器上去的;路由器一般用來讓你連入英特網(wǎng)。

image.png

4 傳輸層:
上面我們大概明白了通過網(wǎng)絡(luò)層的ip地址怎么劃分出來一個一個的子網(wǎng),然后在子網(wǎng)內(nèi)部怎么通過mac地址廣播通信;跨子網(wǎng)的時候,怎么通過ip地址 -> mac地址 -> 交換機 -> 路由器 -> ip地址 -> mac地址 -> 交換機的方式來通過路由器進行通信。
但是這里還有一個問題,就是一臺機器上,是很多個程序用一個網(wǎng)卡進行網(wǎng)絡(luò)通信的,比如說瀏覽器、QQ、這些軟件都用了一個網(wǎng)卡往外面發(fā)送數(shù)據(jù),然后從網(wǎng)卡接收數(shù)據(jù),所以還需要一個端口號的概念,然后那個機器上監(jiān)聽那個端口的程序,就可以提取發(fā)送到這個端口的數(shù)據(jù),知道是自己的數(shù)據(jù)。端口號是065536的范圍內(nèi),01023被系統(tǒng)占用了,別的應(yīng)用程序就用1024以上的端口就ok了。
這里面的通信,就是通過socket來實現(xiàn)的,通過socket就可以基于tcp/ip協(xié)議完成剛才上面說的一系列的比如基于ip地址和mac地址轉(zhuǎn)換和尋址啊,通過路由器通信啊之類的,而且會建立一個端口到另外一個端口的連接。
所以總結(jié)一下:網(wǎng)絡(luò)層,是基于ip協(xié)議,進行主機和主機間的尋址和通信的,然后傳輸層,其實是建立某個主機的某個端口,到另外一個主機的某個端口的連接和通信的。

應(yīng)用層

通過傳輸層的tcp協(xié)議可以傳輸數(shù)據(jù),但是人家收到數(shù)據(jù)之后,怎么來解釋?比如說收到個郵件你怎么處理?收到個網(wǎng)頁你怎么處理?類似這個意思。這就是應(yīng)用層干的事(比如http通信)。這個應(yīng)用層,我們就假設(shè)綜合了會話層、表示層和應(yīng)用層了,3層合成1層,

然后我們看下自己的網(wǎng)絡(luò)設(shè)置,一般包含了ip地址、子網(wǎng)掩碼、網(wǎng)關(guān)地址、DNS地址。前面3個我們其實都知道啥意思了。ip地址和子網(wǎng)掩碼用來劃分子網(wǎng)的,判斷哪些ip地址在一個子網(wǎng)內(nèi)。同時你的ip地址和mac地址關(guān)聯(lián)起來的,唯一定位了你的網(wǎng)卡。網(wǎng)關(guān)地址,你就認為是路由器上的那個網(wǎng)卡的ip地址吧,路由器的網(wǎng)卡也有mac地址,mac地址對應(yīng)了一個ip地址。

DNS地址是啥呢?Domain Name System。因為我們一般定位是通過ip地址+mac地址+端口號來定位一個通信目標的,但是如果在瀏覽器上輸入一個www.baidu.com,咋整?這個時候是先把www.baidu.com發(fā)給DNS服務(wù)器,然后DNS服務(wù)器告訴你www.baidu.com對應(yīng)的ip地址的。
最后總結(jié)一下:
4 層模型的說法
數(shù)據(jù)鏈路層(以太網(wǎng)協(xié)議)->網(wǎng)絡(luò)層(ip協(xié)議)->傳輸層(tcp協(xié)議)->應(yīng)用層(http協(xié)議)
7層模型發(fā)的說法
物理層(網(wǎng)線,光纜)->數(shù)據(jù)鏈路層->網(wǎng)絡(luò)層->傳輸層(這里分為三層,會話層,表示層,應(yīng)用層)->應(yīng)用層

image.png
瀏覽器請求www.baidu.com的全過程

現(xiàn)在我們先假設(shè),我們給電腦設(shè)置了幾個東西:
ip地址:192.168.31.37
子網(wǎng)掩碼:255.255.255.0
網(wǎng)關(guān)地址:192.168.31.1
DNS地址:8.8.8.8

  • 1 這時,我們打開一個瀏覽器,請求www.baidu.com地址,這個時候找DNS服務(wù)器,DNS服務(wù)器解析域名之后,返回一個ip地址,比如172.194.26.108。
  • 2 接著會判斷兩個ip地址是不是一個子網(wǎng)的,用子網(wǎng)掩碼255.255.255.0,對兩個ip地址做與運算,拿到192.168.31.0和172.194.26.0,明顯不是一個子網(wǎng)的。
  • 3 那就得發(fā)送一個數(shù)據(jù)包給網(wǎng)關(guān),其實你就認為是我們的路由器吧,就是192.168.31.1,而且我們是可以拿到網(wǎng)關(guān)ip地址的mac地址的,現(xiàn)在我們從應(yīng)用層出發(fā),通過瀏覽器訪問一個網(wǎng)站,是走應(yīng)用層的http協(xié)議的,并且要把瀏覽器發(fā)出的請求打包成數(shù)據(jù)包,要把哪些東西給放到數(shù)據(jù)包中去呢?如下圖


    image.png
  • 4 接著就是跑傳輸層來了,這個層是tcp協(xié)議,這個tcp協(xié)議會讓你設(shè)置端口,發(fā)送方的端口隨機選一個,接收方的端口一般是默認的80端口
    這個時候,會把應(yīng)用層數(shù)據(jù)包給封裝到tcp數(shù)據(jù)包中去,而且會加一個tcp頭,這個tcp數(shù)據(jù)包是對應(yīng)一個tcp頭的,這個tcp頭里就放了端口號信息。如圖:


    image.png
  • 5 接著跑到網(wǎng)絡(luò)層來了,走ip協(xié)議,這個時候會把tcp頭和tcp數(shù)據(jù)包,放到ip數(shù)據(jù)包里去,然后再搞一個ip頭,ip頭里本機和目標機器的ip地址。
    因為,通過ip協(xié)議,可以判斷說,兩個ip地址不是在一個子網(wǎng)內(nèi)的,所以此時只能將數(shù)據(jù)包先通過以太網(wǎng)協(xié)議廣播到網(wǎng)關(guān)上去,通過網(wǎng)關(guān)再給他發(fā)送出去,如圖:


    image.png
  • 6 接著是數(shù)據(jù)鏈路層,這塊走以太網(wǎng)協(xié)議,這里是把ip頭和ip數(shù)據(jù)包封到以太網(wǎng)數(shù)據(jù)包里去,然后再加一個以太網(wǎng)數(shù)據(jù)包的頭,頭里放了本機網(wǎng)卡mac地址,和網(wǎng)關(guān)的mac地址。但是以太網(wǎng)數(shù)據(jù)包的限制是1500個字節(jié),但是假設(shè)這個時候ip數(shù)據(jù)包都5000個字節(jié)了,那么需要將ip數(shù)據(jù)包切割一下。分成4個包發(fā)送


    image.png

    這4個以太網(wǎng)數(shù)據(jù)包都會通過交換機發(fā)到你的網(wǎng)關(guān)上,然后你的路由器是可以聯(lián)通別的子網(wǎng)的,這個是時候你的路由器就會轉(zhuǎn)發(fā)到別的子網(wǎng)的可能也是某個路由器里去,然后以此類推吧,N多個路由器或者你叫網(wǎng)關(guān)也行,N多個網(wǎng)關(guān)轉(zhuǎn)發(fā)之后,就會跑到百度的某臺服務(wù)器,接收到4個以太網(wǎng)數(shù)據(jù)包。

百度服務(wù)器接收到4個以太網(wǎng)數(shù)據(jù)包以后,根據(jù)ip頭的序號,把4個以太網(wǎng)數(shù)據(jù)包里的ip數(shù)據(jù)包給拼起來,就還原成一個完整的ip數(shù)據(jù)包了。接著就從ip數(shù)據(jù)包里面拿出來tcp數(shù)據(jù)包,再從tcp數(shù)據(jù)包里取出來http數(shù)據(jù)包,讀取出來http數(shù)據(jù)包里的各種協(xié)議內(nèi)容,接著就是做一些處理,然后再把響應(yīng)結(jié)果封裝成htp響應(yīng)報文,封裝在http數(shù)據(jù)包里,再一樣的過程,封裝tcp數(shù)據(jù)包,封裝ip數(shù)據(jù)包,封裝以太網(wǎng)數(shù)據(jù)包,接著通過網(wǎng)關(guān)給發(fā)回去。


image.png
TCP三次握手流程圖?為啥是三次而不是二次或者四次呢?

三次揮手的過程


image.png

為啥不是2次或者4次握手呢


image.png

假設(shè)兩次握手就ok了,要是客戶端第一次握手過去,結(jié)果卡在某個地方了,沒到服務(wù)端;完了客戶端再次重試發(fā)送了第一次握手過去,服務(wù)端收到了,ok了,大家來回來去,三次握手建立了連接。

結(jié)果,尷尬的是,后來那個卡在哪兒的老的第一次握手發(fā)到了服務(wù)器,服務(wù)器直接就返回一個第二次握手,
假設(shè)tcp建立連接只需要2次握手的話,這個時候服務(wù)器開辟了資源準備客戶端發(fā)送數(shù)據(jù)啥的,結(jié)果呢?客戶端根本就不會理睬這個發(fā)回去的二次握手,因為之前都通信過了。那么服務(wù)器干等著???

但是如果是三次握手,那個二次握手發(fā)回去,客戶端發(fā)現(xiàn)根本不對,就會發(fā)送個復(fù)位的報文過去,讓服務(wù)器撤銷開辟的資源,別等著了。

Https通信原理

(1)瀏覽器把自己支持的加密規(guī)則發(fā)送給網(wǎng)站

(2)網(wǎng)站從這套加密規(guī)則里選出來一套加密算法和hash算法,然后把自己的身份信息用證書的方式發(fā)回給瀏覽器,證書里有網(wǎng)站地址、加密公鑰、證書頒發(fā)機構(gòu)

(3)瀏覽器驗證證書的合法性,然后瀏覽器地址欄上會出現(xiàn)一把小鎖;瀏覽器接著生成一串隨機數(shù)密碼,然后用證書里的公鑰進行加密,這塊走的非對稱加密;用約定好的hash算法生成握手消息的hash值,然后用密碼對消息進行加密,然后把所有東西都發(fā)給網(wǎng)站,這塊走的是對稱加密

(4)網(wǎng)站,從消息里面可以取出來公鑰加密后的隨機密碼,用本地的私鑰對消息解密取出來密碼,然后用密碼解密瀏覽器發(fā)來的握手消息,計算消息的hash值,并驗證與瀏覽器發(fā)送過來的hash值是否一致,最后用密碼加密一段握手消息,發(fā)給瀏覽器

(5)瀏覽器解密握手消息,然后計算消息的hash值,如果跟網(wǎng)站發(fā)來的hash一樣,握手就結(jié)束,之后所有的數(shù)據(jù)都會由之前瀏覽器生成的隨機密碼,然后用對稱加密來進行進行加密。


image.png
MyISAM和InnoDB存儲引擎的區(qū)別

myisam,不支持事務(wù),不支持外鍵約束,索引文件和數(shù)據(jù)文件分開,這樣在內(nèi)存里可以緩存更多的索引,對查詢的性能會更好,適用于那種少量的插入,大量查詢的場景。比如報表系統(tǒng)。
innodb,主要特點就是支持事務(wù),走聚簇索引,強制要求有主鍵,支持外鍵約束,高并發(fā)、大數(shù)據(jù)量、高可用等相關(guān)成熟的數(shù)據(jù)庫架構(gòu),分庫分表、讀寫分離、主備切換,全部都可以基于innodb存儲引擎來玩兒,如果真聊到這兒,其實大家就可以帶一帶,說你們用innodb存儲引擎怎么玩兒分庫分表支撐大數(shù)據(jù)量、高并發(fā)的,怎么用讀寫分離支撐高可用和高并發(fā)讀的。

MySQL的索引實現(xiàn)原理?各種索引平時都怎么用?

mysql的索引是怎么實現(xiàn)的?是一顆b+樹,說b+樹之前, 是先來聊聊b-樹是啥

B-樹

定義就不說了,現(xiàn)在又不是大學(xué)考試。反正大概就長上面那個樣子,查找的時候,就是從根節(jié)點開始二分查找。
b+樹是b-樹的變種,mysql里面一般就是b+樹來實現(xiàn)索引,所以b+樹很重要。
b+樹跟b-樹不太一樣的地方在于:
每個節(jié)點的指針上限為2d而不是2d+1。
內(nèi)節(jié)點不存儲data,只存儲key;葉子節(jié)點不存儲指針。
B+樹

myism存儲引擎的索引實現(xiàn)
在myisam存儲引擎的索引中,每個葉子節(jié)點的data存放的是數(shù)據(jù)行的物理地址,比如0x07之類的東西,然后我們可以畫一個數(shù)據(jù)表出來,一行一行的,每行對應(yīng)一個物理地址。
image.png

比如一個查詢:select * from table where id = 15 -> 0x07物理地址(先找到地址再根據(jù)地址去數(shù)據(jù)查詢文件) -> 15,張三,22
myisam最大的特點是數(shù)據(jù)文件和索引文件是分開的,大家看到了么,先是索引文件里搜索,然后到數(shù)據(jù)文件里定位一個行的。
innodb存儲引擎的索引
跟myisam最大的區(qū)別在于說,innodb的數(shù)據(jù)文件本身就是個索引文件,就是主鍵key,然后葉子節(jié)點的data就是那個數(shù)據(jù)的所在行。
image.png

innodb存儲引擎,要求必須有主鍵,會根據(jù)主鍵建立一個默認索引,叫做聚簇索引。innodb的數(shù)據(jù)文件本身同時也是個索引文件,索引存儲結(jié)構(gòu)大致如下:
15,data:0x07,完整的一行數(shù)據(jù),(15,張三,22)
22,data:完整的一行數(shù)據(jù),(22,李四,30)

就是因為這個原因,innodb表是要求必須有主鍵的。innodb存儲引擎下,如果對某個非主鍵的字段創(chuàng)建個索引,那么(對于這個字段形成的B+樹來說)最后那個葉子節(jié)點的值就是主鍵的值,因為可以用主鍵的值到聚簇索引里根據(jù)主鍵值再次查找到數(shù)據(jù),即所謂的回表,例如:
select * from table where name = ‘張三’
先到name的索引里去找,找到張三對應(yīng)的葉子節(jié)點,葉子節(jié)點的data就是那一行的主鍵,id=15,然后再根據(jù)id=15,到數(shù)據(jù)文件里面的聚簇索引(根據(jù)主鍵組織的索引)根據(jù)id=15去定位出來id=15這一行的完整的數(shù)據(jù)

所以這里就明白了一個道理,為啥innodb下不要用UUID生成的超長字符串作為主鍵?因為這么玩兒會導(dǎo)致所有的索引的data都是那個主鍵值,最終導(dǎo)致索引會變得過大,浪費很多磁盤空間。
還有一個道理,一般innodb表里,建議統(tǒng)一用auto_increment自增值作為主鍵值,因為這樣可以保持聚簇索引直接加記錄就可以,如果用那種不是單調(diào)遞增的主鍵值,可能會導(dǎo)致b+樹分裂后重新組織,會浪費時間。

索引的使用規(guī)則

創(chuàng)建聯(lián)合索引 :create index (shop_id,product_id,gmt_create)
(1)全列匹配
這個就是說,你的一個sql里,正好where條件里就用了這3個字段,那么就一定可以用到這個聯(lián)合索引的:
select * from product where shop_id=1 and product_id=1 and gmt_create=’2018-01-01 10:00:00’
(2)最左前綴匹配
這個就是說,如果你的sql里,正好就用到了聯(lián)合索引最左邊的一個或者幾個列表,那么也可以用上這個索引,在索引里查找的時候就用最左邊的幾個列就行了:
select * from product where shop_id=1 and product_id=1,這個是沒問題的,可以用上這個索引的
(3)最左前綴匹配了,但是中間某個值沒匹配
這個是說,如果你的sql里,就用了聯(lián)合索引的第一個列和第三個列,那么會按照第一個列值在索引里找,找完以后對結(jié)果集掃描一遍根據(jù)第三個列來過濾,第三個列是不走索引去搜索的,就是有一個額外的過濾的工作,但是還能用到索引,所以也還好,例如:
select * from product where shop_id=1 and gmt_create=’2018-01-01 10:00:00’
就是先根據(jù)shop_id=1在索引里找,找到比如100行記錄,然后對這100行記錄再次掃描一遍,過濾出來gmt_create=’2018-01-01 10:00:00’的行
這個我們在線上系統(tǒng)經(jīng)常遇到這種情況,就是根據(jù)聯(lián)合索引的前一兩個列按索引查,然后后面跟一堆復(fù)雜的條件,還有函數(shù)啥的,但是只要對索引查找結(jié)果過濾就好了,根據(jù)線上實踐,單表幾百萬數(shù)據(jù)量的時候,性能也還不錯的,簡單SQL也就幾ms,復(fù)雜SQL也就幾百ms??梢越邮艿?。
(4)沒有最左前綴匹配
那就不行了,那就在搞笑了,一定不會用索引,所以這個錯誤千萬別犯
select * from product where product_id=1,這個肯定不行
(5)前綴匹配
這個就是說,如果你不是等值的,比如=,>=,<=的操作,而是like操作,那么必須要是like ‘XX%’這種才可以用上索引,比如說
select * from product where shop_id=1 and product_id=1 and gmt_create like ‘2018%’
(6)范圍列匹配
如果你是范圍查詢,比如>=,<=,between操作,你只能是符合最左前綴的規(guī)則才可以范圍,范圍之后的列就不用索引了
select * from product where shop_id>=1 and product_id=1
這里就在聯(lián)合索引中根據(jù)shop_id來查詢了
(7)包含函數(shù)
如果你對某個列用了函數(shù),比如substring之類的東西,那么那一列不用索引
select * from product where shop_id=1 and 函數(shù)(product_id) = 2
上面就根據(jù)shop_id在聯(lián)合索引中查詢

創(chuàng)建索引時的注意點
1 常用查詢字段適合做索引
2 選擇性高的字段適合做索引 select count(DISTINCT col) / count(*) FROM t_worker,這個值越大越好

事務(wù)隔離級別

(1)讀未提交
(2)讀已提交
(3)可重復(fù)讀
(4)串行化
MySQL是如何實現(xiàn)Read Repeatable的吧?因為一般我們都不修改這個隔離級別,但是你得清楚是怎么回事兒,MySQL是通過MVCC機制來實現(xiàn)的,就是多版本并發(fā)控制,multi-version concurrency control。

當我們使用innodb存儲引擎,會在每行數(shù)據(jù)的最后加兩個隱藏列,一個保存行的創(chuàng)建事務(wù)id,一個保存行的刪除事務(wù)id, 事務(wù)id是mysql自己維護的自增的,全局唯一。如以下數(shù)據(jù)


image.png

查:事務(wù)id=121的事務(wù),在執(zhí)行select * from table where id=1的時候,隱含的條件是WHERE current_trantion_id >= create_trantion_id and (delete_trantion_id >= create_trantion_id OR delete_trantion_id IS NULL)也就是說創(chuàng)建事務(wù)版本id<=當前事務(wù)版本號(事務(wù)id)<刪除事務(wù)版本id(或者刪除事務(wù)版本id為null),也就是說查是查的快照,從而來實現(xiàn)可重復(fù)讀。
改: 修改數(shù)據(jù)的時候不是直接覆蓋,而是創(chuàng)建一條新的數(shù)據(jù),同時將之前數(shù)據(jù)的delete_trantion_id 設(shè)置為當前事務(wù)id,如上面的小李四。
刪:將delete_trantion_id 設(shè)置為當前事務(wù)id。

mysql鎖

mysql 鎖一般分為,表鎖,行鎖,頁鎖
myIsam執(zhí)行查詢的時候,會默認加一個共享鎖,別人只能來看 不能寫。myIsam執(zhí)行寫的時候 會加一個表獨占鎖,別人不能讀也不能寫。
頁鎖一般沒人用。
行鎖,innodb 執(zhí)行增刪改的時候會自動給那一行加行級排它鎖,讀的時候什么鎖都不加,因為innodb 讀的時候是讀快照。
悲觀鎖與樂觀鎖
悲觀鎖的應(yīng)用場景,就比如查出一條數(shù)據(jù),要在內(nèi)存中經(jīng)過一番操作再去進行更新數(shù)據(jù)庫,這個過程中別人是不能更新的。高并發(fā)場景下不能使用悲觀鎖,因為當一個事務(wù)鎖住了數(shù)據(jù),那么其他事務(wù)都會發(fā)生阻塞,會導(dǎo)致大量的事務(wù)發(fā)生積壓拖垮整個系統(tǒng)。悲觀鎖一般用的很少。就是比較版本號是否已經(jīng)改變,如果已經(jīng)改變那么就重新查詢再來執(zhí)行操作。
悲觀鎖語法:select * from user_info where id=1 for update; 這里一定要加篩選條件,否則就升級為表鎖了。另外還要置于事務(wù)環(huán)境下

樂觀鎖:就是不需要提前加鎖,但是需要加一個版本號version,先查出數(shù)據(jù)來,然后更新的時候帶上版本號,UPDATE col = 'xx',version = #{version} +1 user_info WHERE id =1 AND version = #{version}
死鎖: 事務(wù)A持有A鎖的情況下,再去要B鎖資源,事務(wù)B持有B鎖的情況下再去要A鎖資源。死鎖情況下去找一下DBA看日志

SQL調(diào)優(yōu)

互聯(lián)網(wǎng)公司一般都不需要SQL調(diào)優(yōu),基本都是單表查詢,join邏輯放到代碼里面去實現(xiàn)。這樣是為了后面數(shù)據(jù)庫好擴展。
實在調(diào)優(yōu)就explain一下,看看查詢有沒有走索引。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.網(wǎng)絡(luò)軟件 網(wǎng)絡(luò)軟件一般包括網(wǎng)絡(luò)操作系統(tǒng)、網(wǎng)絡(luò)協(xié)議軟件、網(wǎng)絡(luò)管理軟件、網(wǎng)絡(luò)通信軟件、網(wǎng)絡(luò)應(yīng)用軟件五部分組成。其中...
    JYBlog閱讀 1,089評論 0 0
  • IP協(xié)議IP地址路由、DHCP網(wǎng)絡(luò)配置 一、IP協(xié)議 (一)IP協(xié)議的特征: 運行于OSI的網(wǎng)絡(luò)層 面向無連接的協(xié)...
    哈嘍別樣閱讀 966評論 0 0
  • 圖解MySQL索引--B-Tree(B+Tree) java一日一條昨天 看了很多關(guān)于索引的博客,講的大同小異。但...
    你的世界你來定閱讀 165評論 0 0
  • 一、HTTP狀態(tài)碼 狀態(tài)碼分為以下5類 : 1)100-101:信息提示 2)200-206:成功 3)300-3...
    尋找tp閱讀 172評論 0 1
  • 網(wǎng)絡(luò)基礎(chǔ)知識說明: 一、網(wǎng)絡(luò)基礎(chǔ) 美國 貝爾實驗室 linux系統(tǒng)C語言GNU 開源計劃GCC編譯器 gcc ...
    殷長空閱讀 490評論 0 0

友情鏈接更多精彩內(nèi)容