redis

引入redis

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

配置文件

spring:
  redis:
    host: 192.168.56.10
    port: 6379

测试

    @Autowired
    StringRedisTemplate redisTemplate;

    @Test
    public void testRedisTemplate(){
       //存储hello world
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        //保存
        ops.set("hello","world"+ UUID.randomUUID());
        //查询
        String hello = ops.get("hello");
        System.out.println(hello);
    }

bug

    //todo 并发产生堆外内存溢出
    //原因springboot2.0以后redia默认使用lettuce作为redis客户端,使用netty进行网络通信
    //lettuce的bug导致netty内存溢出,netty如果没有配置,默认使用xmx的值作为堆外内存
    //可以通过-Dio.netty.maxDirectMemory进行设置
    //解决方案:不能使用-Dio.netty.maxDirectMemory只去调大堆外内存
    //1、升级lettuce客户端  2、切换使用jedis
    //lettuce、jedis直接操作redis底层,redistemplate为再次封装
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>        <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <exclusions>
            <exclusion>
                <groupId>io.lettuce</groupId>
                <artifactId>lettuce-core</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

存在的问题



加锁–synchronized (this)(分布式下不可用

public Map<String, List<Catelog2Vo>> getCatelogJsonPlusFromDB() {
//加锁,只要是同一把锁,就能锁住需要这个锁的所有线程
//springboot所有的组件在容器中默认单例,使用this可以锁住
//synchronized (this)分布式下不可用
synchronized (this){
//得到锁之后,先去看redis中有无再操作
String catelogJSON = redisTemplate.opsForValue().get("catelogJSON");
if (!StringUtils.isEmpty(catelogJSON)){
Map<String, List<Catelog2Vo>> result=JSON.parseObject(catelogJSON,new TypeReference<Map<String, List<Catelog2Vo>>>(){});
return result;
}
//要返回的格式
Map<String, List<Catelog2Vo>> mapl1 = new HashMap<>(16);
//查出所有数据
List<CategoryEntity> catelogOrgin = baseMapper.selectList(new LambdaQueryWrapper<CategoryEntity>().orderByDesc(CategoryEntity::getCatLevel));
//获得最深层
Integer catLevel = catelogOrgin.get(0).getCatLevel();
if (!catelogOrgin.isEmpty()) {
Map<String, List<Catelog2Vo.Catelog3Vo>> mapl2 = new HashMap<>(16);
catelogOrgin.forEach(item -> {
if (item.getCatLevel().equals(catLevel)) {
//先构造二级数据,k,v==item.pid,item
//先判断k存在
Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(item.getParentCid().toString(), item.getCatId().toString(), item.getName());
if (mapl2.containsKey(item.getParentCid().toString())) {
//存在就放数据
List<Catelog2Vo.Catelog3Vo> catelog3Vos = mapl2.get(item.getParentCid().toString());
catelog3Vos.add(catelog3Vo);
} else {
List<Catelog2Vo.Catelog3Vo> catelog3Vos = new ArrayList<>(11);
catelog3Vos.add(catelog3Vo);
mapl2.put(item.getParentCid().toString(), catelog3Vos);
}

} else if (item.getCatLevel().equals(catLevel - 1)) {
//判断一级map是否存在这个k
//获得此分类下的所有三级
List<Catelog2Vo.Catelog3Vo> catelog3Vos = mapl2.get(item.getCatId().toString());
Catelog2Vo catelog2Vo = new Catelog2Vo(item.getParentCid().toString(), catelog3Vos, item.getCatId().toString(), item.getName());
if (mapl1.containsKey(item.getParentCid().toString())) {
List<Catelog2Vo> catelog2Vos = mapl1.get(item.getParentCid().toString());
catelog2Vos.add(catelog2Vo);
} else {
List<Catelog2Vo> list=new ArrayList<>(11);
list.add(catelog2Vo);
mapl1.put(item.getParentCid().toString(),list);
}
} else {
//避免一级分类下无数据的不显示,这里也要判断一下
if (!mapl1.containsKey(item.getCatId().toString())) {
// List<Catelog2Vo> list=new ArrayList<>(11);
mapl1.put(item.getCatId().toString(), null);
}
}
});
}

return mapl1;
}
}
  • 加锁时序问题
    解决方法
@Override
public Map<String, List<Catelog2Vo>> getCatelogJsonPlus() {
/** 空结果缓存,解决缓存穿透
*设置过期时间,加随机值,解决缓存雪崩
* 加锁,解决缓存击穿
*/
String catelogJSON = redisTemplate.opsForValue().get("catelogJSON");
if (StringUtils.isEmpty(catelogJSON)) {
Map<String, List<Catelog2Vo>> catelogJsonFromDB = getCatelogJsonPlusFromDB();
//以JSON存储,跨语言跨平台

return catelogJsonFromDB;
}
//redis查到的反序列化成对象
Map<String, List<Catelog2Vo>> result = JSON.parseObject(catelogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
});
return result;
}
    public Map<String, List<Catelog2Vo>> getCatelogJsonPlusFromDB() {
//加锁,只要是同一把锁,就能锁住需要这个锁的所有线程
//springboot所有的组件在容器中默认单例,使用this可以锁住
//synchronized (this)分布式下不可用
synchronized (this) {
//得到锁之后,先去看redis中有无再操作
String catelogJSON = redisTemplate.opsForValue().get("catelogJSON");
if (!StringUtils.isEmpty(catelogJSON)) {
Map<String, List<Catelog2Vo>> result = JSON.parseObject(catelogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
});
return result;
}
log.info("查询了数据库");
//要返回的格式
Map<String, List<Catelog2Vo>> mapl1 = new HashMap<>(16);
//查出所有数据
List<CategoryEntity> catelogOrgin = baseMapper.selectList(new LambdaQueryWrapper<CategoryEntity>().orderByDesc(CategoryEntity::getCatLevel));
//获得最深层
Integer catLevel = catelogOrgin.get(0).getCatLevel();
if (!catelogOrgin.isEmpty()) {
Map<String, List<Catelog2Vo.Catelog3Vo>> mapl2 = new HashMap<>(16);
catelogOrgin.forEach(item -> {
if (item.getCatLevel().equals(catLevel)) {
//先构造二级数据,k,v==item.pid,item
//先判断k存在
Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(item.getParentCid().toString(), item.getCatId().toString(), item.getName());
if (mapl2.containsKey(item.getParentCid().toString())) {
//存在就放数据
List<Catelog2Vo.Catelog3Vo> catelog3Vos = mapl2.get(item.getParentCid().toString());
catelog3Vos.add(catelog3Vo);
} else {
List<Catelog2Vo.Catelog3Vo> catelog3Vos = new ArrayList<>(11);
catelog3Vos.add(catelog3Vo);
mapl2.put(item.getParentCid().toString(), catelog3Vos);
}

} else if (item.getCatLevel().equals(catLevel - 1)) {
//判断一级map是否存在这个k
//获得此分类下的所有三级
List<Catelog2Vo.Catelog3Vo> catelog3Vos = mapl2.get(item.getCatId().toString());
Catelog2Vo catelog2Vo = new Catelog2Vo(item.getParentCid().toString(), catelog3Vos, item.getCatId().toString(), item.getName());
if (mapl1.containsKey(item.getParentCid().toString())) {
List<Catelog2Vo> catelog2Vos = mapl1.get(item.getParentCid().toString());
catelog2Vos.add(catelog2Vo);
} else {
List<Catelog2Vo> list = new ArrayList<>(11);
list.add(catelog2Vo);
mapl1.put(item.getParentCid().toString(), list);
}
} else {
//避免一级分类下无数据的不显示,这里也要判断一下
if (!mapl1.containsKey(item.getCatId().toString())) {
// List<Catelog2Vo> list=new ArrayList<>(11);
mapl1.put(item.getCatId().toString(), null);
}
}
});
}
String jsonString = JSON.toJSONString(mapl1);
redisTemplate.opsForValue().set("catelogJSON", jsonString, 1, TimeUnit.DAYS);
return mapl1;
}
}

