Redis Practical Guide: From Basic Concepts to Go Language Applications
Redis (Remote Dictionary Server) is an open-source in-memory data structure storage system that can not only be used as a database, cache, and message broker, but also supports a variety of data structures, such as strings, hashes, lists, collections, and ordered collections. Today we will take an in-depth look at the use of Redis with actual Go code examples.
Redis Basic Data Structure and Operation
1. 字符串(String)
Strings are the most basic data type in Redis and can store text, number, or binary data.
GO
package main
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
var ctx = context. Background()
var rdb *redis. Client
func init() {
rdb = redis. NewClient(&redis. Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
}
func stringOperations() {
Set the key-value pair
err := rdb. Set(ctx, "name", "Redis", 0). Err()
if err != nil {
panic(err)
}
Get the key value
val, err := rdb. Get(ctx, "name"). Result()
if err != nil {
panic(err)
}
FMT. Printlon("In name:", good)
Set key-value pairs with expiration times
err = rdb. Set(ctx, "counter", 0, 1*time. Hour). Err()
if err != nil {
panic(err)
}
Self-increment operation
err = rdb. Incr(ctx, "counter"). Err()
if err != nil {
panic(err)
}
val, err = rdb. Get(ctx, "counter"). Result()
if err != nil {
panic(err)
}
fmt. Println("counter:", val)
}
2nd 哈希(hash)
Hashing allows us to store multiple key-value pairs under a single key, making it ideal for storing objects.
GO
func hashOperations() {
Set the hash field
err := rdb. HSet(ctx, "user:1000", "name", "张三"). Err()
if err != nil {
panic(err)
}
err = rdb. HSet(ctx, "user:1000", "email", "zhangsan@example.com"). Err()
if err != nil {
panic(err)
}
Get a single field
name, err := rdb. HGet(ctx, "user:1000", "name"). Result()
if err != nil {
panic(err)
}
fmt. Println("user name:", name)
Get all the fields
userMap, err := rdb. HGetAll(ctx, "user:1000"). Result()
if err != nil {
panic(err)
}
fmt. Println("user info:", userMap)
}
3. List
A list is an ordered list of strings that supports inserting and removing elements at both ends.
GO
func listOperations() {
Insert elements to the left of the list
err := rdb. LPush(ctx, "tasks", "task1", "task2", "task3"). Err()
if err != nil {
panic(err)
}
Insert elements to the right of the list
err = rdb. RPush(ctx, "tasks", "task4"). Err()
if err != nil {
panic(err)
}
Pop up the element from the left
task, err := rdb. LPop(ctx, "tasks"). Result()
if err != nil {
panic(err)
}
fmt. Println("popped task:", task)
Get the list length
length, err := rdb. LLen(ctx, "tasks"). Result()
if err != nil {
panic(err)
}
fmt. Println("tasks count:", length)
Get all the elements in the list
tasks, err := rdb. LRange(ctx, "tasks", 0, -1). Result()
if err != nil {
panic(err)
}
fmt. Println("remaining tasks:", tasks)
}
4. Set
A collection is an unordered collection of strings that supports operations such as intersection, union, and difference set.
GO
func setOperations() {
Add elements to the collection
err := rdb. SAdd(ctx, "tags:post:1", "go", "redis", "database"). Err()
if err != nil {
panic(err)
}
err = rdb. SAdd(ctx, "tags:post:2", "go", "web", "api"). Err()
if err != nil {
panic(err)
}
Check if the element is in the collection
isMember, err := rdb. SIsMember(ctx, "tags:post:1", "redis"). Result()
if err != nil {
panic(err)
}
fmt. Println("is 'redis' in post1 tags:", isMember)
Get all the elements in the collection
tags, err := rdb. SMembers(ctx, "tags:post:1"). Result()
if err != nil {
panic(err)
}
fmt. Println("post1 tags:", tags)
Collect union operations
union, err := rdb. SUnion(ctx, "tags:post:1", "tags:post:2"). Result()
if err != nil {
panic(err)
}
fmt. Println("union of tags:", union)
Collection intersection operations
intersect, err := rdb. SInter(ctx, "tags:post:1", "tags:post:2"). Result()
if err != nil {
panic(err)
}
fmt. Println("intersection of tags:", intersect)
}
5. 有序集合(Sorted Set)
Ordered sets are similar to collections, but each element has a score associated with it, which can be sorted based on the score.
GO
func sortedSetOperations() {
Add elements to an ordered collection
err := rdb. ZAdd(ctx, "leaderboard",
&redis. Z{Score: 100, Member: "player1"},
&redis. Z{Score: 90, Member: "player2"},
&redis. Z{Score: 95, Member: "player3"}). Err()
if err != nil {
panic(err)
}
Increase member scores
err = rdb. ZIncrBy(ctx, "leaderboard", 10, "player2"). Err()
if err != nil {
panic(err)
}
Get the top N members sorted by score
players, err := rdb. ZRevRangeWithScores(ctx, "leaderboard", 0, 2). Result()
if err != nil {
panic(err)
}
fmt. Println("Leaderboard:")
for i, player := range players {
fmt. Printf("%d. %v - %v points\n", i+1, player. Member, player. Score)
}
}
Code examples for practical application scenarios
1. Caching applications
GO
func getCachedUser(userID string) (map[string]string, error) {
Try getting it from the cache first
cachedUser, err := rdb. HGetAll(ctx, "user:"+userID). Result()
if err != nil && err != redis. Nil {
return nil, err
}
If data exists in the cache
if len(cachedUser) > 0 && cachedUser["name"] != "" {
fmt. Println ("Get user information from cache")
return cachedUser, nil
}
Simulate getting data from a database
fmt. Println ("Get User Information from Database")
user := map[string]string{
"id": userID,
"name": "Zhang San",
"email": "zhangsan@example.com",
}
Deposit data into the cache and set an expiration time
err = rdb. HMSet(ctx, "user:"+userID, user). Err()
if err != nil {
return nil, err
}
Set the expiration time
rdb. Expire(ctx, "user:"+userID, 10*time. Minute)
return user, nil
}
2. Distributed locks
GO
func acquireLock(lockKey string, timeout time. Duration) (string, error) {
Generate a unique identity
lockValue := fmt. Sprintf("%d", time. Now(). UnixNano())
Try to get a lock, set an expiration time to prevent deadlocks
ok, err := rdb. SetNX(ctx, lockKey, lockValue, timeout). Result()
if err != nil {
return "", err
}
if !ok {
return "", fmt. Errorf("无法获取锁")
}
return lockValue, nil
}
func releaseLock(lockKey, lockValue string) error {
Use Lua scripts to ensure atomicity
script := `
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
`
result, err := rdb. Eval(ctx, script, []string{lockKey}, lockValue). Result()
if err != nil {
return err
}
if result. (int64) == 0 {
return fmt. Errorf("Lock has expired or is held by another process")
}
return nil
}
func doWithLock(lockKey string, fn func() error) error {
lockValue, err := acquireLock(lockKey, 10*time. Second)
if err != nil {
return err
}
defer releaseLock(lockKey, lockValue)
return fn()
}
3. Message queue
GO
producer
func produceMessage(queueName, message string) error {
return rdb. LPush(ctx, queueName, message). Err()
}
consumer
func consumeMessage(queueName string) (string, error) {
Blocking pop-up message with a timeout of 1 second
result, err := rdb. BRPop(ctx, 1*time. Second, queueName). Result()
if err != nil {
return "", err
}
BRPop returns an array with key names and values, we only need values
if len(result) >= 2 {
return result[1], nil
}
return "", fmt. Errorf("Unable to get message")
}
func messageQueueExample() {
queueName := "task_queue"
Startup producers
go func() {
for i := 0; i < 5; i++ {
Message:= fmt. Sprintf("task_%d", i)
err := produceMessage(queueName, message)
if err != nil {
fmt. Printf("生产消息失败: %v\n", err)
} else {
fmt. Printf("生产消息: %s\n", message)
}
time. Sleep(1 * time. Second)
}
}()
Activate the consumer
go func() {
for {
message, err := consumeMessage(queueName)
if err != nil {
if err != redis. Nil {
fmt. Printf("消费消息失败: %v\n", err)
}
time. Sleep(1 * time. Second)
Go on
}
fmt. Printf("消费消息: %s\n", message)
Simulate processing time
time. Sleep(2 * time. Second)
}
}()
}
Full example
GO
func main() {
Test the connection
pong, err := rdb. Ping(ctx). Result()
if err != nil {
fmt. Println("连接 Redis 失败:", err)
return
}
fmt. Println("连接成功:", pong)
Perform various actions
Fmt. Println("=== 字符串操作 ===")
stringOperations()
fmt. Println("\n=== 哈希操作 ===")
hashOperations()
fmt. Println("\n=== List Operation ===")
listOperations()
fmt. Println("\n=== Collection Operation ===")
setOperations()
fmt. println("\n=== Ordered Collection Operation ===")
sortedSetOperations()
fmt. Println("\n=== 缓存示例 ===")
user, err := getCachedUser("123")
if err != nil {
fmt. Printf("获取用户失败: %v\n", err)
} else {
fmt. Printf("获取用户: %+v\n", user)
}
To get the same user again, it should be from the cache
user, err = getCachedUser("123")
if err != nil {
fmt. Printf("获取用户失败: %v\n", err)
} else {
fmt. Printf("获取用户: %+v\n", user)
}
fmt. println("\n=== Distributed Lock Example ===")
err = doWithLock("test_lock", func() error {
fmt. Println("Perform an operation that requires lock protection")
time. Sleep(3 * time. Second)
return nil
})
if err != nil {
fmt. Printf("Failed to execute lock protection operation: %v\n", err)
}
fmt. Println("\n=== Message Queue Example ===")
messageQueueExample()
Wait a while to observe the output of the message queue sample
time. Sleep(10 * time. Second)
}