成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詳解 mini-redis 復(fù)刻 Redis 的 I NCR 指令

數(shù)據(jù)庫(kù) Redis
本文將針對(duì)字符串操作中介紹筆者近期所復(fù)刻的鍵值自增指令的落地思路,以幫助讀者更好的理解和學(xué)習(xí)mini-redis。

因?yàn)榻诒容^忙碌,所以對(duì)于mini-redis的復(fù)刻基本處于一些指令向的完善,而本文將針對(duì)字符串操作中介紹筆者近期所復(fù)刻的鍵值自增指令的落地思路,以幫助讀者更好的理解和學(xué)習(xí)mini-redis。

對(duì)象類(lèi)型前置校驗(yàn)

因?yàn)橹噶钍腔谧址僮鞯模栽趫?zhí)行INCR或者DECR之前我們都必須針對(duì)入?yún)⒌逆I值對(duì)進(jìn)行校驗(yàn),所以對(duì)于以下情況,我們都必須采用fail-fast的方式提前將失敗暴露,將鍵值對(duì)已存在,對(duì)應(yīng)的值非字符串類(lèi)型(例如:字典類(lèi)型),直接響應(yīng)錯(cuò)誤:

基于上述的基本概念,我們給出落地的代碼,即位于command.go的incrDecrCommand方法,可以看到我們會(huì)優(yōu)先到redis內(nèi)存中查看是否存在對(duì)應(yīng)的key,如果存在則進(jìn)行必要的類(lèi)型判斷,如果非字符串類(lèi)型即REDIS_STRING則直接響應(yīng)錯(cuò)誤出去,并直接返回:

func incrDecrCommand(c *redisClient, incr int64) {
 var value int64
 var oldValue int64
 var newObj *robj
 //查看鍵值對(duì)是否存在
 o := lookupKeyWrite(c.db, c.argv[1])
 //如果鍵值對(duì)存在且類(lèi)型非字符串類(lèi)型,直接響應(yīng)錯(cuò)誤并返回
 if o != nil && checkType(c, o, REDIS_STRING) {
  return
 }
 
 
 //......

}

對(duì)此我們也給出checkType的內(nèi)部邏輯,可以看到當(dāng)比對(duì)類(lèi)型不一致時(shí)會(huì)直接輸出錯(cuò)誤并返回true,讀者可以參考注釋了解:

func checkType(c *redisClient, o *robj, rType int) bool {
 //如果類(lèi)型不一致,則輸出-WRONGTYPE Operation against a key holding the wrong kind of value
 if o.robjType != rType {
  addReply(c, shared.wrongtypeerr)
  return true
 }
 return false
}

其實(shí)筆者這里也想吐槽一句redis對(duì)于函數(shù)設(shè)計(jì)的語(yǔ)義的不恰當(dāng)性,理論性合理的函數(shù)進(jìn)行校驗(yàn)時(shí)正確的做法應(yīng)該是:

  • 邏輯校驗(yàn)失敗,輸出錯(cuò)誤返回false。
  • 邏輯校驗(yàn)正確,返回true。

也只能說(shuō)因?yàn)槟承v史原因,或者設(shè)計(jì)者有著自己的主觀編碼習(xí)慣吧,本著一比一的復(fù)刻理念,筆者也沿襲了這樣的編碼思路。

基于數(shù)值池高效完成字符串轉(zhuǎn)換

針對(duì)字符串類(lèi)型(可以轉(zhuǎn)數(shù)值的情況下,它也會(huì)轉(zhuǎn)數(shù)值類(lèi)型),我們都是通過(guò)robj類(lèi)型創(chuàng)建和維護(hù),因?yàn)槲覀儽敬嗡鶑?fù)刻的incr和decr所操作的類(lèi)型是字符串中可轉(zhuǎn)為數(shù)值的對(duì)象,所以本著數(shù)值類(lèi)型有跡可循的規(guī)律以及空間換時(shí)間的思想,我們提出池化思想,即將0-9999數(shù)值緩存一份數(shù)值池,后續(xù)的增減操作后處于該范圍的數(shù)值都可以直接使用數(shù)值池里對(duì)應(yīng)的robj對(duì)象,以節(jié)約robj對(duì)象創(chuàng)建的開(kāi)銷(xiāo)和非必要的內(nèi)存資源占用:

