go语言总结
Q:context作用,原理,超時控制
A: golang context的理解,context主要用于父子任務之間的同步取消信號,本質上是一種協程調度的方式。另外在使用context時有兩點值得注意:上游任務僅僅使用context通知下游任務不再需要,但不會直接干涉和中斷下游任務的執行,由下游任務自行決定后續的處理操作,也就是說context的取消操作是無侵入的;context是線程安全的,因為context本身是不可變的(immutable),因此可以放心地在多個協程中傳遞使用。
Q:切片和數組區別
A: 數組是內置(build-in)類型,是一組同類型數據的集合,它是值類型,通過從0開始的下標索引訪問元素值。在初始化后長度是固定的,無法修改其長度。當作為方法的參數傳入時將復制一份數組而不是引用同一指針。數組的長度也是其類型的一部分,通過內置函數len(array)獲取其長度。
注意:和C中的數組相比,又是有一些不同的
1. Go中的數組是值類型,換句話說,如果你將一個數組賦值給另外一個數組,那么,實際上就是將整個數組拷貝一份
2. 如果Go中的數組作為函數的參數,那么實際傳遞的參數是一份數組的拷貝,而不是數組的指針。這個和C要區分開。因此,在Go中如果將數組作為函數的參數傳遞的話,那效率就肯定沒有傳遞指針高了。
3. array的長度也是Type的一部分,這樣就說明[10]int和[20]int是不一樣的。array的結構用圖示表示是這樣的:
?[ len|...]?
len表示數組的長度,后面的int儲存的是實際數據?
與數組相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。切片中有兩個概念:一是len長度,二是cap容量,長度是指已經被賦過值的最大下標+1,可通過內置函數len()獲得。容量是指切片目前可容納的最多元素個數,可通過內置函數cap()獲得。切片是引用類型,因此在當傳遞切片時將引用同一指針,修改值將會影響其他的對象。?
切片可以通過數組來初始化,也可以通過內置函數make()初始化 .初始化時len=cap,在追加元素時如果容量cap不足時將按len的2倍擴容。
Q:channel關閉阻塞問題,goroutine如何調度,gopark是怎么回事?
channel關閉阻塞問題:https://blog.csdn.net/sgsgy5/article/details/82054902
goroutine如何調度參考:https://www.cnblogs.com/wdliu/p/9272220.html
https://jingwei.link/2019/05/26/golang-routine-scheduler.html
gopark參考:https://blog.csdn.net/u010853261/article/details/85887948
PMG模型描述,誰創建的PMG,runtime是怎么個東西,怎么啟動第一個goroutine
A: golang CPS并發模型和PMG模型的理解。
參考:https://www.jianshu.com/p/36e246c6153d
Q:Golang 探索對Goroutine的控制方法
A:參考:https://www.cnblogs.com/tr3e/p/7995689.html
Q:go逃逸分析怎么回事,內存什么時候棧分配什么時候堆分配
A:?為什么要逃逸分析
C/C++中動態分配的內存需要我們手動釋放,導致猿們平時在寫程序時,如履薄冰。這樣做有他的好處:程序員可以完全掌控內存。但是缺點也是很多的:經常出現忘記釋放內存,導致內存泄露。所以,很多現代語言都加上了垃圾回收機制。
Go的垃圾回收,讓堆和棧對程序員保持透明。真正解放了程序員的雙手,讓他們可以專注于業務,“高效”地完成代碼編寫。把那些內存管理的復雜機制交給編譯器,而程序員可以去享受生活。
逃逸分析這種“騷操作”把變量合理地分配到它該去的地方,“找準自己的位置”。即使你是用new申請到的內存,如果我發現你竟然在退出函數后沒有用了,那么就把你丟到棧上,畢竟棧上的內存分配比堆上快很多;反之,即使你表面上只是一個普通的變量,但是經過逃逸分析后發現在退出函數之后還有其他地方在引用,那我就把你分配到堆上。
如果變量都分配到堆上,堆不像棧可以自動清理。它會引起Go頻繁地進行垃圾回收,而垃圾回收會占用比較大的系統開銷(占用CPU容量的25%)。
堆適合不可預知大小的內存分配。但是為此付出的代價是分配速度較慢,而且會形成內存碎片。棧內存分配則會非???。棧分配內存只需要兩個CPU指令:“PUSH”和“RELEASE”,分配和釋放;而堆分配內存首先需要去找到一塊大小合適的內存塊,之后要通過垃圾回收才能釋放。
通過逃逸分析,可以盡量把那些不需要分配到堆上的變量直接分配到棧上,堆上的變量少了,會減輕分配堆內存的開銷,同時也會減少gc的壓力,提高程序的運行速度。
逃逸分析如何完成
Go逃逸分析最基本的原則是:如果一個函數返回對一個變量的引用,那么它就會發生逃逸。
簡單來說,編譯器會分析代碼的特征和代碼生命周期,Go中的變量只有在編譯器可以證明在函數返回后不會再被引用的,才分配到棧上,其他情況下都是分配到堆上。
Go語言里沒有一個關鍵字或者函數可以直接讓變量被編譯器分配到堆上,相反,編譯器通過分析代碼來決定將變量分配到何處。
對一個變量取地址,可能會被分配到堆上。但是編譯器進行逃逸分析后,如果考察到在函數返回后,此變量不會被引用,那么還是會被分配到棧上。
簡單來說,編譯器會根據變量是否被外部引用來決定是否逃逸:
1)如果函數外部沒有引用,則優先放到棧中;
2) 如果函數外部存在引用,則必定放到堆中;
針對第一條,可能放到堆上的情形:定義了一個很大的數組,需要申請的內存過大,超過了棧的存儲能力。
Q:sync.Map實現原理,適用的場景
A:go 1.9 官方提供sync.Map 來優化線程安全的并發讀寫的map。該實現也是基于內置map關鍵字來實現的。
這個實現類似于一個線程安全的 map[interface{}]interface{} . 這個map的優化主要適用了以下場景:
(1)給定key的鍵值對只寫了一次,但是讀了很多次,比如在只增長的緩存中;
(2)當多個goroutine讀取、寫入和覆蓋的key值不相交時。
參考:https://blog.csdn.net/u010853261/article/details/103848666
Q:go語言有什么優點和缺點
A: 優勢:容易學習,生產力,并發,動態語法。劣勢:包管理,錯誤處理,缺乏框架。
Q:Go框架用過哪些,有看源碼嗎
A: 優勢:beego,go-micro,gin等
Q:Go GC算法,三色標記法描述
A: 參考:https://www.jianshu.com/p/12544c0ad5c1
Q:Go內存模型(tcmalloc)
A:tcmalloc是線程緩存的malloc,實現了高效的多線程內存管理,用于替代系統的內存分配相關的函數
參考:https://blog.csdn.net/zhghost/article/details/104096921
Q:行列都是有序的二維數組,查找k是否存在,時間復雜度
1 3 5 7 9
3 5 7 9 11
4 6 8 10 12
A:二分查找:O(log2(max(m,n)))
Q:有序數組,有2N+1個數,其中N個數成對出現,僅1個數單獨出現,找出那個單獨出現的數.,時間復雜度
1,1,2,2,3,4,4,5,5,6,6,7,7
答案為3
A: O(log2(2N))二分查找,查找中間位置的數相等值是在左邊還是右邊?左邊則再左子數組繼續查找,右邊則在右子數組繼續查找。
Q:100億個數求top100,時間復雜度
A:分組查找或bitmap,參考:https://blog.csdn.net/zyq522376829/article/details/47686867
Q:100億個數和100億個數求交集,時間復雜度
A: 全排列問題,自己找去
2、linux,操作系統
Q:Select/epoll,IO多路復用,底層數據結構,epoll的幾個函數,兩種模式
A: Select/epoll 問題,網上很多
Q:搶占式調度是什么回事
A: 進程優先級和時間分片等方面理解
參考:https://tiancaiamao.gitbooks.io/go-internals/content/zh/05.5.html
https://gocn.vip/topics/9884?locale=zh-CN
Q:用戶態和內核態
A: 系統態(內核態),操作系統在系統態運行——運行操作系統程序
用戶態(也稱為目態),應用程序只能在用戶態運行——運行用戶程序
Q:B+樹和B樹區別,優缺點
A:B樹每個節點都存儲key和data,所有節點組成這棵樹,并且葉子節點指針為null。只有葉子節點存儲data,葉子節點包含了這棵樹的所有鍵值,葉子節點不存儲指針,順序訪問指針,也就是每個葉子節點增加一個指向相鄰葉子節點的指針。
Q:B樹和二叉查找樹或者紅黑色區別
A:基礎數據結構問題
Q:聚簇索引什么特點,為什么這樣,順序查詢的實現,回表查詢,聯合索引特性
A:
- 聚簇索引:將數據存儲與索引放到了一塊,找到索引也就找到了數據
- 非聚簇索引:將數據存儲于索引分開結構,索引結構的葉子節點指向了數據的對應行,myisam通過key_buffer把索引先緩存到內存中,當需要訪問數據時(通過索引訪問數據),在內存中直接搜索索引,然后通過索引找到磁盤相應數據,這也就是為什么索引不在key buffer命中時,速度慢的原因
Q:大表分頁查詢,10億行數據,查找第N頁數據,怎么優化
A: 根據查詢的頁數和查詢的記錄數可以算出查詢的id的范圍,可以使用 id between and 來查詢。
Q:悲觀鎖和樂觀鎖,mysql相關鎖說一下
A:
樂觀鎖( Optimistic Locking):對加鎖持有一種樂觀的態度,即先進行業務操作,不到最后一步不進行加鎖,"樂觀"的認為加鎖一定會成功的,在最后一步更新數據的時候再進行加鎖。
悲觀鎖(Pessimistic Lock):悲觀鎖對數據加鎖持有一種悲觀的態度。因此,在整個數據處理過程中,將數據處于鎖定狀態。悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)。
Q:如何分庫分表
A:
1)垂直分表
也就是“大表拆小表”,基于列字段進行的。一般是表中的字段較多,將不常用的, 數據較大,長度較長(比如text類型字段)的拆分到“擴展表“。一般是針對那種幾百列的大表,也避免查詢時,數據量太大造成的“跨頁”問題。
2)垂直分庫
垂直分庫針對的是一個系統中的不同業務進行拆分,比如用戶User一個庫,商品Producet一個庫,訂單Order一個庫。切分后,要放在多個服務器上,提高性能。
3)水平分庫分表
將單張表的數據切分到多個服務器上去,每個服務器具有相應的庫與表,只是表中數據集合不同。水平分庫分表能夠有效的緩解單機和單庫的性能瓶頸和壓力,突破IO、連接數、硬件資源等的瓶頸。
4、redis
Q:幾種數據結構(list,set,zset,geohash,bitmap)實現原理
A:參考:https://www.cnblogs.com/MouseDong/p/11134039.html
Q:pipline用來干嘛
A:pipeline的作用是將一批命令進行打包,然后發送給服務器,服務器執行完按順序打包返回。
Q:事務
A: redis事務就是一次性、順序性、排他性的執行一個隊列中的一系列命令。
Q:備份(aof/rdb)原理,哪些參數可調
A:RDB是根據指定的規則定時將內存中的數據備份到硬盤上,AOF是在每次執行命令后命令本身記錄下來,所以RDB的備份文件是一個二進制文件,而AOF的備份文件是一個文本文件。
after 900 sec (15 min) if at least 1 key changed 900秒(15分鐘)內至少1個key值改變(則進行數據庫保存--持久化)after 300 sec (5 min) if at least 10 keys changed 300秒(5分鐘)內至少10個key值改變(則進行數據庫保存--持久化)after 60 sec if at least 10000 keys changed 60秒(1分鐘)內至少10000個key值改變(則進行數據庫保存--持久化) save 900 1 save 300 10 save 60 10000AOF有3種方式將操作命令存入AOF文件
1. appendfsync no 不保存
只執行WHRITE操作,SAVE操作會被略過,只有在Redis被關閉、AOF功能被關閉、系統的寫緩存被刷新(如緩存已被寫滿)這三種情況,SAVE操作會被執行,但是這三種情況都會引起Redis主進程阻塞
2. appendfsync everysec 每秒鐘保存一次
這種模式中,SAVE原則上每隔一秒鐘就會執行一次,具體的執行周期和文件寫入、保存時,Redis所處的狀態有關,此模式下SAVE操作由后臺子線程調用,不會引起服務器主進程的阻塞
3. appendfsync always 每執行一個命令保存一次
在這種模式下,每執行一個命令,WRITE和SAVE都會被執行,且SAVE操作會阻塞主進程
Q:網絡模型,為什么單線程能hold住10萬QPS
A:網絡模型,I/O復用,Reactor 設計模式,參考:https://blog.csdn.net/ligupeng7929/article/details/90742578。
Q:熱點key怎么處理
A:1、熱key加載到系統內存中,直接從系統內存中取,而不走到redis層。
2、redis集群,熱點備份分布到集群中,避免單臺redis集中訪問。
Q:一致性hash解決什么問題
A:redis集群和負載均衡
Q:redis集群(主從,高可用,擴展節點)
A:參考:https://www.jianshu.com/p/7d5fbf90bcd7
5、kafka相關
Q:消息是否按照時間有序,kafka分區的數據是否有序,如何保證有序
A:不保證按時間有序,主題在單個分區是有序的。
如何保證有序?kafka topic 只設置一個分區,或者producer將消息發送到指定分區
Q:Kafka為什么吞吐量高
A:
1)順序讀寫
kafka的消息是不斷追加到文件中的,這個特性使kafka可以充分利用磁盤的順序讀寫性能,順序讀寫不需要硬盤磁頭的尋道時間,只需很少的扇區旋轉時間,所以速度遠快于隨機讀寫。
2)零拷貝
利用Linux kernel"零拷貝(zero-copy)"系統調用機制,就是跳過“用戶緩沖區”的拷貝,建立一個磁盤空間和內存的直接映射,數據不再復制到“用戶態緩沖區”。
3)分區
kafka中的topic中的內容可以被分為多分區存在,每個分區又分為多個段,所以每次操作都是針對一小部分做操作,很輕便,并且增加并行操作的能力。
4)批量發送
kafka允許進行批量發送消息,producter發送消息的時候,可以將消息緩存在本地,等到了固定條件發送到kafka
等消息條數到固定條數,一段時間發送一次。
5)數據壓縮
Kafka還支持對消息集合進行壓縮,Producer可以通過GZIP或Snappy格式對消息集合進行壓縮
壓縮的好處就是減少傳輸的數據量,減輕對網絡傳輸的壓力
Q:kafka的存儲模型
A:Kafka一個Topic可以有多個Partition,多個線程,每個線程負責一個Partition進行讀寫
每個Paratition可以有多個LogSegment,每個LogSegment文件包括一個日志數據文件和兩個索引文件(偏移量索引文件和消息時間戳索引文件)。
以上紅字部分就是他的存儲模型
其中,每個LogSegment中的日志數據文件大小均相等(該日志數據文件的大小可以通過在Kafka Broker的config/server.properties配置文件的中的“log.segment.bytes”進行設置,默認為1G大小(1073741824字節),在順序寫入消息時如果超出該設定的閾值,將會創建一組新的日志數據和索引文件
Kafka的索引文件是采用稀疏索引的方式,每隔一定的字節數建立了一條索引
所以在添加數據時,如果還沒有LogSegment,就會建第一個LogSegment,然后把數據順序寫在該LogSegment的日志數據文件里,然后再把索引加到偏移量索引文件里去
偏移量索引文件的每條索引由offset和position組成,每個索引條目可以唯一確定在各個分區數據文件的一條消息
在查找數據時
先根據Position定位到LogSegment,再根據Position和offset在logSegment找到日志數據文件中對應數據的位置
原理就是這樣。
Q:Kafka消費者多個group消費同一個topic,會重復消費嗎?
A:不會。
8、項目問題
Q:遇到過內存溢出嗎?怎么解決
A:主要了解有沒有處理過內存泄漏導致的問題,C/C++定位內存泄漏問題;Golang和JAVA主要與GC的工作機制有關,堆內存一直增長,導致應用內存溢出等。
Q:布隆過濾器怎么設置m,n,k的值,怎么合理安排key(用戶和item越來越多,怎么保證內存不會爆)
A:m,n,k 網上有實踐經驗,可參考。item越來越多的話,進行item的拆分,拆分本質是不要將 Hash(Key) 之后的請求分散在多個節點的多個小 bitmap 上,而是應該拆分成多個小 bitmap 之后,對一個 Key 的所有哈希函數都落在這一個小 bitmap 上。
Q:服務雪崩怎么處理,怎么解決保證不影響線上
A:限流,降級,熔斷方面措施,結合后端系統架構闡述,如網關的限流和快速失敗。
Q:redis和mysql數據一致性怎么保證
A:重點考慮業務邏輯上寫和數據的流程(異常和錯誤處理等),結合MQ做異步重試處理。
Q:分布式鎖應用場景,哪些坑
A:鎖過期了,業務還沒執行完;分布式鎖,redis主從同步的坑;獲取到鎖后,線程異常。
總結
- 上一篇: 算法总结之编码(C++)
- 下一篇: kafka消费端慢慢延迟(网络带宽不足)