1. 程式人生 > >第一期學習總結

第一期學習總結

在工作的過程中,不斷聽到新名詞,新概念,新方法。覺得是時候總結一波知識點記錄下來,促進深入瞭解的同時,便於以後複習。非常喜歡阮老師的每週分享,但是做不到像他一樣每週去瀏覽那麼多東西,而且總能發現很有趣的知識點。現在期望可以做到雙月一總結,有些知識點可能還是沒有那麼深入理解,希望能堅持下來,在工作的同時慢慢進步,也是相當不錯了。原本於國慶前就有此計劃,並寫好了初稿,中間各種原因,又推遲了一個月。= =

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