最近使用redis集群進行incr操作,總是發現計數不准確,后來經過檢查發現redis在執行incr超時會執行重試機制,造成計數不准確,測試代碼:
/** * incrf: * 將 key 中儲存的數字值增一。 如果 key 不存在,那么 key 的值會先被初始化為 0 ,然后再執行 INCR 操作。 如果值包含錯誤的類型,或字符串類型的值不能表示為數字,那么返回一個錯誤。 本操作的值限制在 64 位(bit)有符號數字表示之內。 這是一個針對字符串的操作,因為 Redis 沒有專用的整數類型,所以 key 內儲存的字符串被解釋為十進制 64 位有符號整數來執行 INCR 操作。 返回值: 執行 INCR 命令之后 key 的值。 這里有問題,最終數據結果大於10000 這是因為設置的超時時間太小了,他去重試了,所以最終結果大於10000 */ @Test public void incrTest() throws InterruptedException { /** * 測試線程安全 */ jedisCluster.del("incrNum"); final AtomicInteger atomicInteger = new AtomicInteger(0); final CountDownLatch countDownLatch = new CountDownLatch(10); ExecutorService executorService = Executors.newFixedThreadPool(10); for(int i = 0 ; i < 10 ; i ++){ executorService.submit(new Runnable() { @Override public void run() { //每個線程增加1000次,每次加1 for(int j = 0 ; j < 1000 ; j ++){ atomicInteger.incrementAndGet(); jedisCluster.incr("incrNum"); } countDownLatch.countDown(); } }); } countDownLatch.await(); System.out.println(jedisCluster.get("incrNum")); System.out.println(atomicInteger); }
這是個多線程的demo,不管多線程還是單線程,我最終得到的結果都是比10000大。這就奇怪了,incr
是原子的,理論上不應該會這樣。我起初以為是jedis客戶端的BUG,后來仔細一看才發現,這不是BUG,是我在創建jediscluster的時候設置的超時時間太短了,導致其超時重試。
解決辦法
將超時時間設置的大一些。
我們在使用這些開源框架的時候,一定要全面了解其運作原理,這樣才能事半功倍。另外需要注意,想httpclient、dubbo等RPC框架都會有超時重試機制,在使用的時候要注意。