在實際生產環境下,某場景下,數以千萬乃至上億的數據會批量落入db,nosql,而根據數據的ttl過期,會在某個多久之后的時間給這批數據del.
加入每天9點都在批量insert—all,而前n天的數據恰好在這個時間節點(n天后的9點)ttl過期,觀察db的io負載,會急速飈上去,io打的很高,甚至到100%。
根據生產上的如上情況,做了下面的測試:
在實際場景中,操作nosql是很常用的,下面分享下壓測mongo的場景和具體結果:
測試用例代碼如下:
package main import ( "fmt" "gopkg.in/mgo.v2/bson" "time" ) func main() { for j := 0; j < 20; j ++ { go pressTest() } pressTest() //測試mongo寫入壓測 } func pressTest (){ defer timeCost()() //注意,是對 timeCost()返回的函數進行調用,因此需要加兩對小括號 for j := 0; j < 2; j ++ { InsertMfAndroid("abc",1) } } // 耗時統計函數 func timeCost() func() { start := time.Now() return func() { tc := time.Since(start) fmt.Printf("time cost0000 = %v\n", tc) } } //可插入多條數據(先做循環插入mongo) MId string, token_arry, userIds []string, p, appId int func InsertMfAndroid(newMid string, tokenNums int) { //defer timeCost()() //注意,是對 timeCost()返回的函數進行調用,因此需要加兩對小括號 session := GetSession().Clone() defer session.Close() timeUnix := time.Now().Unix() timeUnixString := strconv.FormatInt(timeUnix,10) // int64到string sendTime, _ := strconv.Atoi(timeUnixString) // string到int var docs []interface{} for j := 0; j < tokenNums; j ++ { docs = append(docs, bson.M{"mid": newMid, "t": "1","sut": sendTime, "cut": time.Now(), "expireTime":getExpireTime()}) } collection := session.DB(beego.AppConfig.String("mongo::db")).C("token_msg") err := collection.Insert(docs...) if err != nil { beego.Error("mongo存儲消息流水:[%s]", err) } }
//獲取過期時間
func getExpireTime() time.Time {
timeStr := time.Now().Format("2006-01-02")
//使用Parse 默認獲取為UTC時區 需要獲取本地時區 所以使用ParseInLocation
t, _ := time.ParseInLocation("2006-01-02 15:04:05", timeStr+" 23:59:59", time.Local)
zeroTime := t.Unix() + 1
//時間戳 to 時間
tm := time.Unix(zeroTime + 356400, 0)
return tm
}
UAT環境壓測 mongo副本集:
8核16G 虛擬機,(上面已搭若干實例),已使用10G,空閑內存6G左右前提下,壓測mongo服務。
(插入字段和生產線上字段格式長度基本相同。)
1)單進程(攜程)寫入:單次批量寫入,1萬條/次,耗時221ms;
2)單進程(攜程)寫入:單次批量寫入,1千條/次,耗時38ms(40ms左右);
3)單進程(攜程)寫入: 連續遍歷1千次,每次insert_all 1k條/次,插入總數量1百萬條記錄,總耗時19s;
4)單進程(攜程)寫入: 連續遍歷1萬次,每次insert_all 1k條/次, 插入總量1千萬條記錄,總耗時3min12s(3min14s),cpu 使用率 150%左右,mongo狀態每秒鍾insert操作5w多次;
5)單進程(攜程)寫入: 遍歷兩次,每次insert-all 1k條/次,耗時77ms;
6)並發寫入:20個進程同時批量寫入,每個進程總共insert_all 100萬條記錄,連續遍歷1000次,每次寫入1000條記錄,mongo的寫入次數每秒鍾/20萬次寫操作,每個進程寫完1min40
S,20個進程寫入基本為1min40s左右。
7)在2千萬的表中,單純的做TTL過期,iostat查看await和util%,util%達到30-40%左右,說明這個操作比較消耗IO性能.
8) 並發寫入:20個進程,2千萬條記錄,每個進程寫入100w條記錄的IoStat狀態
9)最左邊是Mongo每秒鍾的寫操作次數,連接數和等待隊列正常,無明顯增高變化;
10)用固定集合capCollection指定集合(表)大小和條數,Insert每秒鍾2w多條操作,iostat的util最高在30%多,io正常.
結合前幾篇寫的博客, 地址如下,https://www.cnblogs.com/unqiang/p/11987684.html
創建mongo刪除的索引,根據業務,如果是白天忙時,晚上閑時,最好選擇晚上去做TTL過期,這個不會在業務高峰的時候,批量插入和刪除都去搶占IO操作,
而單純的TTL過期,就會把io的util%打到30%,業務數據量如上,總計2000w數據,然后TTL索引過期.
要是再加上,2000w的數據,20個進程每個進程100w同時批量寫,IO util%一下會飆升上去.
故建議根據業務選擇刪除mongo的數據,或者選擇寫腳本或程序手動控制或自動刪除.