本文介紹訪問云數據庫 Tair(兼容 Redis)時的常見報錯與解決方法。
報錯概覽
分類 | 報錯項 |
Redis通用異常 | |
Proxy(代理模式)通用異常 | |
Lua腳本與事務(Transaction) | |
Jedis客戶端 | |
Lettuce客戶端 | |
Redisson客戶端 | |
Spring Data Redis客戶端 | |
StackExchange.Redis客戶端 | |
Predis客戶端 | |
phpredis客戶端 | |
Go-redis客戶端 | |
node-redis客戶端 |
Redis通用異常
ERR illegal address
可能原因:未將客戶端的IP地址添加至Tair實例的白名單中。
解決方法:將客戶端的IP地址添加入至Tair實例的白名單中,具體操作請參見連接診斷。
ERR sentinel compatibility mode is disabled
可能原因:未開啟Tair實例的Sentinel兼容。
解決方法:在控制臺開啟Sentinel兼容,具體操作請參見開啟Sentinel兼容。
ERR max number of clients reached
可能原因:客戶端的連接數超過了Tair實例的最大連接數。
NOAUTH Authentication required
可能原因:Tair實例設置了密碼鑒權,但客戶端沒有提供密碼或提供了錯誤的密碼。
解決方法:請使用正確的賬號密碼進行訪問,更多信息請參見Redis實例登錄方式。
若實例使用Sentinel模式訪問,請參見使用Sentinel兼容模式連接Redis。
WRONGPASS invalid username-password pair
可能原因:密碼錯誤。
解決方法:請使用正確的賬號密碼進行訪問,更多信息請參見Redis實例登錄方式。
若實例使用Sentinel模式訪問,請參見使用Sentinel兼容模式連接Redis。
ERR invalid password
可能原因:密碼錯誤。
解決方法:請使用正確的賬號密碼進行訪問,更多信息請參見Redis實例登錄方式。
若在DMS中產生該報錯,則有可能是DMS保存了之前登錄的賬號密碼,而此時密碼發生了修改。您可以在DMS數據庫實例列表中,右鍵單擊目標實例,選擇編輯實例,在數據庫密碼文本框中輸入新的密碼進行重試。
Connection reset by peer
可能原因:客戶端連接被關閉,通常是由于客戶端緩沖區異常而關閉客戶端連接。
解決方法:檢查應用側代碼或調整客戶端Buffer的大小,更多信息請參見Unexpected end of stream章節。
UnknownHostException
或failed to connect: xxx.redis.rds.aliyuncs.com could not be resolved
。
可能原因:客戶端無法正常解析Tair實例的域名地址。
解決方案:請設置正確的DNS服務器地址,更多信息請參見解決因域名解析失敗導致的連接問題。
OOM command not allowed when used memory > 'maxmemory'
可能原因:Tair實例已使用的內存超過該實例的最大配置(maxmemory)。
如果是Tair集群實例,可能是其中一個節點已使用的內存已超過該節點的最大配置。
WRONGTYPE Operation against a key holding the wrong kind of value
可能原因:命令使用錯誤,例如對String數據類型執行HASH
命令。
解決方法:修改錯誤代碼或命令,更多信息請參Redis Commands。
ERR unknown command 'xxx'
可能原因:Tair不存在您調用的命令。
解決方法:檢查當前實例版本的命令支持情況,更多信息請參見Redis開源版命令支持。
最新小版本將提供更豐富的功能與穩定的服務,建議將實例的小版本升級到最新,具體操作請參見升級小版本與代理版本。
ERR command 'xxx' not support for your account
可能原因:阿里云禁止用戶執行某些Tair命令,或您手動在#no_loose_disabled-commands參數中配置了禁止執行的命令。更多信息請參見Redis開源版命令支持和禁用高風險命令。
解決方法:若需執行您禁用的命令,您可以在#no_loose_disabled-commands參數中刪除對應命令。
NOPERM this user has no permissions to run the 'xxx'
可能原因:阿里云禁止用戶執行某些Tair命令,或您手動在#no_loose_disabled-commands參數中配置了禁止執行的命令。更多信息請參見Redis開源版命令支持和禁用高風險命令。
解決方法:若需執行您禁用的命令,您可以在#no_loose_disabled-commands參數中刪除對應命令。
ERR FLUSHDB is not allowed in migrating mode
可能原因:Tair云原生版集群架構實例在執行增加或減少數據節點時,禁止使用FLUSHDB
或FLUSHALL
命令。
解決方法:等待Tair云原生版集群架構實例執行增加或減少數據節點結束,更多信息請參見調整實例的分片數量。
CROSSSLOT Keys in request don't hash to the same slot
可能原因:Tair集群架構直連模式不支持跨Slot執行涉及多Key的命令,例如DEL、MSET、MGET等。
解決方法:
在執行操作命令前增加確認Key Slot的邏輯(例如通過CLUSTER KEYSLOT命令),確保單個命令執行的所有Key在一個Slot中。
通過改造Key名稱,增加Hash tags使其保證在同一個Slot,該方案在使用過程中需避免數據傾斜,更多信息請參見Hash tags。
改造實例為集群架構代理(Proxy)模式,Proxy模式支持跨Slot執行DEL、MGET、MSET等涉及多Key的命令,更多信息請參見Tair Proxy特性說明。
ERR READONLY you can't write against a read only instance
可能原因:Tair實例在主備切換、升降配或小版本升級時,將出現秒級的連接閃斷和30秒以內的只讀狀態。
解決方法:屬于正常現象,實例會自動恢復,您無需進行任何操作。請提前為您的應用設計重連機制和異常處理的能力,更多信息請參見升級實例配置。
Proxy(代理模式)通用異常
ERR client ip is not in whitelist
可能原因:未將客戶端的IP地址添加至Tair實例的白名單中。
解決方法:將客戶端的IP地址添加入至Tair實例的白名單中,具體操作請參見連接診斷。
NOWRITE You can't write against a non-write redis
或NOREAD You can't read against a non-read redis
。
可能原因:實例處于欠費或到期狀態,實例狀態顯示為已鎖定。
解決方法:對賬號進行充值,或對到期的實例進行續費,更多信息請參見到期與欠費。
ERR syntax error
可能原因:命令語法錯誤,例如需要傳入4個參數,實際僅傳入3個參數。
解決方法:檢查命令格式是否正確,更多信息請參Redis Commands。
ERR no such db node
可能原因:使用阿里云自研的Tair命令時,傳入的db node
錯誤。
解決方法:傳入正確的db node
,db node
需小于分片數量,更多信息請參見阿里云自研的Proxy命令。
ERR 'xxx' command keys must in same slot
可能原因:在Tair集群架構實例中,通過事務或腳本執行命令時要求所有Key必須在同一個Slot中,否則將返回上述錯誤。
解決方法:改造事務或腳本,您可以通過CLUSTER KEYSLOT
命令獲取目標Key的Hash Slot。
Tair集群架構實例會根據CRC算法將Key均勻的寫入不同Slot中。若您希望將多個Key寫入到一個Slot中,您可以使用Hash Tags,但該方法若使用不當容易造成數據傾斜,請謹慎使用,更多信息請參見Hash tag。
ERR for redis cluster, eval/evalsha number of keys can't be negative or zero
可能原因:執行EVAL
和EVALSHA
命令未傳入Key或numkeys參數的值未大于0。
解決方法:執行EVAL
和EVALSHA
命令時,至少需要傳入一個Key且numkeys參數的值大于0,更多信息請參見Lua腳本基本語法。
ERR request refused, too many pending request, now count xxx, beyond threshold xxx
可能原因:由于客戶端使用了不合理的Pipeline,Tair后端堆積了過多未處理的Request,新請求被拒絕。
解決方法:減少Pipeline的請求數量。
ERR redis temporary failure
可能原因:部分Tair子實例訪問超時,可能是網絡抖動、連接數到達上限導致斷鏈、實例正在進行主備切換或執行慢查詢等導致。
解決方法:屬于正常現象,實例會自動恢復,您無需進行任何操作。請提前為您的應用設計重連機制和異常處理的能力。
ERR redis temporary failure (ErrorCode 7002)
可能原因:部分Tair子實例訪問超時,可能是實例正在變配或正在進行主備切換導致。
解決方法:屬于正常現象,實例會自動恢復,您無需進行任何操作。請提前為您的應用設計重連機制和異常處理的能力。
Lua腳本與事務(Transaction)
NOSCRIPT No matching script. Please use EVAL.
可能原因:使用EVALSHA
命令時,若SHA1值對應的腳本未緩存至Tair中。
解決方法:通過EVAL
命令或SCRIPT LOAD
命令將目標腳本緩存至Tair中后進行重試,更多信息請參見處理NOSCRIPT錯誤。
BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
可能原因:處理Lua腳本超時。
解決方法:通過SCRIPT KILL命令終止Lua腳本或等待Lua腳本執行結束,更多信息請參見處理Lua腳本超時。
ERR command eval not support for normal user
可能原因:無法執行EVAL
的相關命令。
解決方法:請將實例的小版本升級至最新,具體操作請參見升級小版本與代理版本。
ERR eval/evalsha command keys must be in same slot
解決方法:改造Lua腳本,您可以通過CLUSTER KEYSLOT
命令獲取目標Key的Hash Slot,更多信息請參見集群中Lua腳本的限制。
ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array
可能原因:Proxy(代理)節點的Lua腳本限制。
解決方法:所有Key都應該由KEYS數組來傳遞,例如EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo {foo}bar
,不能使用Lua變量替換KEYS,更多信息請參見集群中Lua腳本的限制。
EXECABORT Transaction discarded because of previous errors
可能原因:事務中的命令執行失敗,可能是命令語法錯誤或運行錯誤等。
解決方法:檢查代碼邏輯,修復錯誤命令。
UNKILLABLE Sorry the script already executed write commands against the dataset.
可能原因:當前Lua腳本已執行寫命令,此時SCRIPT KILL命令無法生效。
解決方法:在控制臺的實例列表頁面,找到對應實例,單擊操作列的重啟,更多信息請參見重啟實例。
UNKILLABLE The busy script was sent by a master instance in the context of replication and cannot be killed.
可能原因:當前Lua腳本已經被Master節點發給自己的replica節點,此時SCRIPT KILL命令無法生效。
解決方法:在控制臺的實例列表頁面,找到對應實例,單擊操作列的重啟,更多信息請參見重啟實例。
NOTBUSY No scripts in execution right now.
可能原因:當前沒有正在運行Lua腳本。
解決方法:無需處理,不要調用SCRIPT KILL命令。
Jedis客戶端
Could not get a resource from the pool
可能原因:無法從連接池獲取到Jedis連接。
當blockWhenExhausted參數為true(默認)時,若連接池沒有可用的Jedis連接,客戶端通常會等待一段時間(等待時間由maxWaitMillis參數決定,單位為毫秒),若長時間沒有獲取到可用的Jedis連接,會出現如下異常:
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool … Caused by: java.util.NoSuchElementException: Timeout waiting for idle object at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
當blockWhenExhausted參數為false時,若連接池沒有可用的Jedis連接,則會立即出現如下異常:
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool … Caused by: java.util.NoSuchElementException: Timeout waiting for idle object at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
解決方法:您可以從如下幾個方面進行排查。
連接泄露
JedisPool默認maxTotal值為8,從如下代碼得知,從JedisPool中獲取了8個Jedis資源,但沒有歸還資源。因此,在第9次嘗試獲取Jedis資源時,無法調用
jedisPool.getResource().ping()
。GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379); // 向JedisPool借用8次連接,但是沒有執行歸還操作。 for (int i = 0; i < 8; i++) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.ping(); } catch (Exception e) { logger.error(e.getMessage(), e); } } jedisPool.getResource().ping();
推薦使用如下規范代碼。
Jedis jedis = null; try { jedis = jedisPool.getResource(); // 具體的命令。 jedis.executeCommand() } catch (Exception e) { // 如果命令有Key,建議在錯誤日志中把Key打印出來,對于集群架構來說,可通過Key定位到具體節點。 logger.error(e.getMessage(), e); } finally { // 注意:這里不是關閉連接,在JedisPool模式下,Jedis會被歸還給資源池。 if (jedis != null) jedis.close(); }
maxTotal值設置得過小
當業務并發量大時,可能會由于maxTotal值設置的過小導致異常。例如,一次命令運行時間的平均耗時約為1ms(
Borrow|Return resource
+ Jedis執行命令 + 網絡時間),一個連接的QPS大約為1000,業務期望的QPS為50000,則理論上需要的maxTotal值為50000 / 1000 = 50。在該情況下,您可以在客戶端所在的機器上執行下述命令,該命令返回的結果為連接客戶端的連接數,您可以根據該數值對maxTotal值進行調整。
netstat -an | grep 6379 | grep EST | wc -l
Jedis連接阻塞
當Tair實例發生阻塞時(例如慢查詢等原因),所有連接會在超時時間范圍內等待,當并發量較大時,會造成連接池資源不足,更多信息請參見connect timed out。
Jedis連接被拒絕
從JedisPool中獲取連接時,由于沒有空閑連接,需要重新生成一個Jedis連接,但是連接被拒絕,異常示例如下:
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool at redis.clients.util.Pool.getResource(Pool.java:50) at redis.clients.jedis.JedisPool.getResource(JedisPool.java:99) at TestAdmin.main(TestAdmin.java:14) Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused at redis.clients.jedis.Connection.connect(Connection.java:164) at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:80) at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1676) at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:87) at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:861) at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435) at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363) at redis.clients.util.Pool.getResource(Pool.java:48) ... 2 more Caused by: java.net.ConnectException: Connection refused at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:579) at redis.clients.jedis.Connection.connect(Connection.java:158) ... 9 more
可以從
at redis.clients.jedis.Connection.connect(Connection.java:158)
中看出,實際是在創建一個Socket連接,并調用connect
函數,但是被拒絕連接,如下為Jedis源碼。socket.setSoLinger(true, 0); 158: socket.connect(new InetSocketAddress(host, port), connectionTimeout);
通常情況下,該問題需要排查Tair的域名配置是否正確、該段時間網絡是否正常。
java.net.SocketTimeoutException: connect timed out
可能原因:客戶端連接Tair實例超時。
解決方法:更多信息請參見連接問題排查流程。
java.net.SocketTimeoutException: Read timed out
可能原因:網絡不穩定、讀寫超時時間過短、存在慢查詢或發生阻塞等原因造成Jedis API調用超時。
解決方法:適當增大超時時間,或進行實例診斷,查看實例在對應時間點是否存在性能問題或異常。
若在DMS中產生該報錯,則有可能是實例專有網絡的連接地址或端口被修改了。您可以在DMS數據庫實例列表中,右鍵單擊目標實例,選擇編輯實例,然后選擇錄入方式為連接串地址,將修改后的實例連接地址填寫到連接串地址文本框中進行重試
No reachable node in cluster
可能原因:JedisCluster地址無法訪問。
解決方法:若是首次訪問Tair實例,請檢查是否將客戶端的IP地址添加至Tair白名單中或客戶端的網絡情況;若不是首次訪問Tair實例,可以進行實例診斷,進行問題定位。
Caused by: java.lang.NumberFormatException: For input string: "6379@13028"
可能原因:Jedis 2.8.0及以下版本引入了ClusterNodeInformationParser
來解析cluster slots
返回值,但Redis后續更改了此命令返回值類型,所以報錯NumberFormatException
。
解決方法:將Jedis升級至2.9.0及以上版本。
No more cluster attempts left
可能原因:JedisCluster在API超時之后會默認重試5次(MaxAttempts,默認為5),并且在均失敗之后拋出此錯誤。
解決方法:適當增大超時時間或進行實例診斷。
Unexpected end of stream
可能原因:Jedis緩沖區異常,您可以從如下幾個方面進行排查。
多個線程使用一個Jedis連接
通常情況下,一個線程使用一個Jedis連接。例如下面代碼就是兩個線程共用了一個Jedis連接:
new Thread(new Runnable() { public void run() { for (int i = 0; i < 100; i++) { jedis.get("hello"); } } }).start(); new Thread(new Runnable() { public void run() { for (int i = 0; i < 100; i++) { jedis.hget("haskey", "f"); } } }).start();
為避免出現這種情況,您可以使用JedisPool管理Jedis連接,實現線程安全。
長時間閑置連接
長時間閑置連接會被服務端主動斷開,請查詢實例的timeout參數配置、Jedis連接池配置,確定是否需要進行空閑超時檢測。
說明默認設置下,即使某個客戶端已經空閑了很長時間,Tair也不會主動斷開與該客戶端的連接,若您調整過timeout參數,則可能會遇到該問題。更多信息請參見設置客戶端連接的空閑時間。
解決方法:檢查是否有多線程共用Jedis代碼或由于長時間閑置連接造成服務端斷開連接。
java.lang.Long cannot be cast to java.util.List
可能原因:若多個線程操作同一個Jedis連接就會返回該報錯,Jedis本身存在線程安全問題。
解決方法:Jedis的正確使用方法是一個線程操作一個Jedis。您可以使用JedisPool(非Jedis)避免該問題。
Broken pipe (Write failed)
可能原因:在Jedis單連接模式(未使用JedisPool)下超時后,客戶端關閉了Socket,此時若您繼續調用讀寫接口寫入數據,會返回該報錯。
解決方法:Jedis的正確使用方法是一個線程操作一個Jedis。您可以使用JedisPool(非Jedis)避免該問題。
No way to dispatch this command to Redis Cluster because keys have different slots
可能原因:JedisCluster操作的Key不在同一個Slot(槽)中。
解決方法:通過Hash tags對Key進行改造,更多信息請參見Hash tags。
您也可以使用Proxy(代理)模式屏蔽集群的限制。
Lettuce客戶端
Connection to xxx not allowed. This Partition is not known in the cluster view.
可能原因:Lettuce客戶端在默認情況下,配置為refreshOption = null , validateClusterNodeMembership = true
,表示開啟validateClusterNodeMembership
檢測。在Tair實例地址發生路由變化后,由于沒有開啟refreshOption
,即不會更新路由表,此時validateClusterNodeMembership
檢測就會返回該報錯。
解決方法:配置refreshOption
選項,并且設置validateClusterNodeMembership
為false
,更多信息請參見Lettuce。
io.lettuce.core.RedisConnectionException: Unable to connect xxx
可能原因:客戶端連接Tair實例超時。
解決方法:更多信息請參見連接問題排查流程。
java.nio.channels.UnresolvedAddressException
可能原因:大概率是Netty版本沖突。
解決方法:檢查Netty依賴,建議選擇較高的版本,更多信息請參見Spring-boot issues。
ERR Unknown sentinel subcommand 'master'
可能原因:Lettuce在master-replica Sentinel模式下會向Redis實例發送Sentinel master/slave
命令,而Tair實例在Sentinel兼容模式下僅支持Sentinel get-master-addr-by-name
命令,故產生該報錯。
解決方法:修改代碼為普通模式(非Sentinel),Tair采用自研的高可用服務HA組件,無需Sentinel。
部分實例版本不支持RESP3協議,報錯unknown command
可能原因:Redis 6.0及以上版本支持了RESP3協議,可通過HELLO命令切換RESP協議。但部分低版本實例不支持HELLO命令,可能會存在兼容性問題。
解決方法:您可以直接在程序中指定以RESP2協議訪問Tair實例,示例如下:
client.setOptions(ClientOptions.builder()
.protocolVersion(ProtocolVersion.RESP2)
.build());
若使用Spring-data-redis with Lettuce,示例如下:
LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder().
clientOptions(ClientOptions.builder().protocolVersion(ProtocolVersion.RESP2).build()).build();
return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
Redisson客戶端
org.redisson.client.RedisConnectionException: Unable to connect to Redis server xxx
可能原因:客戶端連接Tair實例超時。
解決方法:更多信息請參見連接問題排查流程。
No enum constant org.redisson.cluster.ClusterNodeInfo.Flag.NOFAILOVER
可能原因:Redisson低版本Bug,更多信息請參見Redisson issue。
解決方法:將Redisson升級至3.11.6及以上版本。
Spring Data Redis客戶端
NOPERM this user has no permissions to run the 'config|get' command
可能原因:在實例信息頁確認您的實例版本為Redis 7.0。云數據庫 Tair(兼容 Redis)的7.0版本禁用CONFIG
命令。
應用啟動時,Spring Data Redis會執行CONFIG SET
命令動態設置notify-keyspace-events
參數來啟用KeyspaceEventMessageListener功能。因為CONFIG SET
命令被禁用,導致啟動時報錯。
解決方法:設置keyspaceNotificationsConfigParameter為空,繞過該問題,您可以參見SpringRedisTest示例,更多信息請參見Spring Data Redis。
@EnableRedisRepositories(enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP, keyspaceNotificationsConfigParameter = "")
StackExchange.Redis客戶端
Multiple databases are not supported on this server; cannot switch to database
可能原因:集群架構不支持執行SELECT
命令。
解決方法:將cluster_compat_enable
參數設置為0(即關閉原生Redis Cluster語法兼容),具體操作請參見設置實例參數,然后重啟客戶端應用后重試。
Predis客戶端
Error while reading line from the server.
可能原因:讀取超時,您可能正在執行一個慢查詢。
解決方法:適當增大超時時間或將客戶端的read_write_timeout
參數改為0
或-1
,更多信息請參見Predis questions。
phpredis客戶端
Cannot assign requested address
可能原因:客戶端通過短連接訪問Tair實例時,產生該報錯。
解決方法:使用pconnect
替換connect
的連接方式,或修改客戶端所在ECS實例的tcp_max_tw_buckets
內核參數,更多信息請參見Cannot assign requested address。
redis protocol error, got ' ' as reply type byte
可能原因:phpredis低版本Bug,更多信息請參見phpredis issues。
解決方法:將phpredis升級至最新版。
php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution
解決方案:請設置正確的DNS服務器地址,更多信息請參見解決因域名解析失敗導致的連接問題。
可能原因:客戶端無法正常解析Tair實例的域名地址。
Go-redis客戶端
panic: got 4 elements in cluster info address, expected 2 or 3
可能原因:您的Redis版本為7.0及以上,但未使用兼容的Go-redis客戶端版本導致,更多信息請參見Go-redis issues。
解決方案:升級、使用Go-redis客戶端9.0及以上版本。
node-redis客戶端
SCAN命令死循環或者返回數據為空
可能原因:SCAN命令返回的Cursor值可能超過了JavaScript最大可精確表達的數值Number.MAX_SAFE_INTEGER,導致Cursor不準確,引發死循環,更多信息請參見GitHub #2561。
解決方案:將node-redis客戶端升級至5.0.0及以上版本。