所以筆者在main.go中聲明sharedObjectsStruct 這個(gè)結(jié)構(gòu)體中聲明了一個(gè)integers維護(hù)常量池的robj對(duì)象:

type sharedObjectsStruct struct {
 //......
 integers       [REDIS_SHARED_INTEGERS]*robj //通用0~9999常量數(shù)值池
 //......
}

然后在createSharedObjects方法中完成初始化,后續(xù)就可以直接使用了:

func createSharedObjects() {
 //......

 var i int64
 //初始化常量池對(duì)象
 for i = 0; i < REDIS_SHARED_INTEGERS; i++ {
  //基于接口封裝數(shù)值
  num := interface{}(i)
  //生成string對(duì)象
  shared.integers[i] = createObject(REDIS_STRING, &num)
  //聲明編碼類(lèi)型為int
  shared.integers[i].encoding = REDIS_ENCODING_INT
 }

 //......
}

于是我們就得出了后續(xù)的編碼邏輯:

  • 將value強(qiáng)轉(zhuǎn)為數(shù)值判斷是否超出范圍,如果超了則拋出異常。反之進(jìn)入步驟2。
  • 查看取值范圍是否大于10000,如果是則自己生成robj對(duì)象,反之采用池化數(shù)值池的robj。
  • 基于1、2生成的數(shù)值對(duì)象將鍵值對(duì)更新或者覆蓋到內(nèi)存數(shù)據(jù)庫(kù)中。
/**
 針對(duì)字符串類(lèi)型的值進(jìn)行如下判斷的和轉(zhuǎn)換:
 1. 如果為空,說(shuō)明本次的key不存在,直接初始化一個(gè)空字符串,后續(xù)會(huì)直接初始化一個(gè)0值使用
 2. 如果是字符串類(lèi)型,則轉(zhuǎn)為字符串類(lèi)型
 3. 如果是數(shù)值類(lèi)型,則先轉(zhuǎn)為字符串類(lèi)型進(jìn)行后續(xù)的通用數(shù)值轉(zhuǎn)換操作保證一致性
 */
 var s string
 if o == nil {
  s = ""
 } else if isString(*o.ptr) {
  s = (*o.ptr).(string)
 } else {
  s = strconv.FormatInt((*o.ptr).(int64), 10)
 }
 //進(jìn)行類(lèi)型強(qiáng)轉(zhuǎn)為數(shù)值,如果失敗,直接輸出錯(cuò)誤并返回
 if getLongLongFromObjectOrReply(c, s, &value, nil) != REDIS_OK {
  return
 }

 oldValue = value
 //如果累加超范圍則報(bào)錯(cuò)
 if (incr < 0 && oldValue < 0 && incr < (math.MinInt64-oldValue)) ||
  (incr > 0 && oldValue > 0 && incr > (math.MaxInt64-oldValue)) {
  errReply := "increment or decrement would overflow"
  addReplyError(c, &errReply)
  return
 }
 //基于incr累加的值生成value
 value += incr
 //如果超常量池范圍則封裝一個(gè)對(duì)象使用 
 if o != nil &&
  (value < 0 || value >= REDIS_SHARED_INTEGERS) &&
  (value > math.MinInt64 || value < math.MaxInt64) {
  newObj = o

  i := interface{}(value)
  o.ptr = &i
 } else if o != nil {//如果對(duì)象存在,且累加結(jié)果沒(méi)超范圍則調(diào)用createStringObjectFromLongLong獲取常量對(duì)象
  newObj = createStringObjectFromLongLong(value)
  //將寫(xiě)入結(jié)果覆蓋
  dbOverwrite(c.db, c.argv[1], newObj)
 } else {//從常量池獲取數(shù)值,然后添加鍵值對(duì)到數(shù)據(jù)庫(kù)中
  newObj = createStringObjectFromLongLong(value)
  dbAdd(c.db, c.argv[1], newObj)
 }

通用結(jié)果響應(yīng)

