自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

萬字長文詳細分享 Redis 的常見業(yè)務(wù)場景

數(shù)據(jù)庫 Redis
String類型常用于緩存經(jīng)常訪問的數(shù)據(jù),如數(shù)據(jù)庫查詢結(jié)果、網(wǎng)頁內(nèi)容等,以提高訪問速度和降低數(shù)據(jù)庫的壓力 。

作者 | knightwwang

String類型

Redis的String數(shù)據(jù)結(jié)構(gòu)是一種基礎(chǔ)的鍵值對類型。

  • SET key value - 設(shè)置指定key的值。如果key已經(jīng)存在,這個命令會更新它的值。
SET myKey "myValue"
  • GET key - 獲取與key關(guān)聯(lián)的值。
GET myKey
  • DEL key - 刪除指定的key。
DEL myKey
  • INCR key- 將key中的數(shù)值增加1。如果key不存在,它將首先被設(shè)置為0。
INCR mycounter
  • DECR key- 將key中的數(shù)值減少1。
DECR mycounter

一、場景應(yīng)用場景分析

1.緩存功能

(1) 場景

緩存功能:String類型常用于緩存經(jīng)常訪問的數(shù)據(jù),如數(shù)據(jù)庫查詢結(jié)果、網(wǎng)頁內(nèi)容等,以提高訪問速度和降低數(shù)據(jù)庫的壓力 。

(2) 案例講解

① 背景

在商品系統(tǒng)中,商品的詳細信息如描述、價格、庫存等數(shù)據(jù)通常不會頻繁變動,但會被頻繁查詢。每次用戶訪問商品詳情時,都直接從數(shù)據(jù)庫查詢這些信息會導(dǎo)致不必要的數(shù)據(jù)庫負載。

② 優(yōu)勢

  • 快速數(shù)據(jù)訪問:Redis作為內(nèi)存數(shù)據(jù)庫,提供極速的讀寫能力,大幅降低數(shù)據(jù)訪問延遲,提升用戶體驗。
  • 減輕數(shù)據(jù)庫壓力:緩存頻繁訪問的靜態(tài)數(shù)據(jù),顯著減少數(shù)據(jù)庫查詢,從而保護數(shù)據(jù)庫資源,延長數(shù)據(jù)庫壽命。
  • 高并發(fā)支持:Redis設(shè)計用于高并發(fā)環(huán)境,能夠處理大量用戶同時訪問,保證系統(tǒng)在流量高峰時的穩(wěn)定性。
  • 靈活的緩存策略:易于實現(xiàn)緩存數(shù)據(jù)的更新和失效,結(jié)合適當(dāng)?shù)木彺孢^期和數(shù)據(jù)同步機制,確保數(shù)據(jù)的實時性和一致性。

③ 解決方案

使用Redis String類型來緩存商品的靜態(tài)信息。當(dāng)商品信息更新時,相應(yīng)的緩存也更新或失效。

偽代碼:

// 商品信息緩存鍵的生成
func generateProductCacheKey(productID string) string {
    return "product:" + productID
}

// 將商品信息存儲到Redis緩存中
func cacheProductInfo(productID string, productInfo map[string]interface{}) {
    cacheKey := generateProductCacheKey(productID)
    // 序列化商品信息為JSON格式
    productJSON, _ := json.Marshal(productInfo)
    // 將序列化后的商品信息存儲到Redis
    rdb.Set(ctx, cacheKey, string(productJSON), 0) // 0表示永不過期,實際使用時可以設(shè)置過期時間
}

// 從Redis緩存中獲取商品信息
func getProductInfoFromCache(productID string) (map[string]interface{}, error) {
    cacheKey := generateProductCacheKey(productID)
    // 從Redis獲取商品信息
    productJSON, err := rdb.Get(ctx, cacheKey).Result()
    if err != nil {
        return nil, err
    }
    // 反序列化JSON格式的商品信息
    var productInfo map[string]interface{}
    json.Unmarshal([]byte(productJSON), &productInfo)
    return productInfo, nil
}

// 當(dāng)商品信息更新時,同步更新Redis緩存
func updateProductInfoAndCache(productID string, newProductInfo map[string]interface{}) {
    // 更新數(shù)據(jù)庫中的商品信息

    // 更新Redis緩存中的商品信息
    cacheProductInfo(productID, newProductInfo)
}

2. 計數(shù)器

(1) 場景

計數(shù)器:利用INCR和DECR命令,String類型可以作為計數(shù)器使用,適用于統(tǒng)計如網(wǎng)頁訪問量、商品庫存數(shù)量等 。

(2) 案例講解

① 背景

對于文章的瀏覽量的統(tǒng)計,每篇博客文章都有一個唯一的標(biāo)識符(例如,文章ID)。每次文章被訪問時,文章ID對應(yīng)的瀏覽次數(shù)在Redis中遞增??梢远ㄆ趯g覽次數(shù)同步到數(shù)據(jù)庫,用于歷史數(shù)據(jù)分析。

② 優(yōu)勢

  • 實時性:能夠?qū)崟r更新和獲取文章的瀏覽次數(shù)。
  • 高性能:Redis的原子操作保證了高并發(fā)場景下的計數(shù)準確性。

③ 解決方案

通過Redis實現(xiàn)對博客文章瀏覽次數(shù)的原子性遞增和檢索,以優(yōu)化數(shù)據(jù)庫訪問并實時更新文章的瀏覽統(tǒng)計信息。

// recordArticleView 記錄文章的瀏覽次數(shù)
func recordArticleView(articleID string) {
    // 使用Redis的INCR命令原子性地遞增文章的瀏覽次數(shù)
    result, err := redisClient.Incr(ctx, articleID).Result()
    if err != nil {
        // 如果發(fā)生錯誤,記錄錯誤日志
        log.Printf("Error incrementing view count for article %s: %v", articleID, err)
        return
    }
    // 可選:記錄瀏覽次數(shù)到日志或進行其他業(yè)務(wù)處理
    fmt.Printf("Article %s has been viewed %d times\n", articleID, result)
}

