一、環境springBoot:
1)導入依賴:

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

spring: redis: host: 192.168.2.147 port: 6379 password: java1902 jedis: pool: max-active: 100
二、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:"); } }