CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛

教你们一招(这都可以?)每天学一招,一招实现SpringBoot服务缓存性能翻倍,

1.学一招用一招

前言    绝大多数系统都是读多写少的,众所周知,内存的访问速度很快,是磁盘访问速度的数十倍,如果不使用缓存,都通过数据库访问硬盘,对于双十一这样大的交易量是不可想象的有人专门写了一篇《让 CPU 告诉你硬盘和网络到底有多慢》。

2.学会了一招

https://zhuanlan.zhihu.com/p/24726196,将磁盘、内存、网络对数据的处理速度站在人类的角度来感知表述从内存中读取 1MB 的连续数据,耗时大约为 250us,换算成人类时间是 7.5天。

3.我来教你一招

从 SSD 读取 1MB 的顺序数据,大约需要 1ms,换算成人类时间是 1个月由此可见,内存和硬盘的差距,相当于拖拉机和法拉利的差距为什么要用Caffeine    在Java本地缓存神器—Caffeine(一)。

4.一天学会一招,十天学会一套的意思是什么?

里面已经介绍了caffeine和其他缓存框架的性能对比,结果显示,caffeine的性能远远强于其他缓存框架 如果想要了解关于caffeine更多的知识,可以看Java本地缓存神器—Caffeine(一)。

5.一招又一招

Java本地缓存神器—Caffeine(二) 这两篇文章SpringBoot整合caffeine    SpringBoot默认使用的是SimpleCacheConfiguration,即使用ConcurrentMapCacheManager来实现缓存,ConcurrentMapCache实质是一个ConcurrentHashMap集合对象java内置,如果需要使用caffeine,那么需要引用caffeine的依赖。

6.又学会一招

1. 配置caffeine的maven依赖org.springframework.bootspring-boot-starter-cache

7.你教我一招

com.github.ben-manes.caffeinecaffeine

8.来点教你一招

2.在SpringBoot中开启缓存@SpringBootApplication//开启缓存@EnableCaching public class DemoApplication {     

9.又学了一招

publicstaticvoidmain(String[] args) {         SpringApplication.run(DemoApplication.class, args);     } }

3. 通过bean装配caffeine@Configurationpublicclass CaffeineConfig {     @Autowired     BookDao bookDao;     

@Autowired     CacheLoader cacheLoader;     @Bean@Primarypublic CacheManager caffeineCache() {         CaffeineCacheManager cacheManager = 

new CaffeineCacheManager();         Caffeine caffeine = Caffeine.newBuilder()                 //cache的初始容量值

                .initialCapacity(10)                 //maximumSize用来控制cache的最大缓存数量,maximumSize和maximumWeight不可以同时使用,

                .maximumSize(100)                 .expireAfterAccess(5, TimeUnit.SECONDS)                 .removalListener((Integer key, 

Object value, RemovalCause cause) ->                         System.out.printf(“时间:%d,Key:%d,value:%s,移除原因(%s)%n”

,System.currentTimeMillis()/1000, key,value.toString(),cause)                 )                 //使用refreshAfterWrite必须要设置cacheLoader

                .refreshAfterWrite(5, TimeUnit.SECONDS);         cacheManager.setCaffeine(caffeine);         

//缓存加载策略,当key不存在或者key过期之类的都可以通过CacheLoader来重新获得数据         cacheManager.setCacheLoader(cacheLoader);         

return cacheManager;     }     @Beanpublic CacheLoader cacheLoader() {         CacheLoader<

Object, Object> cacheLoader = new CacheLoader() {             @OverridepublicObject load(

Object key) throws Exception {                 System.out.println(“重新从数据库加载数据:” + key);                 

return bookDao.getBookById((int)key);             }             // 达refreshAfterWrite所指定的时候会触发这个事件方法

@OverridepublicObject reload(Object key, Object oldValue) throws Exception {                 //可以在这里处理重新加载策略,本例子,没有处理重新加载,只是返回旧值。

return oldValue;             }         };         return cacheLoader;     } }4. 定义实体对象@Getter@Setter@ToString

publicclassBookBeanimplementsSerializable{     privatestaticfinallong serialVersionUID = -6585766340444705937L

;     privateint bookId;     private String bookName;     private String author;     privatefloat price; }

