Sửa lỗi Redis 'ERR value is not an integer or out of range' khi dùng lệnh INCR và DECR

beginner🔴 Redis2026-05-13| Redis 6.x / 7.x trên Linux, macOS, Windows (WSL2) — ảnh hưởng đến mọi thư viện client (redis-cli, ioredis, redis-py, Jedis, go-redis)

Error Message

ERR value is not an integer or out of range
#redis#incr#decr#kiểu-dữ-liệu#số-nguyên

Chuyện gì vừa xảy ra?

Bạn chạy lệnh INCR, INCRBY, DECR, hoặc DECRBY và Redis trả về:

(error) ERR value is not an integer or out of range

Các lệnh counter của Redis chỉ hoạt động khi giá trị được lưu trông giống một số nguyên thập phân thuần túy — không phải float, không phải JSON blob, không phải chuỗi rỗng. Ngay khi có thứ gì khác nằm dưới key đó, lệnh sẽ dừng ngay lập tức.

Tái hiện lỗi trong 30 giây

# Lưu một số float
127.0.0.1:6379> SET page_views 3.5
OK
127.0.0.1:6379> INCR page_views
(error) ERR value is not an integer or out of range

# Lưu một chuỗi ký tự
127.0.0.1:6379> SET page_views "hello"
OK
127.0.0.1:6379> INCR page_views
(error) ERR value is not an integer or out of range

# Lưu chuỗi rỗng
127.0.0.1:6379> SET page_views ""
OK
127.0.0.1:6379> INCR page_views
(error) ERR value is not an integer or out of range

# Số nguyên vượt quá phạm vi signed 64-bit
127.0.0.1:6379> SET big_num 9999999999999999999999
OK
127.0.0.1:6379> INCR big_num
(error) ERR value is not an integer or out of range

Chẩn đoán key trước tiên

Chưa cần làm gì vội. Hãy bắt đầu bằng cách kiểm tra chính xác những gì đang được lưu:

127.0.0.1:6379> TYPE your_key
string
127.0.0.1:6379> GET your_key
"3.5"
127.0.0.1:6379> STRLEN your_key
(integer) 3

Đây là những nguyên nhân thường gặp nhất:

  • Một số float như "1.0" hoặc "0.5" — code ứng dụng serialize một số mà không cắt bỏ phần thập phân
  • Một JSON blob như "{\"count\": 1}" — ai đó lưu cả object thay vì trích xuất trường số nguyên
  • Chuỗi rỗng "" — key được khởi tạo mà không có giá trị
  • Một số nằm ngoài phạm vi signed 64-bit (-9223372036854775808 đến 9223372036854775807)
  • Một key cũ còn sót lại từ mô hình dữ liệu trước, nay được dùng lại làm counter

Sửa nhanh: đặt lại key về số nguyên hợp lệ

Cần counter hoạt động ngay lập tức? Ghi đè giá trị:

# Kiểm tra giá trị hiện tại
127.0.0.1:6379> GET page_views
"3.5"

# Ghi đè bằng số nguyên gần nhất
127.0.0.1:6379> SET page_views 3
OK

# Bây giờ INCR hoạt động được rồi
127.0.0.1:6379> INCR page_views
(integer) 4

Muốn bắt đầu lại từ đầu? Chỉ cần xóa key và gọi INCR trực tiếp. Redis tự khởi tạo key bị thiếu về 0 trước khi tăng:

127.0.0.1:6379> DEL page_views
(integer) 1
127.0.0.1:6379> INCR page_views
(integer) 1

Xử lý nguyên nhân gốc rễ trong code ứng dụng

Trường hợp 1: Code của bạn ghi giá trị float thay vì số nguyên

Float là thủ phạm phổ biến nhất. Python, JavaScript, và một số ngôn ngữ khác có thể serialize một số như 1 thành "1.0" mà không có cảnh báo nào — và Redis từ chối tăng giá trị đó.

# Python — sai
import redis
r = redis.Redis()
r.set('page_views', 1.0)   # Lưu "1.0", không phải "1"
r.incr('page_views')        # Lỗi

# Python — đúng
r.set('page_views', int(1.0))   # Lưu "1"
r.incr('page_views')             # Hoạt động
// Node.js (ioredis) — sai
await redis.set('page_views', parseFloat('3.5'));  // Lưu "3.5"