// getArticleViewCount 從Redis獲取文章的瀏覽次數(shù)
func getArticleViewCount(articleID string) (int, error) {
    // 從Redis獲取文章的瀏覽次數(shù)
    viewCount, err := redisClient.Get(ctx, articleID).Result()
    if err != nil {
        if err == redis.Nil {
            // 如果文章ID在Redis中不存在,可以認為瀏覽次數(shù)為0
            return 0, nil
        } else {
            // 如果發(fā)生錯誤,記錄錯誤日志
            log.Printf("Error getting view count for article %s: %v", articleID, err)
            return 0, err
        }
    }
    // 將瀏覽次數(shù)從字符串轉(zhuǎn)換為整數(shù)
    count, err := strconv.Atoi(viewCount)
    if err != nil {
        log.Printf("Error converting view count to integer for article %s: %v", articleID, err)
        return 0, err
    }
    return count, nil
}

// renderArticlePage 渲染文章頁面,并顯示瀏覽次數(shù)
func renderArticlePage(articleID string) {
    // 在渲染文章頁面之前,記錄瀏覽次數(shù)
    recordArticleView(articleID)

    // 獲取文章瀏覽次數(shù)
    viewCount, err := getArticleViewCount(articleID)
    if err != nil {
        // 處理錯誤,例如設(shè)置瀏覽次數(shù)為0或跳過錯誤
        viewCount = 0
    }
}

3. 分布式鎖

(1) 場景

分布式鎖:通過SETNX命令(僅當(dāng)鍵不存在時設(shè)置值),String類型可以實現(xiàn)分布式鎖,保證在分布式系統(tǒng)中的互斥訪問 。

(2) 案例講解

① 背景

在分布式系統(tǒng)中,如電商的秒殺活動或庫存管理,需要確保同一時間只有一個進程或線程可以修改共享資源,以避免數(shù)據(jù)不一致的問題。

② 優(yōu)勢

  • 互斥性:確保同一時間只有一個進程可以訪問共享資源,防止數(shù)據(jù)競爭和沖突。
  • 高可用性:分布式鎖能夠在節(jié)點故障或網(wǎng)絡(luò)分區(qū)的情況下仍能正常工作,具備自動故障轉(zhuǎn)移和恢復(fù)的能力。
  • 可重入性:支持同一個進程或線程多次獲取同一個鎖,避免死鎖的發(fā)生。
  • 性能開銷:相比于其他分布式協(xié)調(diào)服務(wù),基于Redis的分布式鎖實現(xiàn)簡單且性能開銷較小。

③ 解決方案

使用Redis的SETNX命令實現(xiàn)分布式鎖的獲取和釋放,通過Lua腳本確保釋放鎖時的原子性,并在執(zhí)行業(yè)務(wù)邏輯前嘗試獲取鎖,業(yè)務(wù)邏輯執(zhí)行完畢后確保釋放鎖,從而保證在分布式系統(tǒng)中對共享資源的安全訪問。

// 偽代碼:在分布式系統(tǒng)中實現(xiàn)分布式鎖的功能

// 嘗試獲取分布式鎖
func tryGetDistributedLock(lockKey string, val string, expireTime int) bool {
    // 使用SET命令結(jié)合NX和PX參數(shù)嘗試獲取鎖
    // NX表示如果key不存在則可以設(shè)置成功
    // PX指定鎖的超時時間(毫秒)
    // 這里的val是一個隨機值,用于在釋放鎖時驗證鎖是否屬于當(dāng)前進程
    result, err := redisClient.SetNX(ctx, lockKey, val, time.Duration(expireTime)*time.Millisecond).Result()
    if err != nil {
        // 記錄錯誤,例如:日志記錄
        log.Printf("Error trying to get distributed lock for key %s: %v", lockKey, err)
        return false
    }
    // 如果result為1,則表示獲取鎖成功,result為0表示鎖已被其他進程持有
    return result == 1
}

// 釋放分布式鎖
func releaseDistributedLock(lockKey string, val string) {
    // 使用Lua腳本來確保釋放鎖的操作是原子性的
    script := `
        if redis.call("get", KEYS[1]) == ARGV[1] then
            return redis.call("del", KEYS[1])
        else
            return 0
        end
    `
    // 執(zhí)行Lua腳本
    result, err := redisClient.Eval(ctx, script, []string{lockKey}, val).Result()
    if err != nil {
        // 記錄錯誤
        log.Printf("Error releasing distributed lock for key %s: %v", lockKey, err)
    }
    // 如果result為1,則表示鎖被成功釋放,如果為0,則表示鎖可能已經(jīng)釋放或不屬于當(dāng)前進程
    if result == int64(0) {
        log.Printf("Failed to release the lock, it might have been released by others or expired")
    }
}

// 執(zhí)行業(yè)務(wù)邏輯,使用分布式鎖來保證業(yè)務(wù)邏輯的原子性
func executeBusinessLogic(lockKey string) {
    val := generateRandomValue() // 生成一個隨機值,作為鎖的值
    if tryGetDistributedLock(lockKey, val, 30000) { // 嘗試獲取鎖,30秒超時
        defer releaseDistributedLock(lockKey, val) // 無論業(yè)務(wù)邏輯是否成功執(zhí)行,都釋放鎖
        // 執(zhí)行具體的業(yè)務(wù)邏輯
        // ...
    } else {
        // 未能獲取鎖,處理重試邏輯或返回錯誤
        // ...
    }
}

// generateRandomValue 生成一個隨機值作為鎖的唯一標(biāo)識
func generateRandomValue() string {
    return strconv.FormatInt(time.Now().UnixNano(), 10)
}

4. 限流

(1) 場景

限流:使用EXPIRE命令,結(jié)合INCR操作,可以實現(xiàn)API的限流功能,防止系統(tǒng)被過度訪問 。

(2) 案例講解

① 背景

一個在線視頻平臺提供了一個API,用于獲取視頻的元數(shù)據(jù)。在高流量事件(如新電影發(fā)布)期間,這個API可能會收到大量并發(fā)請求,這可能導(dǎo)致后端服務(wù)壓力過大,甚至崩潰。

② 優(yōu)勢

  • 穩(wěn)定性保障:通過限流,可以防止系統(tǒng)在高負載下崩潰,確保核心服務(wù)的穩(wěn)定性。
  • 服務(wù)公平性:限流可以保證不同用戶和客戶端在高并發(fā)環(huán)境下公平地使用服務(wù)。
  • 防止濫用:限制API的調(diào)用頻率,可以防止惡意用戶或爬蟲對服務(wù)進行濫用。

③ 解決方案

  • 請求計數(shù):每次API請求時,使用INCR命令對特定的key進行遞增操作。
  • 設(shè)置過期時間:使用EXPIRE命令為計數(shù)key設(shè)置一個過期時間,過期時間取決于限流的時間窗口(例如1秒)。
  • 檢查請求頻率:如果請求計數(shù)超過設(shè)定的閾值(例如每秒100次),則拒絕新的請求或進行排隊。
