何が起きたのか?
INCR、INCRBY、DECR、または DECRBY コマンドを実行したところ、Redisが以下のエラーを返しました:
(error) ERR value is not an integer or out of range
Redisのカウンターコマンドは、保存された値が純粋な10進数の整数のように見える場合にのみ動作します。浮動小数点数、JSONブロブ、空文字列は使用できません。そのキーに他の値が格納された瞬間、コマンドは即座に失敗します。
30秒でエラーを再現する
# Store a 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
# Store a string
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
# Store an empty string
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
# Integer too large for 64-bit signed range
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
まずキーを診断する
何も変更せずに、まず正確に何が格納されているかを確認しましょう:
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
よくある原因は以下の通りです:
"1.0"や"0.5"のような浮動小数点数 — アプリケーションコードが小数部を切り捨てずに数値をシリアライズした"{\"count\": 1}"のようなJSONブロブ — 整数フィールドを抽出せずにオブジェクト全体を保存した- 空文字列
""— キーが値なしで初期化された - 符号付き64ビット範囲(
-9223372036854775808~9223372036854775807)を超えた数値 - 古いデータモデルから残った古いキーが、カウンターとして再利用されている
クイックフィックス:キーを有効な整数にリセットする
すぐにカウンターを動作させる必要がありますか?上書きしてください:
# Check current value
127.0.0.1:6379> GET page_views
"3.5"
# Overwrite with the closest integer
127.0.0.1:6379> SET page_views 3
OK
# Now INCR works
127.0.0.1:6379> INCR page_views
(integer) 4
ゼロから始める場合は、キーを削除して INCR を直接呼び出すだけです。Redisは存在しないキーをインクリメント前に 0 に初期化します:
127.0.0.1:6379> DEL page_views
(integer) 1
127.0.0.1:6379> INCR page_views
(integer) 1
アプリケーションコードの根本原因を修正する
ケース1:コードが整数の代わりに浮動小数点数を書き込んでいる
浮動小数点数が最も頻繁な原因です。Python、JavaScript、その他いくつかの言語では、1 のような数値を警告なしに "1.0" としてシリアライズすることがあります。そしてRedisはそれをインクリメントすることを拒否します。
# Python — wrong
import redis
r = redis.Redis()
r.set('page_views', 1.0) # Stores "1.0", not "1"
r.incr('page_views') # Boom
# Python — correct
r.set('page_views', int(1.0)) # Stores "1"
r.incr('page_views') # Works
// Node.js (ioredis) — wrong
await redis.set('page_views', parseFloat('3.5')); // Stores "3.5"
// Node.js — correct
await redis.set('page_views', Math.floor(3.5)); // Stores "3"
await redis.incr('page_views'); // Works
ケース2:JSONオブジェクトを保存してその中のフィールドをインクリメントしようとしている
Redis文字列は部分的な更新をサポートしていません。カウンターを別のハッシュフィールドとして保存し、代わりに HINCRBY を使用してください:
# Store each field separately as a hash
127.0.0.1:6379> HSET user:42 page_views 10 login_count 3
(integer) 2
# Increment one field atomically
127.0.0.1:6379> HINCRBY user:42 page_views 1
(integer) 11
# Decrement
127.0.0.1:6379> HINCRBY user:42 login_count -1
(integer) 2
ケース3:浮動小数点カウンター — 代わりにINCRBYFLOATを使用する
スコア、レート、または小数点精度が必要な値を追跡しますか?INCRBYFLOAT を使用してください。保存される値は依然として数値である必要がありますが、小数は問題ありません:
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"
注意点が一つあります:INCRBYFLOAT を何度も呼び出すと、保存された値に小数点が含まれるようになります。後でそのキーに対して通常の INCR を実行すると失敗します。浮動小数点キーと整数キーは分けて管理してください。
ケース4:キー初期化の競合状態
2つのプロセスが同時に同じキーを初期化しようとするのは典型的な競合です。一方が非整数値を書き込み、もう一方が即座に INCR を呼び出してエラーになります。解決策はシンプルです — 初期化は INCR 自身に任せてください:
# Don't do this (race condition)
IF key not exists:
SET counter 0
INCR counter
# Do this instead — INCR is atomic and initializes from 0 if key is missing
INCR counter
修正を確認する
# Check the value is a valid integer string
127.0.0.1:6379> GET page_views
"4"
# INCR should return the next integer
127.0.0.1:6379> INCR page_views
(integer) 5
# DECR should work too
127.0.0.1:6379> DECR page_views
(integer) 4
# INCRBY and DECRBY with step
127.0.0.1:6379> INCRBY page_views 10
(integer) 14
127.0.0.1:6379> DECRBY page_views 3
(integer) 11
4つ全てがエラーなく動作しますか?キーは正常です。
インクリメント前にコードにガードを追加する
本番環境のカウンターには安全策が必要です。このPythonヘルパーは、まず保存された値を検証し、破損していればリセットして、発生したことがわかるように警告をログに記録します:
# Python helper
def safe_incr(r, key):
val = r.get(key)
if val is not None:
try:
int(val) # Will raise if not a valid integer string
except (ValueError, TypeError):
# Reset to 0 and log a warning
r.set(key, 0)
print(f"WARNING: reset corrupt counter key '{key}' (was: {val})")
return r.incr(key)
クイックリファレンス:影響を受けるコマンド
INCR key— 1増加させるINCRBY key amount— 指定量増加させる(整数)DECR key— 1減少させるDECRBY key amount— 指定量減少させる(整数)
4つ全てのコマンドは、値が符号付き64ビット整数の文字列表現である必要があります。INCRBYFLOAT は唯一の例外で小数を受け付けますが、同じキーに対して整数コマンドと混在させると、最終的に再びこのエラーに戻ってきます。

