Hunter-Chen / laowang-across-wall

最好用的免费VPN (长期更新)
97 stars 28 forks source link

读写并发测试 #1

Open Hunter-Chen opened 4 years ago

Hunter-Chen commented 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("------------------------------------");
    }
}
Hunter-Chen commented 4 years ago

在本机上测试

单纯读有 41000QPS 模拟数据库和缓存一致性更新的情况下(也就是如上的代码)
3秒更新一次 QPS 38000 如果改成1秒更新一次数据库QPS 26000 200毫秒更新一次数据库 QPS 11000