一、环境springBoot:
1)导入依赖:
<dependency> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
依赖
配置文件 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:");
}
}