完成上述操作后就是將結(jié)果按照RESP協(xié)議規(guī)范將結(jié)果響應(yīng)給客戶(hù)端,按照協(xié)議要求數(shù)值類(lèi)型必須用:號(hào)開(kāi)頭,所以假設(shè)我們累加結(jié)果為10,那么響應(yīng)給客戶(hù)端的結(jié)果就是10\r\n。

對(duì)應(yīng)我們的給出最后的代碼段:

//將累加后的結(jié)果返回給客戶(hù)端,按照RESP格式即 :數(shù)值\r\n,例如返回10 那么格式就是:10\r\n
 reply := *shared.colon + strconv.FormatInt(value, 10) + *shared.crlf
 addReply(c, &reply)

完整的代碼實(shí)現(xiàn)

我們來(lái)小結(jié)一下上述的實(shí)現(xiàn)思路:

  • 鍵值對(duì)查詢(xún)與校驗(yàn)。
  • 數(shù)值類(lèi)型轉(zhuǎn)換與越界判斷。
  • 字符串類(lèi)型強(qiáng)轉(zhuǎn)并基于取值范圍查看是否通過(guò)數(shù)值池獲取。
  • 更新或覆蓋鍵值對(duì)。
  • 將操作結(jié)果返回客戶(hù)端。

完整代碼如下:

func incrDecrCommand(c *redisClient, incr int64) {
 var value int64
 var oldValue int64
 var newObj *robj
 //查看鍵值對(duì)是否存在
 o := lookupKeyWrite(c.db, c.argv[1])
 //如果鍵值對(duì)存在且類(lèi)型非字符串類(lèi)型,直接響應(yīng)錯(cuò)誤并返回
 if o != nil && checkType(c, o, REDIS_STRING) {
  return
 }
 /**
 針對(duì)字符串類(lèi)型的值進(jìn)行如下判斷的和轉(zhuǎn)換:
 1. 如果為空,說(shuō)明本次的key不存在,直接初始化一個(gè)空字符串,后續(xù)會(huì)直接初始化一個(gè)0值使用
 2. 如果是字符串類(lèi)型,則轉(zhuǎn)為字符串類(lèi)型
 3. 如果是數(shù)值類(lèi)型,則先轉(zhuǎn)為字符串類(lèi)型進(jìn)行后續(xù)的通用數(shù)值轉(zhuǎn)換操作保證一致性
 */
 var s string
 if o == nil {
  s = ""
 } else if isString(*o.ptr) {
  s = (*o.ptr).(string)
 } else {
  s = strconv.FormatInt((*o.ptr).(int64), 10)
 }
 //進(jìn)行類(lèi)型強(qiáng)轉(zhuǎn)為數(shù)值,如果失敗,直接輸出錯(cuò)誤并返回
 if getLongLongFromObjectOrReply(c, s, &value, nil) != REDIS_OK {
  return
 }

 oldValue = value

 if (incr < 0 && oldValue < 0 && incr < (math.MinInt64-oldValue)) ||
  (incr > 0 && oldValue > 0 && incr > (math.MaxInt64-oldValue)) {
  errReply := "increment or decrement would overflow"
  addReplyError(c, &errReply)
  return
 }
 //基于incr累加的值生成value
 value += incr
 //如果超常量池范圍則封裝一個(gè)對(duì)象使用
 if o != nil &&
  (value < 0 || value >= REDIS_SHARED_INTEGERS) &&
  (value > math.MinInt64 || value < math.MaxInt64) {
  newObj = o

  i := interface{}(value)
  o.ptr = &i
 } else if o != nil { //如果對(duì)象存在,且累加結(jié)果沒(méi)超范圍則調(diào)用createStringObjectFromLongLong獲取常量對(duì)象
  newObj = createStringObjectFromLongLong(value)
  //將寫(xiě)入結(jié)果覆蓋
  dbOverwrite(c.db, c.argv[1], newObj)
 } else { //從常量池獲取數(shù)值,然后添加鍵值對(duì)到數(shù)據(jù)庫(kù)中
  newObj = createStringObjectFromLongLong(value)
  dbAdd(c.db, c.argv[1], newObj)
 }
 //將累加后的結(jié)果返回給客戶(hù)端,按照RESP格式即 :數(shù)值\r\n,例如返回10 那么格式就是:10\r\n
 reply := *shared.colon + strconv.FormatInt(value, 10) + *shared.crlf
 addReply(c, &reply)

}

