Sửa lỗi Redis 'READONLY You can't write against a read only replica'

intermediate🔴 Redis2026-05-16| Redis 4.x / 5.x / 6.x / 7.x trên Linux, Docker, Kubernetes — mọi cấu hình replication (standalone replica, Sentinel, Redis Cluster)

Error Message

READONLY You can't write against a read only replica.
#redis#replica#slave-read-only#replication#config

Chuyện Gì Vừa Xảy Ra

Bạn chạy một lệnh ghi — SET, HSET, LPUSH, hoặc bất kỳ lệnh nào thay đổi dữ liệu — và Redis trả về:

READONLY You can't write against a read only replica.

Client của bạn đã kết nối đến node replica (trước đây gọi là slave) thay vì node primary. Replica mặc định chỉ được đọc — không có ngoại lệ. Mọi thao tác ghi đều bị từ chối ngay lập tức.

Cách khắc phục phụ thuộc vào lý do client của bạn kết nối nhầm node ngay từ đầu.

Xác Nhận Bạn Đang Ở Trên Replica

Đừng đoán mò. Chạy lệnh này trước khi làm bất cứ điều gì:

redis-cli -h your-redis-host -p 6379 INFO replication

Kiểm tra trường role trong kết quả đầu ra:

# Replication
role:slave
master_host:192.168.1.10
master_port:6379
master_link_status:up

Thấy role:slave hoặc role:replica (Redis 5+) là xác nhận vấn đề. Ghi lại giá trị master_host — đó là nơi các lệnh ghi cần đến.

Nguyên Nhân Phổ Biến

  • IP replica bị hardcode trong cấu hình ứng dụng hoặc chuỗi kết nối
  • Sentinel failover đã xảy ra — primary cũ trở thành replica, nhưng ứng dụng chưa kết nối lại
  • Load balancer định tuyến lệnh ghi đến replica — round-robin không có phân tách đọc/ghi
  • Sai cổng — Sentinel lắng nghe trên cổng 26379, không phải cổng mặc định 6379 của Redis
  • Cluster client cấu hình sai — không theo dõi các redirect MOVED, kết thúc ở replica trong shard

Cách Sửa 1 — Trỏ Client Đến Primary

Cách trực tiếp nhất: lấy địa chỉ primary và kết nối đến đó.

# Lấy host của primary từ bất kỳ node nào
redis-cli -h your-redis-host -p 6379 INFO replication | grep master_host

# Kết nối trực tiếp
redis-cli -h 192.168.1.10 -p 6379
127.0.0.1:6379> SET foo bar
OK

Sau đó cập nhật cấu hình ứng dụng cho phù hợp:

# Ví dụ với Python redis-py
import redis
r = redis.Redis(host='192.168.1.10', port=6379)  # primary, không phải replica
r.set('foo', 'bar')

Cách này hoạt động, nhưng dễ bị lỗi. Ngay khi có failover, bạn lại quay về vạch xuất phát. Với môi trường production, hãy chuyển sang Cách Sửa 2.

Cách Sửa 2 — Dùng Sentinel Để Ứng Dụng Tự Tìm Primary

Hardcode IP của primary là tự chuốc rắc rối. Sau một lần failover — mà Sentinel có thể kích hoạt trong vòng dưới 30 giây — ứng dụng của bạn lại trỏ vào replica.

Sentinel theo dõi primary hiện tại và cung cấp đúng địa chỉ khi cần:

# Python redis-py với Sentinel
from redis.sentinel import Sentinel

sentinel = Sentinel(
    [('sentinel1', 26379), ('sentinel2', 26379), ('sentinel3', 26379)],
    socket_timeout=0.1
)

# Luôn trả về primary hiện tại
master = sentinel.master_for('mymaster', socket_timeout=0.1)
master.set('foo', 'bar')

# Kết nối chỉ đọc đến replica
slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
slave.get('foo')

Đã dùng Sentinel mà vẫn gặp lỗi? Kiểm tra lại xem mymaster có khớp với tên trong sentinel.conf không:

redis-cli -h sentinel-host -p 26379 SENTINEL masters

Tên không khớp sẽ âm thầm gây ra lỗi kết nối trông giống như lỗi định tuyến.