5. 定义访问数据库类@Component publicclassBookDao {     /**      * 模拟数据库      */privatestatic Map bookMap = 

new HashMap<>();     @PostConstruct     privatevoidinitDB() {         BookBean xiyou = new BookBean();         xiyou.setBookId(

1);         xiyou.setBookName(“西游记”);         xiyou.setAuthor(“吴承恩”);         xiyou.setPrice(55.5f);         bookMap.put(

1, xiyou);         BookBean honglou = new BookBean();         honglou.setBookId(2);         honglou.setBookName(

“红楼梦”);         honglou.setAuthor(“曹雪芹”);         honglou.setPrice(66.6f);         bookMap.put(2, honglou);     }     

public BookBean getBookById(int bookId) {         return bookMap.get(bookId);     }     public BookBean 

update(BookBean bookBean) {         bookMap.put(bookBean.getBookId(), bookBean);         return bookMap.

get(bookBean.getBookId());     }     public BookBean save(BookBean bookBean) {         bookMap.put(bookBean.getBookId(), bookBean);         

return bookMap.get(bookBean.getBookId());     }     publicvoiddelete(int bookId) {         bookMap.remove

(bookId);     } }6. 定义服务接口publicinterfaceBookService {     public BookBean getBookById(int bookId);     

public BookBean updata(BookBean bookBean);     public BookBean save(BookBean bookBean);     public String 

delete(int bookId); }7. 定义服务实现类@Service//可以在此处统一指定cacheNames的名称@CacheConfig(cacheNames = {“book”})public

classBookServiceImplimplementsBookService{     @Autowired     BookDao bookDao;     /**      * 如果缓存存在,直接读取缓存值;如果缓存不存在,则调用目标方法,并将结果放入缓存      * value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名      * key:缓存对象存储在Map集合中的key值,非必需,默认按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:

@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值      *      * @param bookId      * @return      */@Cacheable(cacheNames = {

“book”}, key = “#bookId”)//    @Cacheable(value = “book” ,key = “targetClass + methodName +#p0”)@Override

public BookBean getBookById(int bookId) {         System.out.println(“查询数据库”);         return bookDao.getBookById(bookId);     }     

@CachePut(cacheNames = {“book”}, key = “#bookBean.bookId”)@Overridepublic BookBean updata(BookBean bookBean) {         System.

out.println(“更新数据库:” + bookBean.toString());         return bookDao.update(bookBean);     }     @CachePut(cacheNames = {

“book”}, key = “#bookBean.bookId”)//写入缓存,key为user.id,一般该注解标注在新增方法上@Overridepublic BookBean save(BookBean bookBean) {         System.

out.println(“保存至数据库:” + bookBean.toString());         return bookDao.save(bookBean);     }     /**      * CacheEvict 用来从缓存中移除相应数据      *  allEntries=true:方法调用后清空所有cacheName为book的缓存      *  beforeInvocation=true:方法调用前清空所有缓存      *      * 

@param bookId      * @return      */@CacheEvict(cacheNames = {“book”})@Overridepublic String delete(int bookId) {         bookDao.delete(bookId);         

return”成功”;     } }8. 定义controller@RestController@RequestMapping(“book”) public class BookController {     

@Autowired     BookService bookService;     @GetMapping(“get/{bookId}”)     @ResponseBody     public BookBean getBook(

@PathVariable(“bookId”) int bookId) {         System.out.println(“时间:”+System.currentTimeMillis()/1000

+”,查询book接口被调用”);         returnbookService.getBookById(bookId);     }     @PostMapping(“updata”)     @

ResponseBodypublicBookBeanupdata(@RequestBody BookBean bookBean) {         System.out.println(“时间:”+System.currentTimeMillis()/

1000+”,更新book接口被调用”);         returnbookService.updata(bookBean);     }     @PostMapping(“save”)     @

ResponseBodypublicBookBeansave(@RequestBody BookBean bookBean) {         System.out.println(“时间:”+System.currentTimeMillis()/

1000+”,保存book接口被调用”);         returnbookService.save(bookBean);     }     @PostMapping(“delete/{bookId}”

)     @ResponseBodypublicStringdelete(@PathVariable(“bookId”) int bookId) {         System.out.println

(“时间:”+System.currentTimeMillis()/1000+”,删除book接口被调用”);         returnbookService.delete(bookId);     } }

9. 测试1.查询测试(1) 连续两次访问 http://localhost:8080/book/get/2

可以看到第二次访问的时候,是直接从缓存中查询的数据,并没有从数据库重新加载(2) 超过五秒之后再访问 http://localhost:8080/book/get/2

我们能看到会重新从数据库加载数据,并且会caffeine访问时间的回收策略(3) 测试修改对缓存的影响先连续两次访问 http://localhost:8080/book/get/2五秒之内再 调用http post方法访问 http://localhost:8080/book/updata,修改《红楼梦》价格为99.9 实体数据为:{ “bookId”: 2, “bookName”: “红楼梦”, “author”: “曹雪芹”, “price”:99.9 }

再访问http://localhost:8080/book/get/2

我们可以看到,第一次调接口查询,因为触发了回收策略,会从数据库重新加载数据;第二次调接口查询时,会从缓存中获取数据;第三次调接口修改,会更新数据库,并且更新缓存;第四次调接口查询,会直接从缓存中获取数据。

源码https://github.com/coding-hao/spring-learn/tree/main/spring_cache欢迎大家关注微信公众号:CodingTao

© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
相关推荐
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容