// 偽代碼:API限流器
func rateLimiter(apiKey string, threshold int, timeWindow int) bool {
    currentCount, err := redisClient.Incr(ctx, apiKey).Result()
    if err != nil {
        log.Printf("Error incrementing API key %s: %v", apiKey, err)
        return false
    }

    // 如果當(dāng)前計數(shù)超過閾值,則拒絕請求
    if currentCount > threshold {
        return false
    }

    // 重置計數(shù)器的過期時間
    _, err = redisClient.Expire(ctx, apiKey, timeWindow).Result()
    if err != nil {
        log.Printf("Error resetting expire time for API key %s: %v", apiKey, err)
        return false
    }

    return true
}

// 在API處理函數(shù)中調(diào)用限流器
func handleAPIRequest(apiKey string) {
    if rateLimiter(apiKey, 100, 1) { // 限流閾值設(shè)為100,時間窗口為1秒
        // 處理API請求
    } else {
        // 限流,返回錯誤或提示信息
    }
}

5. 共享session

(1) 場景

在多服務(wù)器的Web應(yīng)用中,用戶在不同的服務(wù)器上請求時能夠保持登錄狀態(tài),實現(xiàn)會話共享。

(2) 案例講解

① 背景

考慮一個大型電商平臺,它使用多個服務(wù)器來處理用戶請求以提高可用性和伸縮性。當(dāng)用戶登錄后,其會話信息(session)需要在所有服務(wù)器間共享,以確保無論用戶請求到達哪個服務(wù)器,都能識別其登錄狀態(tài)。

② 優(yōu)勢

  • 用戶體驗:用戶在任何服務(wù)器上都能保持登錄狀態(tài),無需重復(fù)登錄。
  • 系統(tǒng)可靠性:集中管理session減少了因服務(wù)器故障導(dǎo)致用戶登錄狀態(tài)丟失的風(fēng)險。
  • 伸縮性:易于擴展系統(tǒng)以支持更多服務(wù)器,session管理不受影響。

③ 解決方案

使用Redis的String類型來集中存儲和管理用戶session信息。

  • 存儲Session:當(dāng)用戶登錄成功后,將用戶的唯一標(biāo)識(如session ID)和用戶信息序列化后存儲在Redis中。
  • 驗證Session:每次用戶請求時,通過請求中的session ID從Redis獲取session信息,驗證用戶狀態(tài)。
  • 更新Session:用戶活動時,更新Redis中存儲的session信息,以保持其活躍狀態(tài)。
  • 過期策略:設(shè)置session信息在Redis中的過期時間,當(dāng)用戶長時間不活動時自動使session失效。
// 偽代碼:用戶登錄并存儲session
func userLogin(username string, password string) (string, error) {
    // 驗證用戶名和密碼

    // 創(chuàng)建session ID
    sessionID := generateSessionID()

    // 序列化用戶信息
    userInfo := map[string]string{"username": username}
    serializedInfo, err := json.Marshal(userInfo)
    if err != nil {
        // 處理錯誤
        return "", err
    }

    // 存儲session信息到Redis,設(shè)置過期時間
    err = redisClient.Set(ctx, sessionID, string(serializedInfo), time.Duration(30)*time.Minute).Err()
    if err != nil {
        // 處理錯誤
        return "", err
    }

    return sessionID, nil
}

// 偽代碼:從請求中獲取并驗證session
func validateSession(sessionID string) (map[string]string, error) {
    // 從Redis獲取session信息
    serializedInfo, err := redisClient.Get(ctx, sessionID).Result()
    if err != nil {
        // 處理錯誤或session不存在
        return nil, err
    }

    // 反序列化用戶信息
    var userInfo map[string]string
    err = json.Unmarshal([]byte(serializedInfo), &userInfo)
    if err != nil {
        // 處理錯誤
        return nil, err
    }

    return userInfo, nil
}

// 偽代碼:生成新的session ID
func generateSessionID() string {
    return strconv.FormatInt(time.Now().UnixNano(), 36)
}

二、注意事項

  • String類型的值可以是任何形式的文本或二進制數(shù)據(jù),最大容量為512MB 。
  • 在使用String類型作為計數(shù)器時,應(yīng)確保操作的原子性,避免并發(fā)訪問導(dǎo)致的數(shù)據(jù)不一致 。
  • 使用分布式鎖時,要注意鎖的釋放和超時機制,防止死鎖的發(fā)生 。
  • 存儲對象時,應(yīng)考慮序列化和反序列化的成本,以及數(shù)據(jù)的壓縮和安全性 。
  • 在使用String類型作為緩存時,需要合理設(shè)置過期時間,以保證數(shù)據(jù)的時效性 。

List(列表)類型

Redis的List數(shù)據(jù)結(jié)構(gòu)是一個雙向鏈表,它支持在頭部或尾部添加和刪除元素,使其成為實現(xiàn)棧(后進先出)或隊列(先進先出)的理想選擇。

一、基本命令

  • LPUSH key value- 在列表的頭部插入元素。
LPUSH mylist "item1"
  • RPUSH key value- 在列表的尾部插入元素。
RPUSH mylist "item2"
  • LPOP key- 移除并獲取列表頭部的元素。
LPOP mylist
  • RPOP key- 移除并獲取列表尾部的元素。
RPOP mylist
  • LRANGE key start stop- 獲取列表中指定范圍內(nèi)的元素。
LRANGE mylist 0 -1

二、場景應(yīng)用場景分析

1.消息隊列

(1) 場景

消息隊列:List類型常用于實現(xiàn)消息隊列,用于異步處理任務(wù),如郵件發(fā)送隊列、任務(wù)調(diào)度等。

(2) 案例講解

① 背景

在一個電商平臺中,用戶下單后,系統(tǒng)需要執(zhí)行多個異步任務(wù),如訂單處理、庫存更新、發(fā)送確認郵件等。

② 優(yōu)勢

  • 異步處理:使用List作為消息隊列,可以將任務(wù)異步化,提高用戶體驗和系統(tǒng)響應(yīng)速度。
  • 任務(wù)管理:方便地對任務(wù)進行管理和監(jiān)控,如重試失敗的任務(wù)、監(jiān)控任務(wù)處理進度等。
  • 系統(tǒng)解耦:各個任務(wù)處理模塊可以獨立運行,降低系統(tǒng)間的耦合度。

