Bài 3: Thao tác cơ bản - CRUD với String và quản lý TTL
Mục tiêu bài học:
-
Nắm vững các lệnh Create, Read, Update, Delete đối với kiểu dữ liệu String (kiểu dữ liệu phổ biến nhất trong Redis).
-
Xử lý đúng cách lỗi
redis.Nilkhi truy vấn dữ liệu không tồn tại. -
Quản lý vòng đời của dữ liệu thông qua TTL (Time-To-Live).
1. Create & Update: Ghi dữ liệu vào Redis
Trong Redis, kiểu String có thể chứa văn bản, JSON đã serialize, hoặc số nguyên. go-redis cung cấp các hàm tương ứng với các lệnh chuẩn của Redis.
-
Set: Ghi dữ liệu, mặc định sẽ ghi đè (Update) nếu key đã tồn tại. -
SetNX(Set if Not eXists): Chỉ ghi dữ liệu nếu key chưa tồn tại. Rất hữu ích để làm chống trùng lặp request hoặc khóa đơn giản.
Go
package main
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func createAndUpdate(rdb *redis.Client) {
// 1. SET cơ bản (Không có TTL - tồn tại vĩnh viễn)
// Số 0 ở cuối đại diện cho thời gian sống (Expiration) bằng 0
err := rdb.Set(ctx, "user:1", "Nguyen Van A", 0).Err()
if err != nil {
fmt.Println("Lỗi khi Set:", err)
}
// 2. SET với TTL (Tự động xóa sau 10 phút)
rdb.Set(ctx, "session:user_1", "token_abc123", 10*time.Minute)
// 3. SETNX (Chỉ ghi nếu chưa tồn tại)
// Trả về boolean chỉ định việc ghi có thành công hay không
isSet, err := rdb.SetNX(ctx, "config:theme", "dark", 0).Result()
if isSet {
fmt.Println("Ghi thành công vì key chưa tồn tại")
} else {
fmt.Println("Bỏ qua vì key đã tồn tại")
}
}
2. Read: Đọc dữ liệu và cạm bẫy redis.Nil
Khi đọc dữ liệu bằng Get, nếu key không tồn tại, Redis trả về kết quả rỗng. Tuy nhiên, go-redis quy định đây là một lỗi có kiểu redis.Nil để phân biệt với kết quả chuỗi rỗng "".
Lưu ý quan trọng: Bắt buộc phải check riêng
err == redis.Niltrước khi check các lỗi kết nối khác.
Go
func readData(rdb *redis.Client) {
// 1. GET một key
val, err := rdb.Get(ctx, "user:1").Result()
switch {
case err == redis.Nil:
fmt.Println("Key không tồn tại trong Redis (Cache Miss)")
case err != nil:
fmt.Println("Lỗi hệ thống hoặc mạng:", err)
default:
fmt.Println("Giá trị lấy được:", val)
}
// 2. MGET (Multi-Get) - Lấy nhiều key cùng lúc để tối ưu Network RTT
vals, err := rdb.MGet(ctx, "user:1", "user:2", "user:3").Result()
if err == nil {
for i, v := range vals {
// v có thể là nil (nếu key đó không tồn tại) nên cần ép kiểu an toàn
if v != nil {
fmt.Printf("User %d: %v\n", i+1, v)
}
}
}
}
3. Delete: Xóa dữ liệu
Hàm Del nhận vào một danh sách các key (Variadic parameters). Nó trả về số lượng key đã xóa thành công.
Go
func deleteData(rdb *redis.Client) {
// Xóa 1 hoặc nhiều key cùng lúc
deletedCount, err := rdb.Del(ctx, "user:1", "session:user_1").Result()
if err != nil {
fmt.Println("Lỗi khi xóa:", err)
}
fmt.Printf("Đã xóa thành công %d keys\n", deletedCount)
}
4. Quản lý TTL (Time-To-Live)
Ngoài việc set TTL ngay từ lúc tạo (thông qua Set), bạn có thể gia hạn hoặc kiểm tra thời gian sống còn lại của một key đang tồn tại.
Go
func manageTTL(rdb *redis.Client) {
// 1. Kiểm tra TTL hiện tại của key
ttl, err := rdb.TTL(ctx, "session:user_1").Result()
if err == nil {
// Kết quả có thể là:
// > 0: Thời gian sống còn lại
// -1: Key tồn tại nhưng không có TTL (sống vĩnh viễn)
// -2: Key không tồn tại
fmt.Printf("Thời gian sống còn lại: %v\n", ttl)
}
// 2. Gia hạn thời gian sống (Expire)
// Hữu ích khi user vừa tương tác, cần gia hạn session thêm 30 phút
rdb.Expire(ctx, "session:user_1", 30*time.Minute)
// 3. Xóa TTL, biến key thành vĩnh viễn (Persist)
rdb.Persist(ctx, "session:user_1")
}
All rights reserved