第一期學習總結
在工作的過程中,不斷聽到新名詞,新概念,新方法。覺得是時候總結一波知識點記錄下來,促進深入瞭解的同時,便於以後複習。非常喜歡阮老師的每週分享,但是做不到像他一樣每週去瀏覽那麼多東西,而且總能發現很有趣的知識點。現在期望可以做到雙月一總結,有些知識點可能還是沒有那麼深入理解,希望能堅持下來,在工作的同時慢慢進步,也是相當不錯了。原本於國慶前就有此計劃,並寫好了初稿,中間各種原因,又推遲了一個月。= =
1、什麼是kafka rebalance?
在Consumer Group中的若干Consumer,會按照一定規則(均勻)分配同一個topic下的所有partition,各自在相應partition上執行sub工作。當一個Group中有Consumer退出時,Group中的其他Consumer會對topic下的partition進行重新分配,並基於重新分配的結果繼續執行sub工作。這個重新分配的過程被稱為Rebalance。
分配演算法如下:
1. 將目標 topic 下的所有 partirtion 排序,存於PT
2. 對某 consumer group 下所有 consumer 排序,存於 CG,第 i 個consumer 記為 Ci
3. N=size(PT)/size(CG),向上取整
4. 解除 Ci 對原來分配的 partition 的消費權(i從0開始)
5. 將第i*N到(i+1)*N-1個 partition 分配給 Ci
參考文件:
(1)如何利用磁碟順序讀寫快於記憶體隨機讀寫這一現象?
https://www.zhihu.com/question/48794778
(2)kafka之Group狀態變化分析及Rebalance過程
http://matt33.com/2017/01/16/kafka-group/
(3)Kafka Consumer Rebalance的演進
http://lday.me/2017/07/24/0011_kafka_consumer_rebalance_evolution/
2、Mock功能
經常聽到在測試的時候說mock一下,什麼是mock?目前一個產品開發都是分前後端,並行開發的時候有可能後端先做完了,然而前端還沒有做完,聯調前需要自己做測試。有些介面無法得到響應回來的資料,功能沒法測怎麼辦?所謂的mock就是指根據事先定義好的介面資訊如協議、URL、介面名、請求頭、請求引數、返回資料等對介面自動生成模擬資料。建立者可以自由構造需要的資料進行測試。
一句話總結:mock(模擬)是在專案測試中,對專案外部或不容易獲取的物件/介面,用一個虛擬的物件/介面來模擬,以便測試。
參考文件:
(1)為什麼你需要一個mock server
https://juejin.im/entry/57bd37c2c4c9710061606b38
3、JSON Schema
JSON Schema(JSON模式),是一種基於JSON格式定義JSON資料結構的規範。目的:
(1)描述現有資料格式
(2)乾淨的人類和機器可讀的文件
(3)完整的結構驗證,有利於自動化測試,可用於驗證客戶端提交的資料
舉個栗子:
我們現在有JSON資料(1),現在需要用JSON資料(2)來描述(1)的結構。這時的JSON資料(2)就是JSON資料(1)的JSON Schema,用來描述並且驗證JSON資料(1)
JSON資料(1):
{
"name": "sunwukong",
"age": 24,
"gender": "male"
}
JSON資料(2):
{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 4
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 130
},
"gender": {
"type": "string",
"enum": [
"male",
"female"
]
}
}
}
由JSON資料(2)我們可以知道JSON資料(1):
- 是一個json物件,有三個屬性:name、age和gender
- name屬性的值型別是一個字串,最小長度4
- age屬性的值型別是一個整形,最小值是0,最大值是130
- gender屬性的值型別是一個字串,只能取"male"或者"female"
參考文件:
(1)JSON Schema參考書
http://imweb.io/topic/57b5f69373ac222929653f23
(2)JSON Schema快速入門
https://www.jianshu.com/p/8278eb2458c4
4、傳值還是傳指標?
在實際開發中,前人寫的函式傳入的都是指標,特別是在struct中喜歡用指標。一直不理解為什麼,直到要自己去定義一些變數和結構時,覺得是時候深入理解一下。
兩篇參考文件已經寫的非常詳細了。總結一下:Go裡面所有傳參都是傳值,都是一個副本,一個拷貝。如果傳入的是一個值,那麼就是傳入了值的拷貝,如果傳入的是一個指標,那麼就是傳入了指標的拷貝。因為拷貝的內容有時候是非引用型別(int、string、struct等這些),這樣就在函式中無法修改原內容資料。有的是引用型別(指標、map、slice、chan等這些),這樣就可以修改原內容資料。
是否可以修改原內容資料,和傳值、傳引用沒有必然的關係。在c++中,傳引用肯定是可以修改原內容資料的,在Go語言裡,雖然只有傳值,但是我們也可以修改原內容資料,因為引數是引用型別。注意:引用型別和傳引用是兩個概念。Go裡只有傳值(值傳遞)
參考文件:
(1)全面分析Go語言中的型別和型別指標的抉擇
https://colobu.com/2017/01/05/-T-or-T-it-s-a-question/
(2)Go語言引數傳遞是傳值還是傳引用
http://www.flysnow.org/2018/02/24/golang-function-parameters-passed-by-value.html
5、golang的記憶體分配
關於棧區和堆區的區別,以c++為例一般認為
棧區:由編譯器自動分配釋放,存放函式的引數值,區域性變數的值等。
堆區:一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由OS回收。比如new,malloc等申請的記憶體就在堆區。
在棧區分配的代價要遠小於在堆上進行分配。因為有垃圾回收機制。所以要避免記憶體逃逸。
記憶體逃逸:原本在棧上分配記憶體的物件,逃逸到了堆上進行分配。如果無法在編譯期確定變數的作用域和佔用記憶體大小,則會逃逸到堆上。
(1)指標
在函式傳參時,一般都認為傳遞指標可以減少底層值的拷貝,提高效率。一般情況下亦是如此,但是如果拷貝的是少量的資料,那麼傳遞指標效率不一定會高於值拷貝。指標是間接訪址,所指向的地址大多儲存在堆上,因此考慮到GC,指標不一定是高效的。
(2)切片
如果編譯期無法確定切片的大小或者切片大小過大,超出棧大小限制,或者在append時候會導致重新分配記憶體,這時候很可能會分配到堆上。
(3)interface
參考文件
(1)深入理解GO語言之記憶體詳解
https://juejin.im/post/59f2e19f5188253d6816d504
6、GC機制
GC是指垃圾回收。有三種經典的GC演算法:引用計數(reference counting)、標記-清掃(mark & sweep)、節點複製(Copying Garbage Collection),分代收集(Generational Garbage Collection)。文件(2)中有每一步的圖文解說。
參考文件:
(1)Golang垃圾回收剖析
http://legendtkl.com/2017/04/28/golang-gc/
(2)Go語言的實時GC-理論與實踐
https://segmentfault.com/a/1190000010753702
7、innodb類的資料庫的刪除是軟刪除
就是說會有一個標誌位表示當前資料已被刪除,但是不會真的刪除。這樣資料只會越來越多,所以最好的做法是更新,而不是刪除。
8、MYSQL prepare預編譯
prepare語法:
MySQL prepare語法:
PREPARE statement_name FROM preparable_SQL_statement; /*定義*/
EXECUTE statement_name [USING @var_name...]; /*執行預處理語句*/
{DEALLOCATE | DROP} PREPARE statement_name; /*刪除定義*/
PREPARE語句用於預備一個語句,並指定名稱statement_name,以後引用該語句。語句名稱對大小寫不敏感。
例子:
select * from t_test where id=1 or 1=1;
-- 正常查詢
prepare sel from 'select * from t_test where value = ?';
set @v1='abc';
execute sel using @v1;
-- 注入
set @v1='abc' or 1=1;
execute sel using @v1;
-- 後臺查詢查詢語句變成 select * from t_test where value = 1
通常情況下,一條sql語句在db接收到最終執行完畢返回可以分為三步:
(1)語法和語義解析
(2)優化sql語句,制定執行計劃
(3)執行並返回結果
有時候,一條sql需要反覆執行,或者只是個別的值不同(比如update的set子句值不同)。如果每次都需要經過上面的語法語義解析、語句優化、制定執行計劃等。則效率就明顯不同了。
所謂預編譯語句是指將這類語句中的值用佔位符替代,可以視為將sql語句模板化或者說引數化,一般稱這類語句叫Prepared Statements或者Parameterized Statements。
預編譯語句優點:一次編譯、多次執行、省去了解析優化等過程,此外預編譯語句能防止sql注入。
參考文件:
(1)預編譯語句(Prepared Statements)介紹,以MySQL為例
https://www.cnblogs.com/micrari/p/7112781.html
(2)mysql預編譯-Prepared-Statements
http://yihengliu.com/2018/06/04/mysql%E9%A2%84%E7%BC%96%E8%AF%91-Prepared-Statements/
9、slice和陣列(Array)的區別
例子:
a := []int{1} //[]中沒有固定長度的就是slice
b := [2]int{1, 2} //固定了長度的是陣列array
Array是值型別,固定長度,大小是型別的一部分,當一個數組變數被賦值或者被傳遞的時候,實際上會複製整個陣列。而Slice在新元素加入的時候可以增加長度(增長到容量的上限)。array的長度也是Type的一部分,這樣就說明[10]int和[20]int是不一樣的。
Slice一個切片是一個數組片段的描述。它包含了指向陣列的指標,片段的長度和容量(底層陣列的長度)。slice是一個引用型別,是一個動態的指向陣列切片的指標,是一個不定長的,總是指向底層的陣列array的資料結構。
參考文件:
(1)golang slice和array區別
https://segmentfault.com/a/1190000013148775
10、pb資料格式
pb(Protocol Buffers)是一種輕便高效的結構化資料儲存格式,可以用於結構化資料序列化,或者說序列化。很適合做資料儲存或RPC資料交換格式。可用於通訊協議、資料儲存等領域的語言無關、平臺無關、可擴充套件的序列化結構資料格式。
pb提供了一種靈活,高效,自動序列化結構資料的機制,可以聯想XML,但是比XML更小,更快,更簡單。僅需要自定義一次你所需的資料格式,然後使用者就可以使用Protocol Buffers自動生成的特定的原始碼,方便的讀寫使用者自定義的格式化的資料。不限語言,不限平臺。還可以在不破壞元資料格式的基礎上,依據老的資料格式,更新現有的資料格式。
11、GO語言中for迴圈裡的變數地址複用
for _, op := range ops中op的地址是複用的
例子:
package main
import(
"fmt"
)
type data struct{
age int
name string
}
func main() {
testStrs := []string{"hello", "world", "yes", "no"}
for index, s := range testStrs{
fmt.Println("index:", index)
fmt.Println("列印s的地址", &s)
fmt.Println("s的值", s)
}
for i:=0;i<len(testStrs);i++{
fmt.Println("i:", i)
fmt.Println("列印testStrs[i]的地址:", &testStrs[i])
fmt.Println("testStrs[i]的值:", testStrs[i])
}
}
列印結果:
index: 0
列印s的地址 0xc42000e1e0
s的值 hello
index: 1
列印s的地址 0xc42000e1e0
s的值 world
index: 2
列印s的地址 0xc42000e1e0
s的值 yes
index: 3
列印s的地址 0xc42000e1e0
s的值 no
i: 0
列印testStrs[i]的地址: 0xc4200560c0
testStrs[i]的值: hello
i: 1
列印testStrs[i]的地址: 0xc4200560d0
testStrs[i]的值: world
i: 2
列印testStrs[i]的地址: 0xc4200560e0
testStrs[i]的值: yes
i: 3
列印testStrs[i]的地址: 0xc4200560f0
testStrs[i]的值: no
12、Go語言中多個Recover存在時會怎麼樣?
例子:
package main
import(
"fmt"
)
func main(){
defer func(){
//第一層defer
fmt.Println("進入第一層defer")
if err := recover(); err != nil {
fmt.Println("進入第一層的recover邏輯中")
fmt.Printf("%s\n", err)
}
}()
a := 1
b := 2
defer func(){
//第二層defer
fmt.Println("進入第二層defer")
if err := recover(); err != nil {
fmt.Println("進入第二層的recover邏輯中")
fmt.Printf("%s\n", err)
}
}()
fmt.Println(a, b)
panic("出錯啦")
}
列印結果:
1 2
進入第二層defer
進入第二層的recover邏輯中
出錯啦
進入第一層defer