③ 解決方案

使用Redis List類型存儲和管理任務(wù)消息隊列。

// 將新訂單添加到訂單處理隊列
func addOrderToQueue(order Order) {
    redisClient.LPUSH(ctx, "order_queue", order.ToString())
}

// 從訂單處理隊列中獲取待處理的訂單
func getNextOrder() (Order, error) {
    orderJSON, err := redisClient.RPOP(ctx, "order_queue").Result()
    if err != nil {
        return Order{}, err
    }
    var order Order
    json.Unmarshal([]byte(orderJSON), &order)
    return order, nil
}

// 訂單處理完成后,執(zhí)行后續(xù)任務(wù)
func processOrder(order Order) {
    // 處理訂單邏輯
    // ...
    // 發(fā)送確認郵件
    // ...
    // 更新庫存
    // ...
}

2. 排行榜

(1) 場景

排行榜:使用List類型,可以存儲和管理如游戲得分、文章點贊數(shù)等排行榜數(shù)據(jù)。

(2) 案例講解

① 背景

在一個社交平臺中,用戶發(fā)表的文章根據(jù)點贊數(shù)進行排名,需要實時更新和展示排行榜。

② 優(yōu)勢

  • 實時性:能夠快速響應(yīng)用戶的點贊行為,實時更新排行榜。
  • 排序功能:利用LRANGE命令,可以方便地獲取指定范圍內(nèi)的排行榜數(shù)據(jù)。

③ 解決方案

使用Redis List類型存儲用戶的得分或點贊數(shù),并根據(jù)需要對List進行排序。

// 為文章點贊,更新排行榜
func likeArticle(articleID string) {
    // 假設(shè)每個文章都有一個對應(yīng)的得分List
    redisClient.INCR(ctx, "article:"+articleID+":score")
    // 可以進一步使用Sorted Set來維護更復(fù)雜的排行榜
}

// 獲取文章排行榜
func getArticleRankings() []Article {
    articles := []Article{}
    // 遍歷所有文章的得分List
    // 根據(jù)得分進行排序
    // ...
    return articles
}

三、注意事項:

  • List類型在列表元素數(shù)量較大時,操作可能會變慢,需要考慮性能優(yōu)化。
  • 在使用List實現(xiàn)隊列時,要注意處理消息的順序和丟失問題。
  • 可以使用BRPOP或BLPOP命令在多個列表上進行阻塞式讀取,適用于多消費者場景。

Set(集合)類型

Redis的Set數(shù)據(jù)結(jié)構(gòu)是一個無序且元素唯一的集合,它支持集合運算,如添加、刪除、取交集、并集、差集等。這使得Set類型非常適合用于實現(xiàn)一些需要進行成員關(guān)系測試或集合操作的場景。

一、基本命令

  • SADD key member- 向指定的集合添加元素。
SADD mySet "item1"
  • SREM key member- 從集合中刪除元素。
SREM mySet "item1"
  • SISMEMBER key member- 檢查元素是否是集合的成員。
SISMEMBER mySet "item1"
  • SINTER key [key ...]- 取一個或多個集合的交集。
SINTER mySet myOtherSet
  • SUNION key [key ...]  - 取一個或多個集合的并集。
SUNION mySet myOtherSet
  • SDIFF key [key ...]  - 取一個集合與另一個集合的差集。
SDIFF mySet myOtherSet

二、場景應(yīng)用場景分析

1. 標(biāo)簽系統(tǒng)

(1) 場景

標(biāo)簽系統(tǒng):Set類型可用于存儲和處理具有標(biāo)簽特性的數(shù)據(jù),如商品標(biāo)簽、文章分類標(biāo)簽等。

(2) 案例講解

① 背景

在一個內(nèi)容平臺上,用戶可以給文章打上不同的標(biāo)簽,系統(tǒng)需要根據(jù)標(biāo)簽過濾和推薦文章。

② 優(yōu)勢

  • 快速查找:使用Set可以快速判斷一個元素是否屬于某個集合。
  • 靈活的標(biāo)簽管理:方便地添加和刪除標(biāo)簽,實現(xiàn)標(biāo)簽的靈活管理。
  • 集合運算:通過集合運算,如交集和并集,可以輕松實現(xiàn)復(fù)雜的標(biāo)簽過濾邏輯。

③ 解決方案

使用Redis Set類型存儲文章的標(biāo)簽集合,實現(xiàn)基于標(biāo)簽的推薦和搜索。

// 給文章添加標(biāo)簽
func addTagToArticle(articleID string, tag string) {
    redisClient.SADD(ctx, "article:"+articleID+":tags", tag)
}

// 根據(jù)標(biāo)簽獲取文章列表
func getArticlesByTag(tag string) []string {
    return redisClient.SMEMBERS(ctx, "global:tags:"+tag).Val()
}

// 獲取文章的所有標(biāo)簽
func getTagsOfArticle(articleID string) []string {
    return redisClient.SMEMBERS(ctx, "article:"+articleID+":tags").Val()
}

2. 社交網(wǎng)絡(luò)好友關(guān)系

(1) 場景

社交網(wǎng)絡(luò)好友關(guān)系:Set類型可以表示用戶的好友列表,支持快速好友關(guān)系測試和好友推薦。

(2) 案例講解

① 背景

在一個社交網(wǎng)絡(luò)應(yīng)用中,用戶可以添加和刪除好友,系統(tǒng)需要管理用戶的好友關(guān)系。

② 優(yōu)勢

  • 唯一性:保證好友列表中不會有重復(fù)的好友。
  • 快速關(guān)系測試:快速判斷兩個用戶是否互為好友。
  • 好友推薦:利用集合運算,如差集,推薦可能認識的好友。

③ 解決方案

使用Redis Set類型存儲用戶的好友集合,實現(xiàn)好友關(guān)系的管理。

// 添加好友
func addFriend(userOneID string, userTwoID string) {
    redisClient.SADD(ctx, "user:"+userOneID+":friends", userTwoID)
    redisClient.SADD(ctx, "user:"+userTwoID+":friends", userOneID)
}

// 判斷是否是好友
func isFriend(userOneID string, userTwoID string) bool {
    return redisClient.SISMEMBER(ctx, "user:"+userOneID+":friends", userTwoID).Val() == 1
}