分布式锁





redisson

  • 引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
</dependency>
  • 配置
package com.atguigu.gulimall.product.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class MyRedissonConfig {
@Bean(destroyMethod="shutdown")
public RedissonClient redisson() throws IOException {
//1、创建配置
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.56.10:6379");
//2、根据Config创建出Redisson实例
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}
  • 测试
@Autowired
RedissonClient redissonClient;

@Test
public void redisson(){
System.out.println(redissonClient);
}

可重入锁(Reentrant Lock)

@Autowired
RedissonClient redissonClient;
@ResponseBody
@GetMapping("/hello")
public String hello() {
//1、获取一把锁,只要锁的名字一样,就是同一把锁
RLock lock = redissonClient.getLock("my_lock");
//2、加锁,阻塞式等待
lock.lock();//默认加锁30s
//看门狗锁的自动续期,可通过修改Config.lockWatchdogTimeout来指定续期时间,每1/3看门狗时间会重新将锁续满
//加锁的业务只要完成,就不会续期,即使不手动释放锁,也会自动30删除
try {
System.out.println("加锁成功" + Thread.currentThread().getId());
Thread.sleep(30000);
} catch (Exception ignored) {

} finally {
//3、解锁
System.out.println("释放锁成功" + Thread.currentThread().getId());
lock.unlock();
}

return "hello";
}

另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。不会续期

lock.lock(10, TimeUnit.SECONDS);

读写锁

/**
* 读写锁
* 保证一定能读到最新数据,修改时,写锁是排他锁。读锁是共享锁。
* 写+读 阻塞
* 读+写 阻塞
* 写+写 阻塞
* 读+读 相当于无锁,并发读,redis只会记录当前的读锁,会同时加锁成功
* @return
*/
@ResponseBody
@GetMapping("/write")
public String write() {
RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
String s = "";
RLock writeLock = lock.writeLock();
try {
//1、改数据加写锁,读数据加读锁
writeLock.lock();
s = UUID.randomUUID().toString();
Thread.sleep(30000);
stringRedisTemplate.opsForValue().set("write", s);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
writeLock.unlock();
}
return s;
}

@ResponseBody
@GetMapping("/read")
public String read() {
RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
String s = "";
RLock readLock = lock.readLock();
//1、加读锁
readLock.lock();
try {
s = stringRedisTemplate.opsForValue().get("write");
Thread.sleep(30000);
} catch (Exception e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
return s;
}
  • 写-读效果

信号量Semaphore

 /**
* 信号量(Semaphore)
* 车库停车
* 3车位
* k可以做分布式限流
*/
@GetMapping("/park")
@ResponseBody
public String park() throws InterruptedException {
RSemaphore park = redissonClient.getSemaphore("park");
// park.acquire();//获取一个信号,获取一个值,阻塞拿车位,park的值自动减一,若为0则等待
boolean b = park.tryAcquire();//有车位就停,没有就算了
if (b){
//业务
}else {
return "error";
}
return "ok"+b;
}

@GetMapping("/go")
@ResponseBody
public String go() throws InterruptedException {
RSemaphore park = redissonClient.getSemaphore("park");
park.release();//释放一个车位park加1
return "ok";
}

闭锁(CountDownLatch)

/**
* 闭锁(CountDownLatch)
* 学校锁门
* 5个班全部走完,锁门
*/
@GetMapping("/lockDoor")
@ResponseBody
public String lockDoor() throws InterruptedException {
RCountDownLatch door = redissonClient.getCountDownLatch("door");//自动加锁
door.trySetCount(5);
door.await();//等待闭锁都完成
return "放假了";
}

@GetMapping("/gogogo/{id}")
@ResponseBody
public String gogogo(@PathVariable("id") Long id) {
RCountDownLatch door = redissonClient.getCountDownLatch("door");
door.countDown();//减一,减到0自动释放锁
return id+"班的人都走了";
}

一致性




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值