redis——岁月云实战

        单线程序,基于IO多路复用,基于内存和c语言编写,性能高。redis官方命令

1 数据结构

1.1 key的层级

        redis的key可以通过冒号(:)来划分层级,如下图mms:company:order,但系统中可以看到有不少没有的,这些都是不符合开发规则的,研发工程师偷懒。这些事情应该严格要求,否则留下来的人就会很麻烦,根本无法猜到这个key是从哪里来的。

 1.2 数据结构

动画解析Redis为什么用跳表而不是红黑树?我看有些面试官就是很无趣,就好像高考阅读的出题人,总以为比写文章的人还理解,人家作者就是因为跳表比红黑树容易实现,性能也不差就用了,搞一堆题目为难面试者,搞得他们也能写出一个redis一样。

2 场景 

2.1 分布式锁

        先看一下面的代码,下面虽然根据year爬虫,但是再多并发测试,一定会出问题。

  @PostMapping("add")
    public ResponseResult add(String str){
        log.info("接收参数:{}",str);
         List<ZpBaziInfo> records = JsonUtil.parseArray(str,ZpBaziInfo.class);
        zpBaziInfoService.batchAdd(records);
        return new ResponseResult(true,"添加成功");
    } 

@Override
    @Async("zpExecutor")
    public void batchAdd(List<ZpBaziInfo> records) {
        for(ZpBaziInfo record : records){
            add(record);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(ZpBaziInfo record) {
        if (!isExist(record.getYear())){
            log.info("开始新增数据,线程:{},时间:{},年:{}",Thread.currentThread().getName(),System.currentTimeMillis(),record.getYear());
            record.setId(RandomUtil.randomString(11));
            save(record);
        }
    }

        创建10个线程随机获取数据,然后调用上面的代码,于是在没有锁的情况下,重复数据产生。

    @Test
    public void test_add_thread_1() throws InterruptedException {
        ZpBaziInfoTest main = new ZpBaziInfoTest();
        ExecutorService executor = Executors.newFixedThreadPool(10);
        IntStream.range(0, 10)
                .forEach(i -> executor.submit(main::addData));
        // 关闭线程池,表示不再接受新的任务
        executor.shutdown();
        //
         executor.awaitTermination(10, TimeUnit.HOURS); // 等待所有线程完成,最长等待1小时
    }

    private String getData(){
        String[] years = {"甲子","乙丑","丙寅","丁卯","戊辰","己巳","庚午","辛未","壬申","癸酉","甲戌","乙亥","丙子","丁丑","戊寅","己卯","庚辰","辛巳","壬午","癸未","甲申","乙酉","丙戌","丁亥","戊子","己丑","庚寅","辛卯","壬辰","癸巳","甲午","乙未","丙申","丁酉","戊戌","己亥","庚子","辛丑","壬寅","癸卯","甲辰","乙巳","丙午","丁未","戊申","己酉","庚戌","辛亥","壬子","癸丑","甲寅"};

        List<ZpBaziInfo> records = IntStream.range(0,20)
                .mapToObj(i->{
                    ZpBaziInfo record = new ZpBaziInfo();
                    record.setYear(years[new Random().nextInt(years.length)]);
                    record.setMonth("甲子");
                    return record;
                }).collect(Collectors.toList());
        return JsonUtil.toJson(records);
    }

    @Test
    public void test_add(){
        addData();
    }

    private void addData(){
        Map<String,Object> params = new HashMap<>();
        params.put("str",getData());
        String result = HttpUtil.post(url,params);
        //
        System.out.println(Thread.currentThread().getName() + ": " + result);
    }

         于是相当给代码加锁,问题暂时解决,单因为ReentrantLock 是单进程的,在集群环境下这个重复问题依旧会出现,于是就用到了redis的分布式锁。

private final ReentrantLock lock = new ReentrantLock();
    
@Override
    @Async("zpExecutor")
    public void batchAdd(List<ZpBaziInfo> records) {
        for(ZpBaziInfo record : records){
            lock.lock();
            try {
                add(record);
            } finally {
                lock.unlock();
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

warrah

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值