// 獲取用戶的好友列表
func getFriendsOfUser(userID string) []string {
    return redisClient.SMEMBERS(ctx, "user:"+userID+":friends").Val()
}

三、注意事項:

  • 雖然Set是無序的,但Redis會保持元素的插入順序,直到集合被重新排序。
  • Set中的元素是唯一的,任何嘗試添加重復(fù)元素的操作都會無效。
  • 使用集合運算時,需要注意結(jié)果集的大小,因為它可能會影響性能。

Sorted Set類型

Redis的Sorted Set數(shù)據(jù)結(jié)構(gòu)是Set的一個擴展,它不僅能夠存儲唯一的元素,還能為每個元素關(guān)聯(lián)一個分數(shù)(score),并根據(jù)這個分數(shù)對元素進行排序。

一、基本命令

  • ZADD key score member- 向key對應(yīng)的Sorted Set中添加元素member,元素的分數(shù)為score。如果member已存在,則會更新其分數(shù)。
ZADD mySortedSet 5.0 element1
  • ZRANGE key start stop [WITHSCORES]- 獲取key對應(yīng)的Sorted Set中指定分數(shù)范圍內(nèi)的元素,可選地使用WITHSCORES獲取分數(shù)。
ZRANGE mySortedSet 0 -1 WITHSCORES
  • ZREM key member- 從key對應(yīng)的Sorted Set中刪除元素member。
ZREM mySortedSet element1
  • ZINCRBY key increment member- 為key中的member元素的分數(shù)增加increment的值。
ZINCRBY mySortedSet 2.5 element1
  • ZCARD key- 獲取key對應(yīng)的Sorted Set中元素的數(shù)量。
ZCARD mySortedSet

二、場景應(yīng)用場景分析

1. 排行榜系統(tǒng)

(1) 場景

排行榜系統(tǒng):Sorted Set類型非常適合實現(xiàn)排行榜系統(tǒng),如游戲得分排行榜、文章熱度排行榜等。

(2) 案例講解

① 背景

在一個在線游戲中,玩家的得分需要實時更新并顯示在排行榜上。使用Sorted Set可以方便地根據(jù)得分高低進行排序。

② 優(yōu)勢

  • 實時排序:根據(jù)玩家的得分自動排序,無需額外的排序操作。
  • 動態(tài)更新:可以快速地添加新玩家或更新現(xiàn)有玩家的得分。
  • 范圍查詢:方便地查詢排行榜的前N名玩家。

③ 解決方案

使用Redis Sorted Set來存儲和管理游戲玩家的得分排行榜。

偽代碼:

// 更新玩家得分
func updatePlayerScore(playerID string, score float64) {
    sortedSetKey := "playerScores"
    // 添加或更新玩家得分
    rdb.ZAdd(ctx, sortedSetKey, &redis.Z{Score: score, Member: playerID})
}

// 獲取排行榜
func getLeaderboard(start int, stop int) []string {
    sortedSetKey := "playerScores"
    // 獲取排行榜數(shù)據(jù)
    leaderboard, _ := rdb.ZRangeWithScores(ctx, sortedSetKey, start, stop).Result()
    var result []string
    for _, entry := range leaderboard {
        result = append(result, fmt.Sprintf("%s: %.2f", entry.Member.(string), entry.Score))
    }
    return result
}

2. 實時數(shù)據(jù)統(tǒng)計

(1) 場景

實時數(shù)據(jù)統(tǒng)計:Sorted Set可以用于實時數(shù)據(jù)統(tǒng)計,如網(wǎng)站的訪問量統(tǒng)計、商品的銷量統(tǒng)計等。

(2) 案例講解

① 背景

在一個電商平臺中,需要統(tǒng)計商品的銷量,并根據(jù)銷量對商品進行排序展示。

② 優(yōu)勢

  • 自動排序:根據(jù)銷量自動對商品進行排序。
  • 靈活統(tǒng)計:可以按時間段統(tǒng)計銷量,如每日、每周等。

③ 解決方案

使用Redis Sorted Set來實現(xiàn)商品的銷量統(tǒng)計和排序。

偽代碼:

// 更新商品銷量
func updateProductSales(productID string, sales int64) {
    sortedSetKey := "productSales"
    // 增加商品銷量
    rdb.ZIncrBy(ctx, sortedSetKey, float64(sales), productID)
}

// 獲取商品銷量排行
func getProductSalesRanking() []string {
    sortedSetKey := "productSales"
    // 獲取銷量排行數(shù)據(jù)
    ranking, _ := rdb.ZRangeWithScores(ctx, sortedSetKey, 0, -1).Result()
    var result []string
    for _, entry := range ranking {
        result = append(result, fmt.Sprintf("%s: %d", entry.Member.(string), int(entry.Score)))
    }
    return result
}

三、注意事項:

  • Sorted Set中的分數(shù)可以是浮點數(shù),這使得它可以用于更精確的排序需求。
  • 元素的分數(shù)可以動態(tài)更新,但應(yīng)注意更新操作的性能影響。
  • 使用Sorted Set進行范圍查詢時,應(yīng)注意合理設(shè)計分數(shù)的分配策略,以避免性能瓶頸。
  • 在設(shè)計排行榜或其他需要排序的功能時,應(yīng)考慮數(shù)據(jù)的時效性和更新頻率,選擇合適的數(shù)據(jù)結(jié)構(gòu)和索引策略。

Hash類型

Redis的Hash數(shù)據(jù)結(jié)構(gòu)是一種鍵值對集合,其中每個鍵(field)對應(yīng)一個值(value),整個集合與一個主鍵(key)關(guān)聯(lián)。這種結(jié)構(gòu)非常適合存儲對象或鍵值對集合。

一、基本命令

  • HSET key field value- 為指定的key設(shè)置field的值。如果key不存在,會創(chuàng)建一個新的Hash。如果field已經(jīng)存在,則會更新它的值。
HSET myHash name "John Doe"
  • HGET key field- 獲取與key關(guān)聯(lián)的field的值。
HGET myHash name
  • HDEL key field- 刪除key中的field。
HDEL myHash name
  • HINCRBY key field increment- 將key中的field的整數(shù)值增加increment。
HINCRBY myHash age 1
  • HGETALL key- 獲取key中的所有字段和值。
HGETALL myHash

二、場景應(yīng)用場景分析

1. 用戶信息存儲

(1) 場景

用戶信息存儲:Hash類型常用于存儲和管理用戶信息,如用戶ID、姓名、年齡、郵箱等。

