Open Hunter-Chen opened 4 years ago
package com.hunterstudy.springstudy; import com.google.common.base.Stopwatch; import lombok.extern.slf4j.Slf4j; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * @author Hunter Chen * @since 2020-06-05 */ @SpringBootTest @RunWith(SpringRunner.class) @Slf4j public class MyRedisLockTest { @Autowired private RedissonClient redissonClient; @Autowired private StringRedisTemplate redisTemplate; private static final String REDIS_KEY = "test-key"; private static final String REDIS_LOCK = "lock"; // 模拟数据库中的值(修改是原子性的) private volatile int dbValue = 0; @Before public void dataUpdateTest() { // 开一个线程,不停地模拟更新数据库 new Thread(() -> { RLock lock = redissonClient.getLock(REDIS_LOCK); while (true) { try { lock.lock(500, TimeUnit.MILLISECONDS); redisTemplate.delete(REDIS_KEY); // 模拟数据库更新 mapper.update(); dbValue = new Random(100).nextInt(); Thread.sleep(200); redisTemplate.delete(REDIS_KEY); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } // 模拟3秒更新一次数据库 try { Thread.sleep(3000); } catch (InterruptedException e) { } } }).start(); } @Test public void redisIOTest() { final int NUM_OF_THREADS = 100; final long NUM_OF_COUNTS = 1000_000; Stopwatch stopwatch = Stopwatch.createUnstarted(); CountDownLatch countDownLatch = new CountDownLatch(NUM_OF_THREADS + 1); AtomicLong counts = new AtomicLong(); RLock lock = redissonClient.getLock(REDIS_LOCK); for (int i = 0; i < NUM_OF_THREADS; i++) { new Thread(() -> { countDownLatch.countDown(); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } while (true) { String result = redisTemplate.opsForValue().get(REDIS_KEY); if (result == null) { // 说明缓存正在被更新,或者缓存过期了 while (true) { try { boolean haveLock = lock.tryLock(50, 300, TimeUnit.MILLISECONDS); if (haveLock) { // 模拟数据库读取 mapper.get() result = Integer.toString(dbValue); Thread.sleep(200); // 更新缓存 redisTemplate.opsForValue().set(REDIS_KEY, result); lock.unlock(); // return result break; } else { String anotherResult = redisTemplate.opsForValue().get(REDIS_KEY); if (anotherResult != null) { // return anotherResult break; } } } catch (InterruptedException e) { e.printStackTrace(); } } } long count = counts.addAndGet(1); if (count == NUM_OF_COUNTS) { stopwatch.stop(); } } }).start(); } countDownLatch.countDown(); stopwatch.start(); while (stopwatch.isRunning()) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } long seconds = stopwatch.elapsed(TimeUnit.SECONDS); System.out.println("------------------------------------"); System.out.println("总耗时秒数: " + seconds); System.out.println("QPS是 " + (NUM_OF_COUNTS / seconds)); System.out.println("------------------------------------"); } }
在本机上测试
单纯读有 41000QPS 模拟数据库和缓存一致性更新的情况下(也就是如上的代码) 3秒更新一次 QPS 38000 如果改成1秒更新一次数据库QPS 26000 200毫秒更新一次数据库 QPS 11000