// Node.js — đúng
await redis.set('page_views', Math.floor(3.5));   // Lưu "3"
await redis.incr('page_views');                    // Hoạt động

Trường hợp 2: Bạn đang lưu một JSON object và cố tăng giá trị của một trường bên trong

Redis string không hỗ trợ cập nhật từng phần. Hãy lưu counter dưới dạng hash field riêng biệt và dùng HINCRBY thay thế:

# Lưu từng trường riêng biệt dưới dạng hash
127.0.0.1:6379> HSET user:42 page_views 10 login_count 3
(integer) 2

# Tăng một trường theo kiểu atomic
127.0.0.1:6379> HINCRBY user:42 page_views 1
(integer) 11

# Giảm giá trị
127.0.0.1:6379> HINCRBY user:42 login_count -1
(integer) 2

Trường hợp 3: Counter kiểu float — dùng INCRBYFLOAT thay thế

Đang theo dõi điểm số, tỷ lệ, hoặc bất kỳ giá trị nào cần độ chính xác thập phân? Dùng INCRBYFLOAT. Giá trị được lưu vẫn phải là số, nhưng phần thập phân được chấp nhận:

127.0.0.1:6379> SET score 10.5
OK
127.0.0.1:6379> INCRBYFLOAT score 0.5
"11"
127.0.0.1:6379> INCRBYFLOAT score 1.25
"12.25"

Lưu ý một điểm: sau đủ nhiều lần gọi INCRBYFLOAT, giá trị được lưu sẽ chứa dấu thập phân. Nếu sau đó chạy INCR thuần trên key đó sẽ bị lỗi. Hãy tách riêng key float và key số nguyên.

Trường hợp 4: Race condition khi khởi tạo key

Hai tiến trình cùng khởi tạo một key là race condition kinh điển. Một tiến trình ghi giá trị không phải số nguyên; tiến trình kia ngay lập tức gọi INCR và gặp lỗi. Cách sửa rất đơn giản — hãy để INCR tự xử lý việc khởi tạo:

# Đừng làm thế này (race condition)
IF key not exists:
    SET counter 0
INCR counter

# Hãy làm thế này — INCR là atomic và tự khởi tạo từ 0 nếu key chưa tồn tại
INCR counter

Xác nhận đã sửa xong

# Kiểm tra giá trị là chuỗi số nguyên hợp lệ
127.0.0.1:6379> GET page_views
"4"

# INCR phải trả về số nguyên tiếp theo
127.0.0.1:6379> INCR page_views
(integer) 5

# DECR cũng phải hoạt động
127.0.0.1:6379> DECR page_views
(integer) 4

# INCRBY và DECRBY với bước nhảy
127.0.0.1:6379> INCRBY page_views 10
(integer) 14
127.0.0.1:6379> DECRBY page_views 3
(integer) 11

Cả bốn lệnh đều chạy không lỗi? Key đã sạch.

Thêm kiểm tra bảo vệ trong code trước khi tăng giá trị

Counter trên môi trường production xứng đáng có một lớp bảo vệ. Hàm helper Python này xác thực giá trị được lưu trước, đặt lại về 0 nếu bị hỏng, và ghi log cảnh báo để bạn biết điều đó đã xảy ra:

# Python helper
def safe_incr(r, key):
    val = r.get(key)
    if val is not None:
        try:
            int(val)  # Sẽ raise nếu không phải chuỗi số nguyên hợp lệ
        except (ValueError, TypeError):
            # Đặt lại về 0 và ghi log cảnh báo
            r.set(key, 0)
            print(f"WARNING: reset corrupt counter key '{key}' (was: {val})")
    return r.incr(key)

Tham khảo nhanh: các lệnh bị ảnh hưởng

  • INCR key — tăng thêm 1
  • INCRBY key amount — tăng thêm amount (số nguyên)
  • DECR key — giảm đi 1
  • DECRBY key amount — giảm đi amount (số nguyên)

Cả bốn lệnh đều yêu cầu giá trị phải là biểu diễn chuỗi của số nguyên signed 64-bit. INCRBYFLOAT là ngoại lệ duy nhất — nó chấp nhận số thập phân, nhưng nếu dùng lẫn lộn với các lệnh số nguyên trên cùng một key, sớm muộn bạn cũng sẽ quay lại gặp lỗi này.

Related Error Notes