Cách Sửa 3 — Tạm Thời Cho Phép Ghi Trên Replica (Không Khuyến Nghị Cho Production)

Redis có tùy chọn cấu hình replica-read-only no để bỏ giới hạn chỉ đọc. Nghe có vẻ tiện. Nhưng thực ra không — ít nhất là không dùng cho production.

Đây là vấn đề: trong quá trình đồng bộ replication, primary gửi toàn bộ dữ liệu đến replica. Mọi dữ liệu bạn đã ghi cục bộ sẽ bị xóa sạch. Không có merge. Dữ liệu biến mất.

# Thay đổi tạm thời (reset khi khởi động lại)
redis-cli -h replica-host CONFIG SET replica-read-only no

# Cố định (thêm vào redis.conf)
replica-read-only no

Chỉ dùng cách này trên replica độc lập cho mục đích debug hoặc migration một lần. Không bao giờ dùng trong môi trường live khi dữ liệu quan trọng.

Cách Sửa 4 — Redis Cluster: Bật Follow-Redirects Trong Client

Chế độ cluster có thêm một điểm phức tạp. Lệnh ghi vào shard sai trả về MOVED, không phải READONLY. Nhưng nếu bạn kết nối đến replica trong shard đúng, bạn sẽ nhận READONLY. Hai lỗi khác nhau, nguyên nhân gốc tương tự.

Đảm bảo cluster client của bạn trỏ vào primary cho các lệnh ghi:

# Python redis-py cluster client
from redis.cluster import RedisCluster

rc = RedisCluster(
    host='redis-node-1',
    port=6379,
    read_from_replicas=False  # Tất cả thao tác đều đến primary node
)
rc.set('foo', 'bar')

Muốn mở rộng khả năng đọc qua replica? Đặt read_from_replicas=True — nhưng hãy đảm bảo client chỉ định tuyến lệnh đọc theo cách đó, không phải lệnh ghi.

Cách Sửa 5 — Sau Sentinel Failover, Buộc Kết Nối Lại

Các ứng dụng cache địa chỉ primary sẽ bị ảnh hưởng sau failover. IP được cache lúc này đang trỏ vào replica.

# Kiểm tra primary hiện tại là ai
redis-cli -h sentinel-host -p 26379 SENTINEL get-master-addr-by-name mymaster
# Trả về: 192.168.1.11 6379  ← primary mới sau failover

Khởi động lại ứng dụng hoặc xóa connection pool sẽ xóa địa chỉ cũ. Với Sentinel client được cấu hình đúng, việc này xảy ra tự động — không cần can thiệp thủ công.

Xác Minh Đã Sửa Xong

Chạy kiểm tra ghi/đọc nhanh trước khi kết luận:

redis-cli -h correct-primary-host -p 6379 SET test:write ok
# Kết quả mong đợi: OK

redis-cli -h correct-primary-host -p 6379 GET test:write
# Kết quả mong đợi: "ok"

# Dọn dẹp
redis-cli -h correct-primary-host -p 6379 DEL test:write

Sau đó xác nhận lại role một lần nữa:

redis-cli -h correct-primary-host -p 6379 INFO replication | grep role
# Kết quả mong đợi: role:master

Bài Học Rút Ra

  • Hardcode IP của primary là quả bom hẹn giờ — dùng Sentinel hoặc cluster-aware client để failover vô hình với ứng dụng của bạn.
  • Tách biệt connection pool — replica xử lý đọc, primary xử lý ghi. Hãy khai báo rõ ràng sự phân tách này trong cấu hình kết nối, không để ngầm định.
  • Thiết lập cảnh báo thay đổi role — theo dõi role:slave trên các node đáng lẽ phải là primary, hoặc subscribe vào sự kiện Sentinel: redis-cli -h sentinel-host -p 26379 SUBSCRIBE +switch-master.
  • Đừng đụng vào replica-read-only trong production. Nó tồn tại có lý do: dữ liệu trên replica là tạm thời, và tắt giới hạn này chỉ khiến bạn mất dữ liệu ghi âm thầm trong lần đồng bộ tiếp theo.

Related Error Notes