(2) 案例講解

① 背景

在社交網(wǎng)絡(luò)應(yīng)用中,每個用戶都有一系列屬性,如用戶名、年齡、興趣愛好等。使用Hash類型可以方便地存儲和查詢單個用戶的詳細信息。

② 優(yōu)勢

  • 結(jié)構(gòu)化存儲:將用戶信息以字段和值的形式存儲,易于理解和操作。
  • 快速讀寫:Redis的Hash操作提供高速的讀寫性能。
  • 靈活更新:可以單獨更新用戶信息中的某個字段,而無需重新設(shè)置整個對象。

③ 解決方案

使用Redis Hash類型來存儲和管理用戶信息。當(dāng)用戶信息更新時,只更新Hash中的對應(yīng)字段。

偽代碼:

// 存儲用戶信息到Redis Hash
func storeUserInfo(userID string, userInfo map[string]interface{}) {
    hashKey := "user:" + userID
    // 將用戶信息存儲到Redis的Hash中
    for field, value := range userInfo {
        rdb.HSet(ctx, hashKey, field, value)
    }
}

// 從Redis Hash獲取用戶信息
func getUserInfo(userID string) map[string]string {
    hashKey := "user:" + userID
    // 從Redis獲取用戶信息
    fields, err := rdb.HGetAll(ctx, hashKey).Result()
    if err != nil {
        // 處理錯誤
        return nil
    }
    // 將字段轉(zhuǎn)換為字符串映射
    var userInfo = make(map[string]string)
    for k, v := range fields {
        userInfo[k] = v
    }
    return userInfo
}

2. 購物車管理

(1) 場景

購物車管理:Hash類型可以用于實現(xiàn)購物車功能,其中每個用戶的購物車是一個Hash,商品ID作為字段,數(shù)量作為值。

(2) 案例講解

① 背景

在電商平臺中,用戶的購物車需要記錄用戶選擇的商品及其數(shù)量。使用Hash類型可以有效地管理每個用戶的購物車。

② 優(yōu)勢

快速添加和修改:可以快速添加商品到購物車或更新商品數(shù)量。

批量操作:可以一次性獲取或更新購物車中的多個商品。

③ 解決方案

使用Redis Hash類型來實現(xiàn)購物車功能,每個用戶的購物車作為一個獨立的Hash存儲。

偽代碼:

// 添加商品到購物車
func addToCart(cartID string, productID string, quantity int) {
    cartKey := "cart:" + cartID
    // 使用HINCRBY命令增加商品數(shù)量
    rdb.HIncrBy(ctx, cartKey, productID, int64(quantity))
}

// 獲取購物車中的商品和數(shù)量
func getCart(cartID string) map[string]int {
    cartKey := "cart:" + cartID
    // 從Redis獲取購物車內(nèi)容
    items, err := rdb.HGetAll(ctx, cartKey).Result()
    if err != nil {
        // 處理錯誤
        return nil
    }
    // 將商品ID和數(shù)量轉(zhuǎn)換為映射
    var cart map[string]int
    for productID, quantity := range items {
        cart[productID], _ = strconv.Atoi(quantity)
    }
    return cart
}

三、注意事項:

Hash類型的字段值可以是字符串,最大容量為512MB。

在并發(fā)環(huán)境下,應(yīng)確保對Hash的操作是線程安全的,可以使用事務(wù)或Lua腳本來保證。

存儲較大的Hash時,應(yīng)注意性能和內(nèi)存使用情況,合理設(shè)計數(shù)據(jù)結(jié)構(gòu)以避免過度膨脹。

定期清理和維護Hash數(shù)據(jù),避免數(shù)據(jù)冗余和失效數(shù)據(jù)的累積。

Bitmap類型

Redis的Bitmap是一種基于String類型的特殊數(shù)據(jù)結(jié)構(gòu),它使用位(bit)來表示信息,每個位可以是0或1。Bitmap非常適合用于需要快速操作大量獨立開關(guān)狀態(tài)的場景,如狀態(tài)監(jiān)控、計數(shù)器等。

一、基本命令

  • SETBIT key offset value- 對key指定的offset位置設(shè)置位值。value可以是0或1。
SETBIT myBitmap 100 1
  • GETBIT key offset- 獲取key在指定offset位置的位值。
GETBIT myBitmap 100
  • BITCOUNT key [start end]- 計算key中位值為1的數(shù)量??蛇x地,可以指定一個范圍[start end]來計算該范圍內(nèi)的位值。
BITCOUNT myBitmap
  • BITOP operation destkey key [key ...]- 對一個或多個鍵進行位操作(AND, OR, XOR, NOT)并將結(jié)果存儲在destkey中。
BITOP AND resultBitmap key1 key2

二、場景應(yīng)用場景分析

1. 狀態(tài)監(jiān)控

(1) 場景

狀態(tài)監(jiān)控:Bitmap類型可以用于監(jiān)控大量狀態(tài),例如用戶在線狀態(tài)、設(shè)備狀態(tài)等。

(2) 案例講解

① 背景

在一個大型在線游戲平臺中,需要實時監(jiān)控成千上萬的玩家是否在線。使用Bitmap可以高效地記錄每個玩家的在線狀態(tài)。

② 優(yōu)勢

  • 空間效率:使用位來存儲狀態(tài),極大地節(jié)省了存儲空間。
  • 快速讀寫:Bitmap操作可以快速地讀取和更新狀態(tài)。
  • 批量操作:可以對多個狀態(tài)位執(zhí)行批量操作。

③ 解決方案

使用Redis Bitmap來存儲和查詢玩家的在線狀態(tài)。

偽代碼:

// 更新玩家在線狀態(tài)
func updatePlayerStatus(playerID int, isOnline bool) {
    bitmapKey := "playerStatus"
    offset := playerID // 假設(shè)playerID可以直接用作offset
    if isOnline {
        rdb.SetBit(ctx, bitmapKey, int64(offset), 1)
    } else {
        rdb.SetBit(ctx, bitmapKey, int64(offset), 0)
    }
}

// 查詢玩家在線狀態(tài)
func checkPlayerStatus(playerID int) bool {
    bitmapKey := "playerStatus"
    offset := playerID // 假設(shè)playerID可以直接用作offset
    bitValue, err := rdb.GetBit(ctx, bitmapKey, int64(offset)).Result()
    if err != nil {
        // 處理錯誤
        return false
    }
    return bitValue == 1
}

2. 功能開關(guān)

