并發安全就是程序在并發的情況下執行的結果都是正確的;
Go中數據類型分為兩大類:
字節型、布爾型、整型、浮點型取決于操作系統指令值,在64位的指令集架構中可以由一條機器指令完 成,不存在被細分為更小的操作單位,所以這些類型的并發賦值是安全的,但是這個也跟操作系統的位 數有關,比如int64在32位操作系統中,它的高32位和低32位是分開賦值的,此時是非并發安全的。
復數類型、字符串、結構體、數組,切片,字典,通道,接口, 這些底層都是struct,不同成員的賦值 都不是一起的,所以都不是并發安全的。
單例模式的作用是確保無論對象被實例化多少次,全局都只有一個實例存在。根據這一特性,我們可以將其應用到全局唯一性配置、數據庫連接對象、文件訪問對象等。
餓漢式實現單例模式非常簡單,直接看代碼:
package singletontype singleton struct{}var instance = &singleton{}func GetSingleton() *singleton { return instance}
singleton 包在被導入時會自動初始化 instance 實例,使用時通過調用 singleton.GetSingleton() 函數即可獲得 singleton 這個結構體的單例對象。
這種方式的單例對象是在包加載時立即被創建,所以這個方式叫作餓漢式。與之對應的另一種實現方式叫作懶漢式,懶漢式模式下實例會在第一次被使用時被創建。
需要注意的是,盡管餓漢式實現單例模式的方式簡單,但大多數情況下并不推薦。因為如果單例實例化時初始化內容過多,會造成程序加載用時較長。
接下來我們再來看下如何通過懶漢式實現單例模式:
package singletontype singleton struct{}var instance *singletonfunc GetSingleton() *singleton { if instance == nil { instance = &singleton{} } return instance}
相較于餓漢式的實現,懶漢式將實例化 singleton 結構體部分的代碼移到了 GetSingleton() 函數內部。這樣能夠將對象實例化的步驟延遲到 GetSingleton() 第一次被調用時。
不過通過 instance == nil 的判斷來實現單例并不十分可靠,如果有多個 goroutine 同時調用 GetSingleton() 就無法保證并發安全。
Go 的內建 map 是不支持并發寫操作的,原因是 map 寫操作不是并發安全的,當你嘗試多個 Goroutine 操作同一個 map,會產生報錯:fatal error: concurrent map writes。
因此官方另外引入了 sync.Map 來滿足并發編程中的應用。
sync.Map 的實現原理可概括為:
type Map struct { // 加鎖作用,保護 dirty 字段 mu Mutex // 只讀的數據,實際數據類型為 readOnly read atomic.Value // 最新寫入的數據 dirty map[interface{}]*entry // 計數器,每次需要讀 dirty 則 +1 misses int}
其中 readOnly 的數據結構為:
type readOnly struct { // 內建 map m map[interface{}]*entry // 表示 dirty 里存在 read 里沒有的 key,通過該字段決定是否加鎖讀 dirty amended bool}
entry 數據結構則用于存儲值的指針:
type entry struct { p unsafe.Pointer // 等同于 *interface{}}
屬性 p 有三種狀態:
Map 常用的有以下方法:
redis的基本數據結構有: 1、String(字符串);2、Hash(哈希);3、List(列表);4、Set(集合);5、zset(有序集合)。
String 類型是 Redis 中最基本、最常用的數據類型,甚至被很多用戶當成 Redis 少數的數據類型去使用。String 類型在 Redis 中是二進制安全(binary safe)的,這意味著 String 值關心二進制的字符串,不關心具體格式,你可以用它存儲 json 格式或 JPEG 圖片格式的字符串。
應用:
Hash的數據結構我們可以簡單理解為java中的 Map,這種結構就特別適合存儲對象,上面的String的類型確實也可以存儲對象,但每次修改對象中的某一個屬性,都要拿出整個json字符串在修改這個屬性,之后在重新插入,而hash的接口特點讓我們可以只修改該對象的某一個屬性。
hash數據類型在存儲上述類型的數據時具有比 String 類型更靈活、更快的優勢,具體的說,使用 String 類型存儲,必然需要轉換和解析 json 格式的字符串,即便不需要轉換,在內存開銷方面,還是 hash 占優勢。
應用:
List類型是按照插入順序排序的字符串鏈表,一個列表非常多可以存儲2^32-1個元素。我們可以簡單理解為就相當于java中的LinkesdList。和數據結構中的普通鏈表一樣,我們可以在其頭部(left)和尾部(right)添加新的元素。在插入時,如果該鍵并不存在,Redis將為該鍵創建一個新的鏈表。與此相反,如果鏈表中所有的元素均被移除,那么該鍵也將會被從數據庫中刪除。
應用:
Redis 中的 set和Java中的HashSet 有些類似,它內部的鍵值對是無序的、少數的。它的內部實現相當于一個特殊的字典,字典中所有的value都是一個值 NULL。當集合中最后一個元素被移除之后,數據結構被自動刪除,內存被回收。
應用:
Sorted-Sets中的每一個成員都會有一個分數(score)與之關聯,Redis正是通過分數來為集合中的成員進行從小到大的排序。成員是少數的,但是分數(score)卻是可以重復的。
應用: 作為有序的,不可重復的列表,可以做一些排行榜相關的場景:
當大量緩存數據在同一時間過期(失效)或者 Redis 故障宕機時,如果此時有大量的用戶請求,都無法在 Redis 中處理,于是全部請求都直接訪問數據庫,從而導致數據庫的壓力驟增,嚴重的會造成數據庫宕機,從而形成一系列連鎖反應,造成整個系統崩潰,這就是緩存雪崩
我們的業務通常會有幾個數據會被頻繁地訪問,比如秒殺活動,這類被頻地訪問的數據被稱為熱點數據。
如果緩存中的某個熱點數據過期了,此時大量的請求訪問了該熱點數據,就無法從緩存中讀取,直接訪問數據庫,數據庫很容易就被高并發的請求沖垮,這就是緩存擊穿。
當發生緩存雪崩或擊穿時,數據庫中還是保存了應用要訪問的數據,一旦緩存恢復相對應的數據,就可以減輕數據庫的壓力,而緩存穿透就不一樣了。
當用戶訪問的數據,既不在緩存中,也不在數據庫中,導致請求在訪問緩存時,發現緩存缺失,再去訪問數據庫時,發現數據庫中也沒有要訪問的數據,沒辦法構建緩存數據,來服務后續的請求。那么當有大量這樣的請求到來時,數據庫的壓力驟增,這就是緩存穿透的問題。
約束主要有:主鍵約束、外鍵約束、非空約束、檢査約束(bentwen and ,大于、小于、等于、不等于)、唯一約束。
B+樹是多叉樹結構,每個結點都是一個16k的數據頁,能存放較多索引信息,所以扇出很高。三層左右就可以存儲2kw左右的數據(知道結論就行,想知道原因可以看之前的文章)。也就是說查詢一次數據,如果這些數據頁都在磁盤里,那么最多需要查詢三次磁盤IO。
跳表是鏈表結構,一條數據一個結點,如果最底層要存放2kw數據,且每次查詢都要能達到二分查找的效果,2kw大概在2的24次方左右,所以,跳表大概高度在24層左右。最壞情況下,這24層數據會分散在不同的數據頁里,也即是查一次數據會經歷24次磁盤IO。
因此存放同樣量級的數據,B+樹的高度比跳表的要少,如果放在mysql數據庫上來說,就是磁盤IO次數更少,因此B+樹查詢更快。
而針對寫操作,B+樹需要拆分合并索引數據頁,跳表則獨立插入,并根據隨機函數確定層數,沒有旋轉和維持平衡的開銷,因此跳表的寫入性能會比B+樹要好。
HTTP2.0(Hypenext TransferProtocol version2)是超文本傳輸協議的第二版,HTTP2.0相比于HTTP1x,大幅度的提升了web性能,同時向下兼容HTTP1.X協議版 本。
主要核心優勢有
1、采用二進制格式傳輸數據,而非htp1.1文本格式,二進制格式在協議的解析和優化擴展上帶來了跟多的優勢和可能
2、對消息頭采用Hpack進行壓縮傳輸,能夠節省消息頭占用的網絡流量,htp1.1每次請求,都會攜帶大量冗余的頭信息,浪費了很多寬帶資源,
3、異步連接多路復用
4、Server Push,服務器端能夠更快的把資源推送到客戶端。
5、保持與HTTP 1.1語義的向后兼容性也是該版本的一個關鍵
本文轉載自微信公眾號「王中陽Go」,作者「王中陽Go」,可以通過以下二維碼關注。
轉載本文請聯系「王中陽Go」公眾號。
本文鏈接:http://www.tebozhan.com/showinfo-26-98859-0.html阿里校招面試,我來瞅瞅怎么回事
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com