欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang開(kāi)發(fā)之字符串與切片問(wèn)題踩坑記錄

 更新時(shí)間:2023年07月31日 09:12:20   作者:寫(xiě)代碼的lorre  
字符串和切片,都是golang常用的兩種內(nèi)置數(shù)據(jù)類(lèi)型,最近在日常工作中,遇到了一個(gè)字符串切片導(dǎo)致的問(wèn)題,記錄一下排查問(wèn)題的過(guò)程,避免后續(xù)在這種場(chǎng)景上踩坑

背景

在項(xiàng)目中,我們使用mysql來(lái)存儲(chǔ)數(shù)據(jù)信息,其中l(wèi)abel表記錄了標(biāo)簽相關(guān)的信息。表結(jié)構(gòu)如下:

CREATE TABLE `label` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(190) COLLATE utf8mb4_unicode_ci DEFAULT 'label name',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_n` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=7050965 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='label'

其中name字段為varchar類(lèi)型,190代表字符長(zhǎng)度,并且是唯一索引

為什么name字段設(shè)置最大字符長(zhǎng)度為190?

name是varchar類(lèi)型,并且是唯一索引。mysql規(guī)定varchar類(lèi)型為索引時(shí),最大長(zhǎng)度為767字節(jié)

name字段的編碼格式為utf8mb4_unicode_ci,一個(gè)字符最多用4個(gè)字節(jié)來(lái)表示,767 / 4 = 191.75,所以只需限制最大字符長(zhǎng)度小于191.75即可,項(xiàng)目里取190作為限制

業(yè)務(wù)代碼邏輯很簡(jiǎn)單,主要有兩步:

  • 依據(jù)name查詢label表,如果查詢到了label信息,則直接返回
  • 如果沒(méi)有查詢到label信息,則嘗試創(chuàng)建label信息

偽代碼:

const (
   // 最大字符長(zhǎng)度
   LabelNameLengthLimit = 190
)
func GetOrCreateLabel(ctx context.Context, name string) (label dao.Label, err error) {
   var err error
   // 依據(jù)name查詢label信息
   label, err = db_reader.GetLabel(name)
   // 沒(méi)有找到對(duì)應(yīng)的label信息,且字符串長(zhǎng)度超過(guò)190,則切片后再次查詢
   if IsRecordNotFoundError(err) && len(name) > LabelNameLengthLimit {
      label, err = db_reader.GetLabel(name[:LabelNameLengthLimit])
   }
   // 還是報(bào)錯(cuò),則嘗試創(chuàng)建label信息
   if err != nil {
      label.Name = name
      err = db_writer.CreateLabel(ctx, &label)
      // 報(bào)唯一鍵沖突錯(cuò)誤,可能是由于并發(fā)創(chuàng)建導(dǎo)致的問(wèn)題,再次兜底進(jìn)行查詢
      if IsKeyConflict(err) {
         label, err = db_reader.GetLabel(ctx, name)
         if err != nil {
            return label, err
         }
      }
      if err != nil {
         return label, err
      }
   }
   return label, nil
}

問(wèn)題

部分case,首次執(zhí)行業(yè)務(wù)代碼成功,后續(xù)執(zhí)行業(yè)務(wù)代碼一直報(bào)錯(cuò)

case1:

labelName := "? 2015 Charanga Sí o Ké - Chema Mu?oz, Perfecto Artola, Pablo Guerrero, Antonio Flores, Los Gipsy King, Chambao, Adele, La Pegatina, El Chaval de la Peca, Los Manolos ? 2015 Charanga Sí o Ké - Sones de Rumba"

執(zhí)行:

  • 首次執(zhí)行業(yè)務(wù)代碼,執(zhí)行成功
  • 后續(xù)執(zhí)行業(yè)務(wù)代碼,22行穩(wěn)定復(fù)現(xiàn)報(bào)錯(cuò)

分析

后續(xù)執(zhí)行業(yè)務(wù)代碼時(shí)

  • 依據(jù)name,一直查詢不到對(duì)應(yīng)的label信息,err報(bào)錯(cuò)RecordNotFoundError
  • 依據(jù)name[:LabelNameLengthLimit],一直查詢不到對(duì)應(yīng)的label信息,err報(bào)錯(cuò)RecordNotFoundError
var err error
// 依據(jù)name查詢label信息
label, err = db_reader.GetLabel(name)
// 沒(méi)有找到對(duì)應(yīng)的label信息,且字符串長(zhǎng)度超過(guò)190,則切片后再次查詢
if IsRecordNotFoundError(err) && len(name) > LabelNameLengthLimit {
   label, err = db_reader.GetLabel(name[:LabelNameLengthLimit])
}

err != nil時(shí),嘗試創(chuàng)建label信息,此時(shí)報(bào)唯一鍵沖突錯(cuò)誤

線上可能是并發(fā)創(chuàng)建導(dǎo)致的唯一鍵沖突,兜底查詢一次,此時(shí)查詢還是報(bào)錯(cuò)RecordNotFoundError

err = db_writer.CreateLabel(ctx, &label)
// 報(bào)唯一鍵沖突錯(cuò)誤,可能是由于并發(fā)創(chuàng)建導(dǎo)致的問(wèn)題,再次兜底進(jìn)行查詢
if IsKeyConflict(err) {
   label, err = db_reader.GetLabel(ctx, name)
   if err != nil {
      return label, err
   }
}

由于字符長(zhǎng)度超過(guò)了190,定位到主要的問(wèn)題是:依據(jù)切片后name[:LabelNameLengthLimit]查詢時(shí),沒(méi)有查詢到結(jié)果,但是依據(jù)name去創(chuàng)建label信息時(shí),報(bào)唯一鍵沖突,說(shuō)明查詢的值和實(shí)際存儲(chǔ)的值不一致

golang string底層實(shí)現(xiàn)go/src/reflect/value.go

// StringHeader is the runtime representation of a string.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
type StringHeader struct {
   Data uintptr
   Len  int
}

其中:

  • Data 指向底層的[]byte的首地址,string底層其實(shí)是[]byte
  • Len 代表字節(jié)切片的長(zhǎng)度,避免多次獲取字符串長(zhǎng)度時(shí),重復(fù)計(jì)算
  • 內(nèi)置函數(shù)len(string),獲取的是字符串字節(jié)長(zhǎng)度
  • 對(duì)字符串進(jìn)行切片labelName[:LabelNameLengthLimit],是按照字節(jié)數(shù)進(jìn)行切片

所以問(wèn)題就是:查詢和存儲(chǔ)時(shí),截取字符串的標(biāo)準(zhǔn)不一樣

  • 當(dāng)字符串字符長(zhǎng)度超過(guò)190時(shí),查詢時(shí)是按照字節(jié)進(jìn)行截取
  • 當(dāng)字符串字符長(zhǎng)度超過(guò)190時(shí),存儲(chǔ)時(shí)是按照字符進(jìn)行截取

測(cè)試代碼:

labelName := "? 2015 Charanga Sí o Ké - Chema Mu?oz, Perfecto Artola, Pablo Guerrero, Antonio Flores, Los Gipsy King, Chambao, Adele, La Pegatina, El Chaval de la Peca, Los Manolos ? 2015 Charanga Sí o Ké - Sones de Rumba"
fmt.Println("原字符串內(nèi)容:", labelName)
fmt.Println("字節(jié)長(zhǎng)度:", len(labelName))
fmt.Println("按照字節(jié)截取內(nèi)容:", labelName[:LabelNameLengthLimit])
fmt.Println("字符長(zhǎng)度:", len([]rune(labelName)))
fmt.Println("按照字符截取內(nèi)容:", string([]rune(labelName)[:LabelNameLengthLimit]))

輸出:

原字符串內(nèi)容: © 2015 Charanga Sí o Ké - Chema Muñoz, Perfecto Artola, Pablo Guerrero, Antonio Flores, Los Gipsy King, Chambao, Adele, La Pegatina, El Chaval de la Peca, Los Manolos © 2015 Charanga Sí o Ké - Sones de Rumba
字節(jié)長(zhǎng)度: 214
按照字節(jié)截取內(nèi)容: © 2015 Charanga Sí o Ké - Chema Muñoz, Perfecto Artola, Pablo Guerrero, Antonio Flores, Los Gipsy King, Chambao, Adele, La Pegatina, El Chaval de la Peca, Los Manolos © 2015 Charanga S?
字符長(zhǎng)度: 207
按照字符截取內(nèi)容: © 2015 Charanga Sí o Ké - Chema Muñoz, Perfecto Artola, Pablo Guerrero, Antonio Flores, Los Gipsy King, Chambao, Adele, La Pegatina, El Chaval de la Peca, Los Manolos © 2015 Charanga Sí o Ké

解決

解決方案:

  • 先把string轉(zhuǎn)rune切片,使用字符切片來(lái)表示字符串
  • 判斷rune切片長(zhǎng)度是否超過(guò)限制,超過(guò)則依據(jù)字符長(zhǎng)度進(jìn)行切片
const (
   // 最大字符長(zhǎng)度
   LabelNameLengthLimit = 190
)
func GetOrCreateLabel(ctx context.Context, name string) (label dao.Label, err error) {
   if rs := []rune(name); len(rs) > LabelNameLengthLimit {
      name = string(rs[:LabelNameLengthLimit])
   }
   var err error
   // 依據(jù)name查詢label信息
   label, err = db_reader.GetLabel(name)
   // 還是報(bào)錯(cuò),則嘗試創(chuàng)建label信息
   if err != nil {
      label.Name = name
      err = db_writer.CreateLabel(ctx, &label)
      // 報(bào)唯一鍵沖突錯(cuò)誤,可能是由于并發(fā)創(chuàng)建導(dǎo)致的問(wèn)題,再次兜底進(jìn)行查詢
      if IsKeyConflict(err) {
         label, err = db_reader.GetLabel(ctx, name)
         if err != nil {
            return label, err
         }
      }
      if err != nil {
         return label, err
      }
   }
   return label, nil
}

到此這篇關(guān)于Golang開(kāi)發(fā)之字符串與切片問(wèn)題踩坑記錄的文章就介紹到這了,更多相關(guān)Go字符串切片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解決golang中container/list包中的坑

    解決golang中container/list包中的坑

    這篇文章主要介紹了解決golang中container/list包中的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Go 1.21新增的slices包中切片函數(shù)用法詳解

    Go 1.21新增的slices包中切片函數(shù)用法詳解

    Go 1.21新增的 slices 包提供了很多和切片相關(guān)的函數(shù),可以用于任何類(lèi)型的切片,本文通過(guò)代碼示例為大家介紹了部分切片函數(shù)的具體用法,感興趣的小伙伴可以了解一下
    2023-08-08
  • golang構(gòu)建HTTP服務(wù)的實(shí)現(xiàn)步驟

    golang構(gòu)建HTTP服務(wù)的實(shí)現(xiàn)步驟

    其實(shí)很多框架都是在 最簡(jiǎn)單的http服務(wù)上做擴(kuò)展的的,基本上都是遵循h(huán)ttp協(xié)議,本文主要介紹了golang構(gòu)建HTTP服務(wù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • 手把手帶你走進(jìn)Go語(yǔ)言之常量解析

    手把手帶你走進(jìn)Go語(yǔ)言之常量解析

    這篇文章主要介紹了Go語(yǔ)言之常量解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • 關(guān)于go get 下載第三方包存儲(chǔ)路徑問(wèn)題

    關(guān)于go get 下載第三方包存儲(chǔ)路徑問(wèn)題

    這篇文章主要介紹了關(guān)于go get 下載第三方包存儲(chǔ)路徑問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Go處理PDF的實(shí)現(xiàn)代碼

    Go處理PDF的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Go處理PDF的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • Go語(yǔ)言使用Request,Response處理web頁(yè)面請(qǐng)求

    Go語(yǔ)言使用Request,Response處理web頁(yè)面請(qǐng)求

    這篇文章主要介紹了Go語(yǔ)言使用Request,Response處理web頁(yè)面請(qǐng)求,需要的朋友可以參考下
    2022-04-04
  • GoLang中panic與recover函數(shù)以及defer語(yǔ)句超詳細(xì)講解

    GoLang中panic與recover函數(shù)以及defer語(yǔ)句超詳細(xì)講解

    這篇文章主要介紹了GoLang的panic、recover函數(shù),以及defer語(yǔ)句,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-01-01
  • Go實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)庫(kù)表轉(zhuǎn)結(jié)構(gòu)體詳解

    Go實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)庫(kù)表轉(zhuǎn)結(jié)構(gòu)體詳解

    這篇文章主要為大家介紹了Go實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)庫(kù)表轉(zhuǎn)結(jié)構(gòu)體詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • Golang 如何解析和生成json

    Golang 如何解析和生成json

    這篇文章主要介紹了Golang 如何解析和生成json,幫助大家更好的理解和學(xué)習(xí)golang,感興趣的朋友可以了解下
    2020-09-09

最新評(píng)論