《代码实例》利用Redis实现分布式锁&缓存击穿问题

测试类
Springsecurityday01ApplicationTests.java

package com.x.springsecurityday01;

import com.alibaba.fastjson.JSON;
import com.x.springsecurityday01.dao.MenuDao;
import com.x.springsecurityday01.dao.UserDao;
import com.x.springsecurityday01.domain.Menus;
import com.x.springsecurityday01.domain.Users;
import com.x.springsecurityday01.domain.vo.MenusTreeVo;
import com.x.springsecurityday01.util.Count;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@SpringBootTest
class Springsecurityday01ApplicationTests {


   @Autowired(required = false)
   private RedisTemplate<String,String> redisTemplate;
   @Autowired
   private Count counter;






    @Test
    void testLock() throws InterruptedException {

        /**
         * 创建一个子线程
         */
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i< 5000;i++){
                counter.sub();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i =0; i< 5000;i++){
                counter.sub();
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println(counter.count);

    }

    /**
     * 怎么用redis实现分布锁
     * 1.利用redis单线程特性
     * 2:setIfAbsent(,):
     *      作用1:可以放一个key,value到redis;
     *      作用2:会根据key查redis数据库有没有,没有返回true,有返回false
     *
     */
    @Test
    void testRedis2(){


    }

    /**
     * 操作redis list数据类型
     */
    @Test
    void testRedisList(){

        ListOperations<String, String> list = redisTemplate.opsForList();

//        list.leftPush("list","hello1");
//        list.leftPush("list","hello2");
//        list.leftPush("list","hello3");
//        String list1 = list.leftPop("list");//消费
//        System.out.println(list1);

        List<String> list2 = list.range("list", 0, 2);

        for(String s:list2){
            System.out.println(s);
        }

    }

    /**
     * 操作redis set数据类型
     */
    @Test
    void testRedisSet(){
        SetOperations<String, String> set = redisTemplate.opsForSet();
        set.add("set","hello1");
        set.add("set","hello2");
        set.add("set","hello3");

        Set<String> set1 = set.members("set");
        for (String s : set1) {
            System.out.println(s);
        }
    }

}

Count.java

package com.x.springsecurityday01.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class Count {


    //第一种写法把当前线程id 传进入
    //第二种写法 uuid
    /**
     * 取钱的功能
     */
    public int count = 10000;
    @Autowired
    private StringRedisTemplate redisTemplate;

    public void sub(){
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "123",5, TimeUnit.SECONDS);

        try {
            if(lock){
                if(count > 0){
                    this.count --;
                    //1:先查count 原来值
                    //2:减一
                    //3:赋值
                }
            }else {
                sub();//自旋锁
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            redisTemplate.delete("lock");
        }

    }
}

UserSErviceImpl.java

package com.woniu.springsecurityday01.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.woniu.springsecurityday01.dao.MenuDao;
import com.woniu.springsecurityday01.dao.UserDao;
import com.woniu.springsecurityday01.domain.Menus;
import com.woniu.springsecurityday01.domain.Users;
import com.woniu.springsecurityday01.domain.vo.MenusTreeVo;
import com.woniu.springsecurityday01.service.UserService;
import com.woniu.springsecurityday01.util.RequestParams;
import com.woniu.springsecurityday01.util.ResponseResult;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserSErviceImpl implements UserService {

    @Autowired
    private UserDao userDao;
    @Autowired
    private MenuDao menuDao;
    @Autowired
    private StringRedisTemplate redisTemplate;

 
   


    /**
     * 查询用户菜单
     * 缓存一致性问题?出现缓存不一致的原因:因为对原始数据做变更了(增删改)
     * 解决方案;
     * 1:数据库改了,缓存也改(不采用修改缓存的方案)
     * 2:对原始数据(增删改)操作,删除缓存
     *
     * @param account
     * @return
     */
    @Override
    public ResponseResult<?> queryMenusByUser(String account) {

        String userStr = redisTemplate.opsForValue().get("menu:" + account);
        //redis是否有缓存数据
        if(userStr!=null||"".equals(userStr)){
            List<Menus> menus = JSON.parseArray(userStr, Menus.class);
            List<Menus> menuNode = getMenuNode(menus);
            return new ResponseResult<>().ok(menuNode);
        }else {
            //缓存没有,查数据,并且把数据库数据缓存到redis
            Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent("lock", "lock", 5, TimeUnit.SECONDS);
            try {//解决缓存击穿
                if(aBoolean){
                    //拿到锁
                    List<Menus> menus = menuDao.queryMenusByAccount(account);
                    //解决缓存穿透
                    if(menus==null){
                        redisTemplate.opsForValue().set("menu"+account,"");
                    }else {
                        redisTemplate.opsForValue().set("menu"+account,JSON.toJSONString(menus));
                    }
                    List<Menus> menuNode = getMenuNode(menus);
                    return new ResponseResult<>().ok(menuNode);
                }else {
                    return new ResponseResult<>().ok("系统繁忙");
                }
            } catch (Exception e) {
                e.printStackTrace();
                return new ResponseResult<>().fail();
            }finally {
                redisTemplate.delete("lock");
            }

        }

    }
//    @Override备份
//    public ResponseResult<?> queryMenusByUser(String account) {
//
//        String userStr = redisTemplate.opsForValue().get("menu:" + account);
//        //redis是否有缓存数据
//        if(userStr!=null||"".equals(userStr)){
//            List<Menus> menus = JSON.parseArray(userStr, Menus.class);
//            List<Menus> menuNode = getMenuNode(menus);
//            return new ResponseResult<>().ok(menuNode);
//        }else {
//            //缓存没有,查数据,并且把数据库数据缓存到redis
//            List<Menus> menus = menuDao.queryMenusByAccount(account);
//            //解决缓存穿透
//            if(menus==null){
//                redisTemplate.opsForValue().set("menu"+account,"");
//            }else {
//                redisTemplate.opsForValue().set("menu"+account,JSON.toJSONString(menus));
//            }
//            List<Menus> menuNode = getMenuNode(menus);
//            return new ResponseResult<>().ok(menuNode);
//        }
//
//
//
//    }

    /**
     * 把菜单封装成树形结构
     * @param menus
     * @return
     */
    public List<Menus> getMenuNode(List<Menus> menus ){

        //找到所有的一级菜单
        List<Menus> parentMenus = menus.stream().filter(e -> e.getParentId() == null)
                .collect(Collectors.toList());

        for(Menus parentMenu :parentMenus){
            List<Menus> childrenMenus=new ArrayList<>();
            for(Menus menu:menus){
                //找出一级菜单对应二级菜单
                if(parentMenu.getId().equals(menu.getParentId())){
                    //
                    childrenMenus.add(menu);
                }
            }
            parentMenu.setChildrenMenu(childrenMenus);
        }
        return parentMenus;
    }


  
  
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值