(1) 場景

功能開關(guān):Bitmap類型可以用于控制功能開關(guān),例如A/B測試、特性發(fā)布等。

(2) 案例講解

① 背景

在一個SaaS產(chǎn)品中,需要對新功能進行A/B測試,只對部分用戶開放。使用Bitmap可以快速地控制哪些用戶可以訪問新功能。

② 優(yōu)勢

靈活控制:可以快速開啟或關(guān)閉特定用戶的訪問權(quán)限。

易于擴展:隨著用戶數(shù)量的增加,Bitmap可以無縫擴展。

③ 解決方案

使用Redis Bitmap來作為功能開關(guān),控制用戶對新功能的訪問。

偽代碼:

// 為用戶設(shè)置功能訪問權(quán)限
func setFeatureAccess(userID int, hasAccess bool) {
    featureKey := "featureAccess"
    offset := userID // 假設(shè)userID可以直接用作offset
    if hasAccess {
        rdb.SetBit(ctx, featureKey, int64(offset), 1)
    } else {
        rdb.SetBit(ctx, featureKey, int64(offset), 0)
    }
}

// 檢查用戶是否有新功能訪問權(quán)限
func checkFeatureAccess(userID int) bool {
    featureKey := "featureAccess"
    offset := userID // 假設(shè)userID可以直接用作offset
    bitValue, err := rdb.GetBit(ctx, featureKey, int64(offset)).Result()
    if err != nil {
        // 處理錯誤
        return false
    }
    return bitValue == 1
}

注意事項:

  • Bitmap操作是原子性的,適合用于并發(fā)場景。
  • Bitmap使用String類型底層實現(xiàn),所以它的最大容量與String類型相同,為512MB。
  • 位操作可以快速執(zhí)行,但應(yīng)注意不要超出內(nèi)存和性能的限制。
  • 在設(shè)計Bitmap應(yīng)用時,應(yīng)考慮數(shù)據(jù)的稀疏性,以避免不必要的內(nèi)存浪費。

HyperLogLog類型

Redis的HyperLogLog數(shù)據(jù)結(jié)構(gòu)是一種概率數(shù)據(jù)結(jié)構(gòu),用于統(tǒng)計集合中唯一元素的數(shù)量,其特點是使用固定量的空間(通常為2KB),并且可以提供非常接近準確值的基數(shù)估計。

一、基本命令

  • PFADD key element [element ...]- 向key對應(yīng)的HyperLogLog中添加元素。如果key不存在,會創(chuàng)建一個新的HyperLogLog。
PFADD myUniqueSet element1 element2
  • PFCOUNT key- 獲取key對應(yīng)的HyperLogLog中的基數(shù),即唯一元素的數(shù)量。
PFCOUNT myUniqueSet
  • PFMERGE destkey sourcekey [sourcekey ...]- 將多個HyperLogLog集合合并到一個destkey中。
PFMERGE mergedSet myUniqueSet1 myUniqueSet2

二、場景應(yīng)用場景分析

1. 唯一用戶訪問統(tǒng)計

(1) 場景

唯一用戶訪問統(tǒng)計:HyperLogLog類型非常適合用來統(tǒng)計一段時間內(nèi)訪問網(wǎng)站或應(yīng)用的唯一用戶數(shù)量。

(2) 案例講解

① 背景

在一個新聞門戶網(wǎng)站,需要統(tǒng)計每天訪問的唯一用戶數(shù)量,以評估用戶基礎(chǔ)和內(nèi)容的吸引力。

② 優(yōu)勢

  • 空間效率:使用極小的空間即可統(tǒng)計大量數(shù)據(jù)。
  • 近似準確:提供近似但非常準確的基數(shù)估計。
  • 性能:即使在高并發(fā)情況下也能保證高性能。

③ 解決方案

使用Redis HyperLogLog來統(tǒng)計每天訪問的唯一用戶數(shù)量。

偽代碼:

// 記錄用戶訪問
func recordUserVisit(userID string) {
    uniqueSetKey := "uniqueVisitors"
    // 向HyperLogLog中添加用戶ID
    rdb.PFAdd(ctx, uniqueSetKey, userID)
}

// 獲取唯一用戶訪問量
func getUniqueVisitorCount() int64 {
    uniqueSetKey := "uniqueVisitors"
    // 獲取HyperLogLog中的基數(shù)
    count, _ := rdb.PFCount(ctx, uniqueSetKey).Result()
    return count
}

2. 事件獨立性分析

(1) 場景

事件獨立性分析:HyperLogLog可以用于分析不同事件的獨立性,例如,不同來源的點擊事件是否來自相同的用戶群體。

(2) 案例講解

① 背景

在一個廣告平臺,需要分析不同廣告來源的點擊事件是否由相同的用戶群體產(chǎn)生。

② 優(yōu)勢

  • 多集合統(tǒng)計:可以獨立統(tǒng)計不同事件的基數(shù)。
  • 合并分析:可以合并多個HyperLogLog集合進行綜合分析。

③ 解決方案

使用Redis HyperLogLog來獨立統(tǒng)計不同廣告來源的點擊事件,并合并集合以分析用戶群體的獨立性。

偽代碼:

// 記錄點擊事件
func recordClickEvent(clickID string, sourceID string) {
    sourceSetKey := "clicks:" + sourceID
    // 向?qū)?yīng)來源的HyperLogLog中添加點擊ID
    rdb.PFAdd(ctx, sourceSetKey, clickID)
}

// 合并點擊事件集合并分析用戶群體獨立性
func analyzeAudienceIndependence(sourceIDs []string) int64 {
    destKey := "mergedClicks"
    // 合并所有來源的HyperLogLog集合
    rdb.PFMerge(ctx, destKey, sourceIDs)
    // 計算合并后的基數(shù)
    count, _ := rdb.PFCount(ctx, destKey).Result()
    return count
}

三、注意事項:

  • HyperLogLog提供的是近似值,對于大多數(shù)應(yīng)用場景來說已經(jīng)足夠準確。
  • 由于HyperLogLog的特性,它不適合用于精確計數(shù)或小規(guī)模數(shù)據(jù)集的統(tǒng)計。
  • 可以安全地將多個HyperLogLog集合合并,合并操作不會增加內(nèi)存使用量,并且結(jié)果是一個更準確的基數(shù)估計。
  • 在設(shè)計使用HyperLogLog的場景時,應(yīng)考慮數(shù)據(jù)的更新頻率和集合的數(shù)量,以確保性能和準確性。

