Sửa lỗi 'ERR Script attempted to access a non-local key' trong Redis Cluster

intermediate🔴 Redis2026-06-23| Redis Cluster (v3.0 trở lên), Linux (Ubuntu/CentOS), Docker, hoặc Managed Redis (AWS ElastiCache, Azure Cache for Redis).

Error Message

ERR Script attempted to access a non-local key in a cluster node
#redis-cluster#lua#scripting#devops

Tại sao bạn gặp lỗi nàyChuyển từ một instance Redis độc lập sang cluster thường mang lại cảm giác mượt mà—cho đến khi bạn cố gắng chạy một script Lua. Đột nhiên, lệnh EVAL của bạn bị lỗi. Bạn nhận được một thông báo lỗi khó chịu: ERR Script attempted to access a non-local key in a cluster node. Điều này xảy ra vì cluster là một hệ thống phân tán, nơi dữ liệu được rải rác trên nhiều node. Khác với instance đơn lẻ, một node trong cluster chỉ 'sở hữu' một phần dữ liệu cụ thể của bạn.

Nguyên nhân gốc rễ: Sharding dữ liệu và Hash SlotRedis Cluster chia không gian key thành chính xác 16,384 hash slot. Mỗi node xử lý một phạm vi slot cụ thể. Ví dụ, Node A có thể xử lý các slot từ 0 đến 5,460, trong khi Node B đảm nhận từ 5,461 đến 10,922. Khi bạn chạy một script Lua, Redis yêu cầu mọi key mà script tác động đến phải nằm trong cùng một hash slot.

Lỗi này được kích hoạt trong các tình huống sau:

  • Script của bạn cố gắng đọc key_a từ Node 1 và key_b từ Node 2.- Bạn viết cứng (hardcode) tên key bên trong script mà key đó không thuộc về node đang nhận lệnh.- Bạn truyền các key dưới dạng đối số nhưng chúng lại được hash vào các slot khác nhau.Redis thực thi quy tắc này một cách nghiêm ngặt để duy trì tính nguyên tử (atomicity). Nếu một script có thể tác động đến các key trên các node khác nhau, Redis sẽ phải triển khai cơ chế khóa đa node (cross-node locking) tốn kém, điều này sẽ làm giảm hiệu năng trầm trọng.

Cách khắc phục lỗi### Giải pháp 1: Sử dụng Hash Tag để nhóm KeyCách khắc phục đáng tin cậy nhất là sử dụng Hash Tag. Bằng cách bao bọc một phần tên key trong dấu ngoặc nhọn {}, bạn yêu cầu Redis chỉ hash phần văn bản bên trong dấu ngoặc đó. Điều này bắt buộc các key khác nhau phải nằm trong cùng một slot trên cùng một node.

Ví dụ: Thay vì các key sau, vốn có thể nằm trên ba node khác nhau:

user:123:profile
user:123:settings
user:123:stats

Sử dụng hash tag để nhóm chúng lại với nhau:

{user:123}:profile
{user:123}:settings
{user:123}:stats

Giờ đây, Redis chỉ hash chuỗi user:123. Điều này đảm bảo cả ba key đều nằm trong cùng một slot, giúp script Lua có thể truy cập được.

Giải pháp 2: Luôn truyền Key qua mảng KEYSRedis Cluster cần biết script sẽ tác động đến những key nào trước khi nó thực sự chạy. Điều này cho phép client định tuyến yêu cầu đến đúng node. Nếu bạn viết cứng một key bên trong mã Lua, cluster không thể xác thực vị trí của key đó trước.

Cách làm sai (Viết cứng):

-- Cách này khả năng cao sẽ thất bại trong môi trường cluster
local val = redis.call('GET', 'global_settings')
return val

Cách làm đúng (Truyền dưới dạng đối số):

-- Truyền 'global_settings' dưới dạng KEYS[1] từ mã ứng dụng của bạn
local val = redis.call('GET', KEYS[1])
return val

Khi bạn sử dụng mảng KEYS, các client hỗ trợ cluster như ioredis, lettuce, hoặc redis-py sẽ tính toán slot trước. Sau đó, chúng gửi script đến node cụ thể đang nắm giữ key đó.

Giải pháp 3: Di chuyển logic sang tầng ứng dụngĐôi khi bạn đơn giản là không thể đặt các key ở cùng một nơi. Nếu bạn cần so sánh dữ liệu giữa hai người dùng nằm ở các slot khác nhau, một script Lua duy nhất sẽ không hoạt động. Trong trường hợp này, bạn phải xử lý logic trong mã ứng dụng của mình:

  • Lấy user:123:data từ Redis.- Lấy user:456:data từ Redis.- Thực hiện so sánh hoặc xử lý logic trong Node.js, Go, hoặc Python.- Ghi kết quả ngược lại Redis nếu cần thiết.## Các bước xác minhKiểm tra xem các key của bạn có dùng chung một slot hay không bằng lệnh cluster keyslot trong redis-cli:
# Kiểm tra slot cho key profile
redis-cli -c CLUSTER KEYSLOT {user:123}:profile
# Kết quả: 1234

# Kiểm tra slot cho key settings
redis-cli -c CLUSTER KEYSLOT {user:123}:settings
# Kết quả: 1234

Nếu các con số kết quả khớp nhau, script của bạn sẽ chạy mà không gặp lỗi.

Các thực hành tốt nhất để phòng ngừa- Thiết kế cho Sharding: Xác định các key thường xuyên được cập nhật cùng nhau và sử dụng hash tag để giữ chúng trên cùng một node.- Sử dụng Client hỗ trợ Cluster: Đảm bảo thư viện của bạn hỗ trợ Redis Cluster và tự động tính toán hash slot.- Kiểm tra Script: Không bao giờ sử dụng nối chuỗi bên trong Lua để tạo tên key. Nếu một key không nằm trong mảng KEYS, nó không nên xuất hiện trong script của bạn.- Tránh các Key toàn cục: Không trộn lẫn các key cấu hình 'toàn cục' với dữ liệu riêng biệt của người dùng trong cùng một script, vì chúng gần như chắc chắn sẽ nằm trên các node khác nhau.

Related Error Notes