go中redis使用的示例代碼
在Go語言中使用Redis進(jìn)行數(shù)據(jù)存儲和管理可以帶來高效和靈活的優(yōu)勢。下面的講解包括單機(jī)模式、哨兵模式和集群模式的部署及使用。
一、Redis 簡介
Redis是一個高性能的內(nèi)存數(shù)據(jù)庫,支持多種數(shù)據(jù)結(jié)構(gòu),如字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(ZSet)等。它還支持事務(wù)、批量操作和流式數(shù)據(jù)處理。Redis常用于緩存、實時數(shù)據(jù)處理、計數(shù)器、消息隊列等場景。
二、Go中Redis的使用
1. 安裝Go Redis包
推薦使用github.com/go-redis/redis/v9
,它支持單機(jī)、哨兵和集群模式。
安裝命令:
go get github.com/go-redis/redis/v9
2. 單機(jī)模式
特點(diǎn):單一Redis實例,簡單易用,適合開發(fā)和測試環(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) }
說明:
- 使用
redis.NewClient
創(chuàng)建客戶端。 - 使用
Ping
測試連接。 Set
設(shè)置鍵值,Get
讀取值。
3. 哨兵模式
特點(diǎn):提供高可用性,當(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) }
說明:
- 使用
redis.NewFailoverClient
創(chuàng)建客戶端。 - 指定主庫名稱
MasterName
和哨兵地址。 - 當(dāng)主庫故障時,哨兵會自動將從庫提升為主庫。
三、Redis集群
1. 集群模式
特點(diǎn):通過分片實現(xiàn)水平擴(kuò)展,每個節(jié)點(diǎn)處理一部分?jǐn)?shù)據(jù),適合高并發(fā)場景。
集群部署
部署結(jié)構(gòu)
- 6節(jié)點(diǎn):3主3從,每個主節(jié)點(diǎn)負(fù)責(zé)一個分片。
- 每個分片有1主1從。
使用redis-cli創(chuàng)建集群
- 啟動6個Redis實例,分別指定不同端口。
- 使用
redis-cli
命令創(chuàng)建集群。
創(chuàng)建集局腳本:例如 start_cluster.sh
# 啟動6個節(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 集群模式下每個節(jié)點(diǎn)的配置文件,用于指定節(jié)點(diǎn)的運(yùn)行參數(shù)。如果這些配置文件不存在,Redis 會自動生成一個默認(rèn)的配置文件,但為了確保集群部署的正確性,最好手動創(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
: 后臺運(yùn)行。logfile
: 指定日志文件路徑。dir
: 指定數(shù)據(jù)文件存儲路徑。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è)置鍵值對 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) }
說明:
- 使用
redis.NewClusterClient
創(chuàng)建集群客戶端。 - 初始化時提供所有節(jié)點(diǎn)地址。
- 集群模式下,Redis自動處理數(shù)據(jù)分片和請求路由。
四、常用數(shù)據(jù)結(jié)構(gòu)與操作
1. 字符串(String)
// 設(shè)置過期時間 err = client.Set(context.Background(), "key", "value", 10*time.Second).Err() // 遞增計數(shù)器 num, err := client.Incr(context.Background(), "counter").Result() // 遞減計數(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() // 彈出左邊第一個元素 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ù)
// 開始事務(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í)行多個命令 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ù)安全。
- 主庫寫入,數(shù)據(jù)同步到從庫。
- 從庫可用于讀分離,提高讀性能。
2. 故障轉(zhuǎn)移
- 使用哨兵模式實現(xiàn)自動故障轉(zhuǎn)移。
- 集群模式下,節(jié)點(diǎn)故障自動遷移。
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)測試工具。
# 基準(zhǔn)測試 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)存溢出。 - 過期時間: 設(shè)置合理的
expire
,控制鍵生命周期。
3. 調(diào)試
- 使用
slowlog
記錄慢查詢。 - 監(jiān)控
blocked clients
和master_repl_offset
。
八、實際案例
1. 高并發(fā)秒殺系統(tǒng)
需求:在高并發(fā)下,確保商品庫存正確。
解決方案
- 使用Redis的事務(wù)和分布鎖。
- 數(shù)據(jù)結(jié)構(gòu):Hash存儲商品庫存。
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" // 商品庫存Key stockKey := "stock:" + productId // 嘗試獲取鎖,防止超賣 lock, err := client.LockNew(lockKey, 100*time.Millisecond).Acquire(ctx, time.Second*5).Result() if err != nil { return false, err } defer lock.Release(ctx) // 檢查庫存是否充足 currentStock, err := client.HGet(ctx, stockKey, "quantity").Int64() if err != nil || currentStock <= 0 { return false, fmt.Errorf("庫存不足") } // 減少庫存 _, 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() }
說明
- 使用Redis的分布鎖確保秒殺過程的原子性。
- 使用Hash結(jié)構(gòu)存儲庫存信息,實現(xiàn)并發(fā)安全的扣減操作。
九、最佳實踐
1. 數(shù)據(jù)過期時間
- 為鍵設(shè)置合理的TTL,避免內(nèi)存膨脹。
2. 內(nèi)存管理
- 監(jiān)控
used_memory
,確保內(nèi)存使用在可控范圍內(nèi)。 - 配置
maxmemory
和maxmemory-policy
。
3. 日志配置
- 開啟Redis日志,記錄操作和錯誤信息。
- 使用
slowlog
跟蹤慢查詢。
4. 安全性
- 設(shè)置強(qiáng)密碼。
- 配置防火墻,限制訪問來源。
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語言中使用Redis,特別是結(jié)合哨兵和集群模式,可以實現(xiàn)高可用和高擴(kuò)展性的系統(tǒng)。合理選擇數(shù)據(jù)結(jié)構(gòu),使用事務(wù)和管道技術(shù),可以提升性能。同時,注重監(jiān)控和維護(hù),確保Redis的穩(wěn)定運(yùn)行。
到此這篇關(guān)于go中redis使用的示例代碼的文章就介紹到這了,更多相關(guān)go redis使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang中的Slice與數(shù)組及區(qū)別詳解
數(shù)組是一種具有固定長度的基本數(shù)據(jù)結(jié)構(gòu),在golang中與C語言一樣數(shù)組一旦創(chuàng)建了它的長度就不允許改變,數(shù)組的空余位置用0填補(bǔ),不允許數(shù)組越界。今天小編通過實例代碼操作給大家詳細(xì)介紹lang中的Slice與數(shù)組的相關(guān)知識,一起看看吧2020-02-02Go語言實現(xiàn)生產(chǎn)者-消費(fèi)者模式的方法總結(jié)
這篇文章主要介紹了在?Go?語言中實現(xiàn)生產(chǎn)者消費(fèi)者模式的多種方法,并重點(diǎn)探討了通道、條件變量的適用場景和優(yōu)缺點(diǎn),需要的可參考一下2023-05-05golang程序使用alpine編譯出最小arm鏡像實現(xiàn)
這篇文章主要為大家介紹了golang程序使用alpine編譯出最小arm鏡像,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12