go中redis使用的示例代碼
在Go語(yǔ)言中使用Redis進(jìn)行數(shù)據(jù)存儲(chǔ)和管理可以帶來(lái)高效和靈活的優(yōu)勢(shì)。下面的講解包括單機(jī)模式、哨兵模式和集群模式的部署及使用。
一、Redis 簡(jiǎn)介
Redis是一個(gè)高性能的內(nèi)存數(shù)據(jù)庫(kù),支持多種數(shù)據(jù)結(jié)構(gòu),如字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(ZSet)等。它還支持事務(wù)、批量操作和流式數(shù)據(jù)處理。Redis常用于緩存、實(shí)時(shí)數(shù)據(jù)處理、計(jì)數(shù)器、消息隊(duì)列等場(chǎng)景。
二、Go中Redis的使用
1. 安裝Go Redis包
推薦使用github.com/go-redis/redis/v9
,它支持單機(jī)、哨兵和集群模式。
安裝命令:
go get github.com/go-redis/redis/v9
2. 單機(jī)模式
特點(diǎn):?jiǎn)我籖edis實(shí)例,簡(jiǎn)單易用,適合開(kāi)發(fā)和測(cè)試環(huán)境。
連接示例
package main import ( "context" "fmt" "github.com/go-redis/redis/v9" ) func main() { ctx := context.Background() client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, }) pong, err := client.Ping(ctx).Result() if err != nil { fmt.Printf("連接失?。?v\n", err) return } fmt.Println(pong) // 輸出:PONG // 示例:設(shè)置和獲取值 if err := client.Set(ctx, "key", "value", 0).Err(); err != nil { fmt.Printf("設(shè)置失?。?v\n", err) return } val, err := client.Get(ctx, "key").Result() if err != nil { fmt.Printf("獲取失敗:%v\n", err) return } fmt.Printf("值:%v\n", val) }
說(shuō)明:
- 使用
redis.NewClient
創(chuàng)建客戶端。 - 使用
Ping
測(cè)試連接。 Set
設(shè)置鍵值,Get
讀取值。
3. 哨兵模式
特點(diǎn):提供高可用性,當(dāng)主庫(kù)故障時(shí),自動(dòng)故障轉(zhuǎn)移。
依賴
- 必須安裝Redis哨兵服務(wù)。
連接示例
package main import ( "context" "fmt" "github.com/go-redis/redis/v9" ) func main() { ctx := context.Background() client := redis.NewFailoverClient(&redis.FailoverOptions{ MasterName: "mymaster", SentinelAddrs: []string{"localhost:26379", "localhost:26380", "localhost:26381"}, }) pong, err := client.Ping(ctx).Result() if err != nil { fmt.Printf("連接失?。?v\n", err) return } fmt.Println(pong) // 示例:設(shè)置和獲取值 if err := client.Set(ctx, "key", "value", 0).Err(); err != nil { fmt.Printf("設(shè)置失?。?v\n", err) return } val, err := client.Get(ctx, "key").Result() if err != nil { fmt.Printf("獲取失敗:%v\n", err) return } fmt.Printf("值:%v\n", val) }
說(shuō)明:
- 使用
redis.NewFailoverClient
創(chuàng)建客戶端。 - 指定主庫(kù)名稱
MasterName
和哨兵地址。 - 當(dāng)主庫(kù)故障時(shí),哨兵會(huì)自動(dòng)將從庫(kù)提升為主庫(kù)。
三、Redis集群
1. 集群模式
特點(diǎn):通過(guò)分片實(shí)現(xiàn)水平擴(kuò)展,每個(gè)節(jié)點(diǎn)處理一部分?jǐn)?shù)據(jù),適合高并發(fā)場(chǎng)景。
集群部署
部署結(jié)構(gòu)
- 6節(jié)點(diǎn):3主3從,每個(gè)主節(jié)點(diǎn)負(fù)責(zé)一個(gè)分片。
- 每個(gè)分片有1主1從。
使用redis-cli創(chuàng)建集群
- 啟動(dòng)6個(gè)Redis實(shí)例,分別指定不同端口。
- 使用
redis-cli
命令創(chuàng)建集群。
創(chuàng)建集局腳本:例如 start_cluster.sh
# 啟動(dòng)6個(gè)節(jié)點(diǎn),端口分別為30001到30006 for port in {30001..30006}; do redis-server --cluster-enabled yes --cluster-config-file node-${port}.conf --port ${port} --daemonize yes done # 創(chuàng)建集群 redis-cli --cluster create 127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 --cluster-replicas 1
在上面的腳本中,node-${port}.conf
是 Redis 集群模式下每個(gè)節(jié)點(diǎn)的配置文件,用于指定節(jié)點(diǎn)的運(yùn)行參數(shù)。如果這些配置文件不存在,Redis 會(huì)自動(dòng)生成一個(gè)默認(rèn)的配置文件,但為了確保集群部署的正確性,最好手動(dòng)創(chuàng)建這些配置文件。
例如:
# node-30001.conf cluster-enabled yes port 30001 bind 127.0.0.1 daemonize yes logfile /var/log/redis/redis_30001.log dir ./data/30001 save 60 1 appendonly yes
解釋:
cluster-enabled yes
: 啟用集群模式。port
: 指定當(dāng)前節(jié)點(diǎn)的端口。bind
: 綁定主機(jī)地址。daemonize yes
: 后臺(tái)運(yùn)行。logfile
: 指定日志文件路徑。dir
: 指定數(shù)據(jù)文件存儲(chǔ)路徑。save
: 配置數(shù)據(jù)持久化策略。appendonly
: 啟用 AOF 持久化。
然后給腳本賦予執(zhí)行權(quán)限并運(yùn)行:
chmod +x start_cluster.sh ./start_cluster.sh
連接示例
package main import ( "context" "fmt" "github.com/go-redis/redis/v9" ) func main() { // 集群節(jié)點(diǎn)地址 addresses := []string{ "localhost:30001", "localhost:30002", "localhost:30003", "localhost:30004", "localhost:30005", "localhost:30006", } clusterClient := redis.NewClusterClient(&redis.ClusterOptions{ Addrs: addresses, }) pong, err := clusterClient.Ping(context.Background()).Result() if err != nil { fmt.Printf("連接失?。?v\n", err) return } fmt.Println(pong) // 設(shè)置鍵值對(duì) if err := clusterClient.Set(context.Background(), "key", "value", 0).Err(); err != nil { fmt.Printf("設(shè)置失敗:%v\n", err) return } // 獲取值 val, err := clusterClient.Get(context.Background(), "key").Result() if err != nil { fmt.Printf("獲取失?。?v\n", err) return } fmt.Printf("值:%v\n", val) }
說(shuō)明:
- 使用
redis.NewClusterClient
創(chuàng)建集群客戶端。 - 初始化時(shí)提供所有節(jié)點(diǎn)地址。
- 集群模式下,Redis自動(dòng)處理數(shù)據(jù)分片和請(qǐng)求路由。
四、常用數(shù)據(jù)結(jié)構(gòu)與操作
1. 字符串(String)
// 設(shè)置過(guò)期時(shí)間 err = client.Set(context.Background(), "key", "value", 10*time.Second).Err() // 遞增計(jì)數(shù)器 num, err := client.Incr(context.Background(), "counter").Result() // 遞減計(jì)數(shù)器 num, err := client.Decr(context.Background(), "counter").Result()
2. 哈希(Hash)
// 設(shè)置字段值 err = client.HSet(context.Background(), "hashKey", "field", "value").Err() // 獲取字段值 value, err := client.HGet(context.Background(), "hashKey", "field").Result()
3. 列表(List)
// 從左邊推入元素 err = client.LPush(context.Background(), "listKey", "value").Err() // 彈出左邊第一個(gè)元素 value, err := client.LPop(context.Background(), "listKey").Result()
4. 集合(Set)
// 添加元素 err = client.SAdd(context.Background(), "setKey", "element").Err() // 移除元素 err = client.SRem(context.Background(), "setKey", "element").Err() // 獲取所有元素 members, err := client.SMembers(context.Background(), "setKey").Result()
5. 有序集合(ZSet)
// 添加元素并設(shè)置分?jǐn)?shù) err = client.ZAdd(context.Background(), "zsetKey", &redis.Z{Member: "element", Score: 100}).Err() // 獲取元素的分?jǐn)?shù) score, err := client.ZScore(context.Background(), "zsetKey", "element").Result() // 獲取排名 rank, err := client.ZRank(context.Background(), "zsetKey", "element").Result()
五、事務(wù)與批量操作
1. 事務(wù)
// 開(kāi)始事務(wù) ctx := context.Background() tx, err := client.Tx(ctx) // 執(zhí)行事務(wù)中的操作 _, err = tx.Pipeline()( function(ctx context.Context) (_redis.CMDCb, error) { _, err := tx.Get(ctx, "balance").Result() if err != nil { return nil, err } _, err := tx.Incr(ctx, "balance").Result() if err != nil { return nil, err } return nil, nil }, ) if err != nil { fmt.Printf("事務(wù)執(zhí)行失?。?v\n", err) }
2. 管道技術(shù)
// 創(chuàng)建管道 pipe := client.Pipeline() // 執(zhí)行多個(gè)命令 cmds, err := pipe.Set(context.Background(), "key1", "value1", 0). Set(context.Background(), "key2", "value2", 0). Exec(context.Background()) if err != nil { fmt.Printf("管道執(zhí)行失?。?v\n", err) return } // 打印結(jié)果 for _, cmd := range cmds { fmt.Printf("%v\n", cmd) }
六、高可用性
1. 復(fù)制(主從)
- 設(shè)置主從復(fù)制,確保數(shù)據(jù)安全。
- 主庫(kù)寫(xiě)入,數(shù)據(jù)同步到從庫(kù)。
- 從庫(kù)可用于讀分離,提高讀性能。
2. 故障轉(zhuǎn)移
- 使用哨兵模式實(shí)現(xiàn)自動(dòng)故障轉(zhuǎn)移。
- 集群模式下,節(jié)點(diǎn)故障自動(dòng)遷移。
3. 連接池
// 配置連接池 pool := &redis.Pool{ Dial: func(context.Context) (redis.Conn, error) { return client.DialContext(context.Background()) }, MaxActive: 10, // 最大活躍連接數(shù) MaxIdle: 5, // 最大空閑連接數(shù) IdleTimeout: 5 * time.Minute, } // 使用連接池 conn := pool.Get(context.Background()) defer conn.Close()
七、監(jiān)控與性能調(diào)優(yōu)
1. 內(nèi)置工具
- redis-cli: 命令行工具,執(zhí)行各種Redis命令。
- redis-benchmark: 性能基準(zhǔn)測(cè)試工具。
# 基準(zhǔn)測(cè)試 redis-benchmark -h 127.0.0.1 -p 6379 -t set,lpush -n 10000 -q
2. 性能指標(biāo)
- 內(nèi)存使用: 使用
info memory
查看內(nèi)存狀態(tài)。 - 拒絕策略: 配置
maxmemory-policy
避免內(nèi)存溢出。 - 過(guò)期時(shí)間: 設(shè)置合理的
expire
,控制鍵生命周期。
3. 調(diào)試
- 使用
slowlog
記錄慢查詢。 - 監(jiān)控
blocked clients
和master_repl_offset
。
八、實(shí)際案例
1. 高并發(fā)秒殺系統(tǒng)
需求:在高并發(fā)下,確保商品庫(kù)存正確。
解決方案
- 使用Redis的事務(wù)和分布鎖。
- 數(shù)據(jù)結(jié)構(gòu):Hash存儲(chǔ)商品庫(kù)存。
package main import ( "context" "fmt" "sync" "time" "github.com/go-redis/redis/v9" ) // 秒殺函數(shù) func doSecKill(ctx context.Context, client *redis.Client, productId string, userId string) (bool, error) { // 鎖名稱:秒殺鎖 lockKey := "lock:sec:kill" // 商品庫(kù)存Key stockKey := "stock:" + productId // 嘗試獲取鎖,防止超賣(mài) lock, err := client.LockNew(lockKey, 100*time.Millisecond).Acquire(ctx, time.Second*5).Result() if err != nil { return false, err } defer lock.Release(ctx) // 檢查庫(kù)存是否充足 currentStock, err := client.HGet(ctx, stockKey, "quantity").Int64() if err != nil || currentStock <= 0 { return false, fmt.Errorf("庫(kù)存不足") } // 減少庫(kù)存 _, err = client.HIncrBy(ctx, stockKey, "quantity", -1).Result() if err != nil { return false, fmt.Errorf("秒殺失敗:%v", err) } return true, nil } func main() { client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, }) var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func(userId int) { defer wg.Done() ctx := context.Background() success, err := doSecKill(ctx, client, "product001", fmt.Sprintf("user%d", userId)) if success { fmt.Printf("用戶%d秒殺成功\n", userId) } else { fmt.Printf("用戶%d秒殺失?。?v\n", userId, err) } }(i) } wg.Wait() }
說(shuō)明
- 使用Redis的分布鎖確保秒殺過(guò)程的原子性。
- 使用Hash結(jié)構(gòu)存儲(chǔ)庫(kù)存信息,實(shí)現(xiàn)并發(fā)安全的扣減操作。
九、最佳實(shí)踐
1. 數(shù)據(jù)過(guò)期時(shí)間
- 為鍵設(shè)置合理的TTL,避免內(nèi)存膨脹。
2. 內(nèi)存管理
- 監(jiān)控
used_memory
,確保內(nèi)存使用在可控范圍內(nèi)。 - 配置
maxmemory
和maxmemory-policy
。
3. 日志配置
- 開(kāi)啟Redis日志,記錄操作和錯(cuò)誤信息。
- 使用
slowlog
跟蹤慢查詢。
4. 安全性
- 設(shè)置強(qiáng)密碼。
- 配置防火墻,限制訪問(wèn)來(lái)源。
5. 監(jiān)控
- 使用Prometheus、Grafana監(jiān)控Redis性能。
- AlertManager配置告警規(guī)則。
6. 備份恢復(fù)
- 定期備份RDB或AOF文件。
- 配置主從復(fù)制,確保數(shù)據(jù)安全。
7. 連接池管理
- 合理配置連接池參數(shù),避免連接耗盡。
8. 數(shù)據(jù)持久化
- 選擇RDB或AOF,根據(jù)需求配置持久化策略。
十、總結(jié)
在Go語(yǔ)言中使用Redis,特別是結(jié)合哨兵和集群模式,可以實(shí)現(xiàn)高可用和高擴(kuò)展性的系統(tǒng)。合理選擇數(shù)據(jù)結(jié)構(gòu),使用事務(wù)和管道技術(shù),可以提升性能。同時(shí),注重監(jiān)控和維護(hù),確保Redis的穩(wěn)定運(yùn)行。
到此這篇關(guān)于go中redis使用的示例代碼的文章就介紹到這了,更多相關(guān)go redis使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang中的Slice與數(shù)組及區(qū)別詳解
數(shù)組是一種具有固定長(zhǎng)度的基本數(shù)據(jù)結(jié)構(gòu),在golang中與C語(yǔ)言一樣數(shù)組一旦創(chuàng)建了它的長(zhǎng)度就不允許改變,數(shù)組的空余位置用0填補(bǔ),不允許數(shù)組越界。今天小編通過(guò)實(shí)例代碼操作給大家詳細(xì)介紹lang中的Slice與數(shù)組的相關(guān)知識(shí),一起看看吧2020-02-02Go語(yǔ)言實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式的方法總結(jié)
這篇文章主要介紹了在?Go?語(yǔ)言中實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式的多種方法,并重點(diǎn)探討了通道、條件變量的適用場(chǎng)景和優(yōu)缺點(diǎn),需要的可參考一下2023-05-05golang程序使用alpine編譯出最小arm鏡像實(shí)現(xiàn)
這篇文章主要為大家介紹了golang程序使用alpine編譯出最小arm鏡像,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12源碼分析Golang?log是如何實(shí)現(xiàn)的
go語(yǔ)言的log包提供了簡(jiǎn)單的日志記錄功能,允許開(kāi)發(fā)者在應(yīng)用程序中記錄重要的信息、錯(cuò)誤、警告等,log包是Go標(biāo)準(zhǔn)庫(kù)的一部分,因此,使用它不需要安裝額外的第三方庫(kù),本文給大家源碼分析了Golang?log是如何實(shí)現(xiàn)的,需要的朋友可以參考下2024-03-03詳解Go?flag實(shí)現(xiàn)二級(jí)子命令的方法
這篇文章主要介紹了Go?flag?詳解,實(shí)現(xiàn)二級(jí)子命令,本文就探討一下?Go?語(yǔ)言中如何寫(xiě)一個(gè)擁有類似特性的命令行程序,需要的朋友可以參考下2022-07-07Golang連接池的幾種實(shí)現(xiàn)案例小結(jié)
這篇文章主要介紹了Golang連接池的幾種實(shí)現(xiàn)案例小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03