Redis——作为sql数据库缓存

一、环境springBoot:

  1)导入依赖:

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

  2)配置文件 application.yml

spring:
  redis:
    host: 192.168.2.147
    port: 6379
    password: java1902
    jedis:
      pool:
        max-active: 100
application.yml

二、redis作为数据库缓存:

  原理:第一次查询使用sql数据库,查询完成后把数据存入redis用于后续查询,直到sql中的数据变化,清空相应的redis缓存,重新获取;

  缓存失效:更新数据库,采用清空redis缓存的方式;

  1)实体类:

public class Product implements Serializable {
    private Integer id;
    private String name;

    public Product() {
    }

    public Product(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
实体类

  2)测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class RedistestSpringData2ApplicationTests {
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void cacheTest() {
        List<Product> products = (List<Product>) redisTemplate.opsForValue().get("product:");
        if (products == null) {
            System.out.println("查询数据库......");
            // 模拟从数据库查询数据
            products = new ArrayList<Product>();
            products.add(new Product(1, "商品1"));
            products.add(new Product(2, "商品2"));

            redisTemplate.opsForValue().set("product:", products);
        } else {
            System.out.println("查询缓存......");
        }
    }

    @Test
    public void delCacheTest() {
        redisTemplate.delete("product:");
    }

}

三、解决缓存穿透问题:

  原因:一个线程在访问数据库信息后还未写入到缓存时丢失了时间片,其它线程访问时重复访问了数据库,造成数据库巨大压力;

  解决:

    1)synchronized来加锁:因为是分布式开发,synchronized的作用域是JVM,则在多个服务间synchronized是无效的;

    2)Redis分布式锁:

      1、在redis本地使用:   

setnx Lock 1 //1
get Lock //1
setnx Lock 2 //00
del Lock
setnx Locks 2 //1

      2、springboot代码实现:

        //try finally解决死锁问题:

@RunWith(SpringRunner.class)
@SpringBootTest
public class RedistestSpringData2ApplicationTests {
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void multiThreadTest() throws InterruptedException {
        ExecutorService pool = new ThreadPoolExecutor(100, 200, 100, TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<>(100));
        for (int i = 0; i < 100; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    cacheTest();
                }
            });
        }
        Thread.sleep(1000000);
    }

    @Test
    public void cacheTest() {
        List<Product> products = (List<Product>) redisTemplate.opsForValue().get("product:");
        if (products == null) {
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("product:lock", 1);
            if (ifAbsent) {
                try {
                    System.out.println("查询数据库......");
                    // 模拟从数据库查询数据
                    products = new ArrayList<Product>();
                    products.add(new Product(1, "商品1"));
                    products.add(new Product(2, "商品2"));
                    int i = 10 / 0;
                    redisTemplate.opsForValue().set("product:", products);
                } finally {
                    redisTemplate.delete("product:lock");
                }
            } else {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                cacheTest();
            }
        } else {
            System.out.println("查询缓存......");
        }
    }

    @Test
    public void delCacheTest() {
        redisTemplate.delete("product:");
    }
}

四、解决缓存击穿问题:

  原因:重复查询为null时,每次都会重新访问数据库;

  解决:查询到为null值时,返回一个空的对象给redis作为缓存,设置一定时间失效;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RedistestSpringData2ApplicationTests {
    @Autowired
    private RedisTemplate redisTemplate;

    public Product productById(Integer id) {
        if (id > 10) {
            return null;
        }
        return new Product();
    }

    @Test
    public void cachePenetrationTest() {
        for (int i = 11; i < 20; i++) {
            Product product = (Product) redisTemplate.opsForValue().get("product:" + i);
            if (product == null) {
                // 模拟从数据库查询数据
                System.out.println("查询数据库......");
                // 如果i大于10,type都是null
                product = productById(i);
                // 如果时null返回一个空
                if (product == null) {
                    product = new Product(i, "");
                    redisTemplate.opsForValue().set("product:" + i, product);
                    redisTemplate.expire("product:" + i, 10, TimeUnit.MINUTES);
                } else {
                    redisTemplate.opsForValue().set("product:" + i, product);
                    redisTemplate.expire("product:" + i, 20, TimeUnit.MINUTES);
                }
            } else {
                System.out.println("查询缓存......");
            }
        }
    }

    @Test
    public void delCacheTest() {
        redisTemplate.delete("product:");
    }
}

猜你喜欢

转载自www.cnblogs.com/Tractors/p/11304252.html