遞增遞減的復(fù)用

基于上述函數(shù)對(duì)應(yīng)的遞增指令I(lǐng)NCR就使用incrCommand,入?yún)?代表加1,而decrCommand則傳-1扣減即可:

func incrCommand(c *redisClient) {
 //累加1
 incrDecrCommand(c, 1)
}

func decrCommand(c *redisClient) {
 //遞減1
 incrDecrCommand(c, -1)
}

最終效果演示

最后,我們將服務(wù)啟動(dòng)進(jìn)行測(cè)試,可以看到指令正常執(zhí)行:

127.0.0.1:6379> incr k1
(integer) 1
(4.50s)
127.0.0.1:6379> incr k1
(integer) 2
127.0.0.1:6379> incr k1
(integer) 3
127.0.0.1:6379> incr k1
(integer) 4
127.0.0.1:6379> incr k1
(integer) 5
127.0.0.1:6379> incr k1
(integer) 6
127.0.0.1:6379> decr k1
(integer) 5
127.0.0.1:6379> decr k1
(integer) 4
127.0.0.1:6379> decr k1
(integer) 3
127.0.0.1:6379> decr k1
(integer) 2
127.0.0.1:6379> decr k1
(integer) 1
127.0.0.1:6379> decr k1
(integer) 0
127.0.0.1:6379> decr k1
(integer) -1
127.0.0.1:6379>


責(zé)任編輯:趙寧寧 來(lái)源: 寫(xiě)代碼的SharkChili
相關(guān)推薦

2024-11-22 15:00:00

開(kāi)源Redis鏈表

2018-08-15 09:48:27

數(shù)據(jù)庫(kù)Redis應(yīng)用場(chǎng)景

2023-07-03 07:55:25

2024-07-16 08:38:06

2024-04-18 00:20:56

Redis策略數(shù)據(jù)

2024-04-18 08:00:00

2019-08-06 19:36:25

RedisMemcached緩存

2025-01-15 08:19:12

SpringBootRedis開(kāi)源

2024-07-31 08:33:17

2011-10-25 10:36:19

蘋(píng)果臺(tái)式機(jī)

2024-11-11 17:12:22

2011-11-07 09:42:58

蘋(píng)果臺(tái)式機(jī)

2024-10-08 10:13:17

2018-04-27 09:03:57

Redis數(shù)據(jù)存儲(chǔ)

2024-12-09 00:00:09

2011-10-27 15:04:19

蘋(píng)果臺(tái)式機(jī)

2018-11-06 10:51:07

Redis開(kāi)發(fā)存儲(chǔ)系統(tǒng)

2024-09-26 06:30:36

2019-05-17 08:55:49

RedisRDBAOF

2020-01-15 14:51:04

Redis5.0數(shù)據(jù)策略
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 色资源在线视频 | 亚洲欧美国产一区二区三区 | 久久一区二区视频 | 国产精品欧美一区二区 | 999免费视频 | 在线免费看黄 | 久久久国产一区二区三区 | 国产高清av免费观看 | 亚洲国产精品一区在线观看 | 亚洲精品久久 | 有码一区 | 毛片免费观看 | 欧美成人性生活 | 天天玩天天操天天干 | 成人精品福利 | 理论片免费在线观看 | 久久精品亚洲欧美日韩久久 | 久草免费电影 | 国产99在线 | 欧美 | 色成人免费网站 | 国产一区二区三区色淫影院 | 一a一片一级一片啪啪 | 一区二区三区高清不卡 | 久久精品国产一区二区电影 | 国产日韩一区二区三免费高清 | 99精品一区二区 | 日本超碰| 久久久久久亚洲精品 | 久久精品美女 | 精品无码久久久久国产 | 国产精品久久久久久久久免费樱桃 | 在线观看国产视频 | 亚洲精品一区中文字幕乱码 | 好姑娘影视在线观看高清 | 国产福利资源在线 | 特级做a爰片毛片免费看108 | 国产精品久久久久久久久免费 | 色综合天天天天做夜夜夜夜做 | 国产欧美在线观看 | 欧美日韩视频在线 | 人妖一区 |