微服务: nacos配置中心开启-> 创建jvm的本地缓存(减少数据库压力)实战,遇到的问题,以及解决方式(双重校验)

190 阅读5分钟

目录

先看一个测试案例 微服务在后面!!!

应用场景: 

01 测试代码:(放到后面, 主要看效果)

进行测试: -------------------->

异常问题修改

 修改方案一:(不推荐)   

修改方案二: ( jdk1.5推出,推荐)

数据添加错误(重复添加修复)

添加一个同步代码块

 查看测试结果: 成功!!!  写一个好代码真难

 微服务  使用场景

 当并发量大的时候选择开启  不需要的时候关闭


先看一个测试案例 微服务在后面!!!

应用场景: 

       数据量小 访问频率高 数据变化频率低(数据不一致)

01 测试代码:(放到后面, 主要看效果)

(模拟多线程并发的安全问题)

 public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        Thread t0 = new Thread() {
            @Override
            public void run() {
                System.out.println(selectAll());
            }
        };
        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println(selectAll());
            }
        };
        Thread t2 = new Thread() {
            @Override
            public void run() {
                System.out.println(selectAll());
            }
        };
        Thread t3 = new Thread() {
            @Override
            public void run() {
                System.out.println(selectAll());
            }
        };
        Thread t4 = new Thread() {
            @Override
            public void run() {
                System.out.println(selectAll());
            }
        };
        Thread t5 = new Thread() {
            @Override
            public void run() {
                System.out.println(selectAll());
            }
        };
        t0.start();
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();


    }

02 测试功能代码

(模拟jvm的本地缓存)

private static List<String> cache = new ArrayList<>();

 private  static List<String> selectAll() {
    if (cache.isEmpty()) {
            System.out.println("======>IMITATION GET DATA FROM DATABASE <=======");
            List<String> cates = Arrays.asList("CateGory-A", "CateGory-B", "CateGory-C");

            cache.addAll(cates);
    }
}

解释: 建立一个变量 如果这个list是空的 从数据库提取 不是空的直接用

第一个判断条件(是否开启本地缓存[通过配置中心拉取的配置数据进行判断],通常不开启,数据不一致)

进行测试: -------------------->

开启使用上面代码的本地缓存遇到的问题:

/*

* 模拟多线程情况下的本地缓存问题

* 1. 线程异常错误 java.util.ConcurrentModificationException

* 2. 查库次数过多 正常只查一次

* 3. 数据list多次拼装, 数据异常

* 正常: [CateGory-A, CateGory-B, CateGory-C]

* 异常: [CateGory-A, CateGory-B, CateGory-C, null, null, null, CateGory-A, CateGory-B, CateGory-C, CateGory-A, CateGory-B, CateGory-C]

*/

异常问题修改

 修改方案一:(不推荐)   

private static List<String> cache = new Vector<>();

// 使用vector 集合 底层采用悲观锁的方式 一个更新其他必须等待 (不建议,性能差)

修改方案二: ( jdk1.5推出,推荐)

private static CopyOnWriteArrayList<String> cache = new CopyOnWriteArrayList<>();

//乐观锁 不会有线程异常了 但是数据依然存在重复(推荐)

乐观锁底层原理 -> CAS算法

自己的理解: -> CAS(比较和交换)cpu硬件算法 内存中的数与原来的数字进行比较 没有变化,将新的值交换给原来的值,不然就取消  这块百度吧, 自己理解的

数据添加错误(重复添加修复)

修改方案一:

private synchronized static List<String> selectAll() {
        if (cache.isEmpty()) {
            System.out.println("======>IMITATION GET DATA FROM DATABASE <=======");
            List<String> cates = Arrays.asList("CateGory-A", "CateGory-B", "CateGory-C");

            cache.addAll(cates);
        }
        //模拟数据库请求数据, 添加到本地缓存中
        return cache;
    }
性能差 悲观锁 一个进来其他阻塞(不推荐) 

修改方案二:

  //方法02 优化 加一个校验
    private  static List<String> selectAll() {

        if (cache.isEmpty()){
            synchronized (cache){//同步代码块
                if (cache.isEmpty()) {
                    System.out.println("======>IMITATION GET DATA FROM DATABASE <=======");
                    List<String> cates = Arrays.asList("CateGory-A", "CateGory-B", "CateGory-C");

                    cache.addAll(cates);
                }
            }
        }
        //模拟数据库请求数据, 添加到本地缓存中
        return cache;
    }

添加一个同步代码块

/**
 * 问: cache.isEmpty()为何要再次校验???
 * 答: 假设abcd 四个值 ab拿到的值都是空,--
 * --> 遇到锁后排队 然后依旧多次查库,所以再次校验
 * 避免多次查库的情况发生
 */

 查看测试结果: 成功!!!  写一个好代码真难

main
======>IMITATION GET DATA FROM DATABASE <=======
[CateGory-A, CateGory-B, CateGory-C]
[CateGory-A, CateGory-B, CateGory-C]
[CateGory-A, CateGory-B, CateGory-C]
[CateGory-A, CateGory-B, CateGory-C]
[CateGory-A, CateGory-B, CateGory-C]
[CateGory-A, CateGory-B, CateGory-C]

 微服务  使用场景

@RefreshScope下  这个注解
@Value("${useLocalCache:false}")
private boolean useLocalCache;

 当并发量大的时候选择开启  不需要的时候关闭

 注: 微服务其他细节 这里不做说明 , 这里为了突出jvm本地缓存

redis分布式缓存这里不做说明

[难点:  多线程并发的安全问题]