項目中遇到了MGET獲取10000+數據時超時的問題,然后找到網友的測試結果如下(這里就不再重發測試代碼,測試結果一致)
結論
- 一次操作10個key的性能達到一次操作1個key的88%
- 一次操作20個key的性能達到一次操作1個key的72%
- 一次操作50個key的性能達到一次操作1個key的59%
- 一次操作60個key的性能達到一次操作1個key的53%
- 一次操作80個key的性能達到一次操作1個key的43%
- 一次操作100個key的性能大道一次操作1個key的41%
- 一次操作200個key的性能只能達到一次操作1個key的25%,大約是一次處理100個key的60%
- 一次操作500個key的性能只能達到一次操作1個key的11%,大約是一次處理100個key的28%
- 一次操作800個key的性能只能達到一次操作1個key的7%,大約是一次處理100個key的17%
- 當key數目在10以內時,mget性能下降趨勢非常小,性能基本上能達到redis實例的極限
- 當key數目在10~100之間時,mget性能下降明顯,需要考慮redis性能衰減對系統吞吐的影響
- 當key數目在100以上時,mget性能下降幅度趨緩,此時redis性能已經較差,不建議使用在OLTP系統中,或者需要考慮其他手段來提升性能。
解決辦法及測試結果(上代碼看結果)
package cn.hs; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; @RunWith(SpringRunner.class) @SpringBootTest public class RedisGetTests { private static final String TYPE_NAME = RedisGetTests.class.getTypeName(); @Resource private RedisTemplate<String, Object> redisTemplate; @Test public void contextLoads() { Set<String> keySet = new HashSet<>(); for (int i = 0; i < 100000; i++) { String key = TYPE_NAME + ":" + i; keySet.add(key); } long time = System.currentTimeMillis(); multiGet(keySet); System.out.println("redisTemplate.opsForValue().multiGet \t 耗時:" + (System.currentTimeMillis() - time)); // 排除,太慢了 // time = System.currentTimeMillis(); // getData(keySet); // System.out.println("redisTemplate.opsForValue().get \t\t 耗時:" + (System.currentTimeMillis() - time)); // 推薦 time = System.currentTimeMillis(); getDataByPipelined(keySet); System.out.println("redisTemplate.executePipelined \t\t\t 耗時:" + (System.currentTimeMillis() - time)); } private void multiGet(Set<String> keySet) { redisTemplate.opsForValue().multiGet(keySet); } private void getData(Set<String> keySet) { for (String key : keySet) { redisTemplate.opsForValue().get(key); } } private void getDataByPipelined(Set<String> keySet) { redisTemplate.executePipelined((RedisCallback<Object>) redisConnection -> { for (String key : keySet) { redisConnection.get(key.getBytes(StandardCharsets.UTF_8)); } return null; }); } }
輸出結果
redisTemplate.opsForValue().multiGet 耗時:422
redisTemplate.executePipelined 耗時:177
推薦方式
private void getDataByPipelined(Set<String> keySet) { redisTemplate.executePipelined((RedisCallback<Object>) redisConnection -> { for (String key : keySet) { redisConnection.get(key.getBytes(StandardCharsets.UTF_8)); } return null; }); }