GEO類型

Redis的GEO數(shù)據(jù)結(jié)構(gòu)用于存儲地理位置信息,它允許用戶進行各種基于地理位置的操作,如查詢附近的位置、計算兩個地點之間的距離等。

一、基本命令

  • GEOADD key longitude latitude member- 向key對應(yīng)的GEO集合中添加帶有經(jīng)緯度的成員member。
GEOADD myGeoSet 116.407526 39.904030 "Beijing"
  • GEOPOS key member [member ...]- 返回一個或多個成員的地理坐標(biāo)。
GEOPOS myGeoSet "Beijing"
  • GEODIST key member1 member2 [unit]- 計算兩個成員之間的距離。
GEODIST myGeoSet "Beijing" "Shanghai"
  • GEOHASH key member [member ...]- 返回一個或多個成員的Geohash表示。
GEOHASH myGeoSet "Beijing"
  • GEORADIUS key longitude latitude radius unit [WITHCOORD] [WITHDIST] [WITHHASH]- 查詢給定位置周圍指定半徑內(nèi)的所有成員。
GEORADIUS myGeoSet 116.407526 39.904030 500 km WITHCOORD WITHDIST WITHHASH

二、場景應(yīng)用場景分析

1. 附近地點搜索

(1) 場景

附近地點搜索:GEO類型可以用于實現(xiàn)基于地理位置的搜索功能,如查找附近的餐館、影院等。

(2) 案例講解

① 背景

在一個旅游應(yīng)用中,用戶需要查找當(dāng)前位置附近的旅游景點。

② 優(yōu)勢

  • 精確搜索:基于真實地理坐標(biāo)進行搜索,結(jié)果精確。
  • 靈活的搜索范圍:可以自定義搜索半徑,適應(yīng)不同的搜索需求。

③ 解決方案

使用Redis GEO類型來實現(xiàn)基于用戶當(dāng)前位置的附近地點搜索。

偽代碼:

// 搜索附近地點
func searchNearbyPlaces(longitude float64, latitude float64, radius float64) []string {
    geoSetKey := "touristSpots"
    // 執(zhí)行GEORADIUS命令搜索附近地點
    places, _ := rdb.GeoRadius(
        ctx, geoSetKey, longitude, latitude, radius, "km",
        &redis.GeoRadiusQuery{WithCoord: true, WithDist: true, WithHash: true}).Result()
    
    var nearbyPlaces []string
    for _, place := range places {
        nearbyPlaces = append(nearbyPlaces, fmt.Sprintf("Name: %s, Distance: %.2f km", place.Member.(string), place.Dist))
    }
    return nearbyPlaces
}

2. 用戶定位與導(dǎo)航

(1) 場景

用戶定位與導(dǎo)航:GEO類型可以用于記錄用戶的地理位置,并提供導(dǎo)航服務(wù)。

(2) 案例講解

① 背景

在一個打車應(yīng)用中,需要根據(jù)司機和乘客的位置進行匹配,并提供導(dǎo)航服務(wù)。

② 優(yōu)勢

  • 實時定位:能夠?qū)崟r記錄和更新司機和乘客的位置。
  • 距離計算:快速計算司機與乘客之間的距離,為匹配提供依據(jù)。

③ 解決方案

使用Redis GEO類型來記錄司機和乘客的位置,并計算他們之間的距離。

偽代碼:

// 記錄司機位置
func recordDriverPosition(driverID string, longitude float64, latitude float64) {
    geoSetKey := "drivers"
    rdb.GeoAdd(ctx, geoSetKey, &redis.GeoLocation{Longitude: longitude, Latitude: latitude, Member: driverID})
}

// 計算司機與乘客之間的距離并匹配
func matchDriverToPassenger(passengerLongitude float64, passengerLatitude float64) (string, float64) {
    driversGeoSetKey := "drivers"
    passengerGeoSetKey := "passengers"
    // 記錄乘客位置
    rdb.GeoAdd(ctx, passengerGeoSetKey, &redis.GeoLocation{Longitude: passengerLongitude, Latitude: latitude, Member: "PassengerID"})

    // 搜索最近的司機
    nearestDriver, _ := rdb.GeoRadius(
        ctx, driversGeoSetKey, passengerLongitude, passengerLatitude, 5, "km",
        &redis.GeoRadiusQuery{Count: 1, Any: true}).Result()

    if len(nearestDriver) > 0 {
        // 計算距離
        distance := nearestDriver[0].Dist
        return nearestDriver[0].Member.(string), distance
    }
    return "", 0
}

三、注意事項:

  • GEO類型操作依賴于經(jīng)緯度的準確性,因此在添加位置信息時應(yīng)確保數(shù)據(jù)的準確。
  • GEO類型提供了豐富的地理位置查詢功能,但應(yīng)注意不同查詢操作的性能影響。
  • 在使用GEORADIUS等查詢命令時,應(yīng)考慮查詢半徑的大小,以平衡查詢結(jié)果的準確性和性能。
  • GEO類型是Redis較新的功能,使用時應(yīng)注意版本兼容性。
責(zé)任編輯:趙寧寧 來源: 騰訊技術(shù)工程
相關(guān)推薦

2021-10-18 11:58:56

負載均衡虛擬機

2022-09-06 08:02:40

死鎖順序鎖輪詢鎖

2022-04-25 10:56:33

前端優(yōu)化性能

2021-01-19 05:49:44

DNS協(xié)議

2022-09-14 09:01:55

shell可視化

2024-03-07 18:11:39

Golang采集鏈接

2020-07-15 08:57:40

HTTPSTCP協(xié)議

2020-11-16 10:47:14

FreeRTOS應(yīng)用嵌入式

2023-06-12 08:49:12

RocketMQ消費邏輯

2020-07-09 07:54:35

ThreadPoolE線程池

2022-07-19 16:03:14

KubernetesLinux

2022-10-10 08:35:17

kafka工作機制消息發(fā)送

2024-05-10 12:59:58

PyTorch人工智能

2021-08-26 05:02:50

分布式設(shè)計

2024-01-11 09:53:31

面試C++

2022-09-08 10:14:29

人臉識別算法

2022-07-15 16:31:49

Postman測試

2024-01-05 08:30:26

自動駕駛算法

2021-06-04 07:27:24

sourcemap前端技術(shù)

2022-02-15 18:45:35

Linux進程調(diào)度器
點贊
收藏

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