Rumah  >  Artikel  >  Java  >  Bagaimana untuk menggunakan cache dalam projek SpringBoot

Bagaimana untuk menggunakan cache dalam projek SpringBoot

PHPz
PHPzke hadapan
2023-05-16 14:34:13900semak imbas

Kata Pengantar

Caching boleh meningkatkan prestasi dan kestabilan sistem dengan berkesan dengan menyimpan data yang kerap diakses dalam ingatan, mengurangkan tekanan pada sumber data asas seperti pangkalan data. Saya rasa semua orang telah menggunakannya lebih kurang dalam projek mereka, dan projek kami tidak terkecuali Namun, semasa saya menyemak kod syarikat baru-baru ini, tulisannya sangat bodoh dan rendah adalah seperti berikut:

public User getById(String id) {
	User user = cache.getUser();
    if(user != null) {
        return user;
    }
    // 从数据库获取
    user = loadFromDB(id);
    cahce.put(id, user);
	return user;
}

Malah, Spring Boot menyediakan abstraksi caching yang berkuasa yang memudahkan untuk menambah caching pada aplikasi anda. Artikel ini akan membincangkan tentang cara menggunakan anotasi cache berbeza yang disediakan oleh Spring untuk melaksanakan amalan terbaik untuk caching.

Dayakan caching @EnableCaching

Kebanyakan projek sekarang ialah projek SpringBoot Kami boleh menambah anotasi @EnableCaching pada kelas permulaan untuk mendayakan fungsi caching.

@SpringBootApplication
@EnableCaching
public class SpringCacheApp {

    public static void main(String[] args) {
        SpringApplication.run(Cache.class, args);
    }
}

Memandangkan anda mahu boleh menggunakan cache, anda perlu mempunyai pengurus cache Bean Secara lalai, @EnableCaching akan mendaftarkan ConcurrentMapCacheManager Bean, dan tiada pengisytiharan kacang yang berasingan diperlukan. ConcurrentMapCacheManager menyimpan nilai dalam contoh ConcurrentHashMap, yang merupakan pelaksanaan mekanisme cache yang paling mudah untuk thread-safe.

Pengurus cache tersuai

Pengurus cache lalai tidak dapat memenuhi keperluan kerana ia disimpan dalam memori jvm, jadi bagaimana untuk menyimpannya dalam redis? Pada masa ini anda perlu menambah pengurus cache tersuai.

1. Tambah kebergantungan

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

2. Konfigurasikan pengurus cache Redis

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory();
    }

    @Bean
    public CacheManager cacheManager() {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .disableCachingNullValues()
            .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory())
            .cacheDefaults(redisCacheConfiguration)
            .build();

        return redisCacheManager;
    }
}

Sekarang kita mempunyai pengurus cache, bagaimana kita mengendalikan cache pada peringkat perniagaan?

Kita boleh menggunakan @Cacheable, @CachePut atau @CacheEvict anotasi untuk mengendalikan cache.

@Cacheable

Anotasi ini boleh cache hasil pelaksanaan kaedah Apabila kaedah dipanggil semula dalam had masa cache, kaedah itu sendiri tidak akan dipanggil, tetapi hasilnya akan diperolehi terus dari cache dan dikembalikan kepada pemanggil.

Bagaimana untuk menggunakan cache dalam projek SpringBoot

Contoh 1: Mencache hasil pertanyaan pangkalan data.

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    @Cacheable(value = "myCache", key = "#id")
    public MyEntity getEntityById(Long id) {
        return repository.findById(id).orElse(null);
    }
}

Dalam contoh ini, anotasi @Cacheable digunakan untuk cache hasil kaedah getEntityById(), yang mendapatkan semula objek MyEntity daripada pangkalan data berdasarkan IDnya.

Tetapi bagaimana jika kami mengemas kini data? Data lama masih dalam cache?

@CachePut

Kemudian @CachePut keluar Perbezaan daripada anotasi @Cacheable ialah kaedah menggunakan anotasi @CachePut tidak akan menyemak sama ada ia wujud dalam cache sebelum ini. Sebaliknya, kaedah akan dilaksanakan setiap kali dan hasil pelaksanaan akan ditulis ke cache yang ditentukan dalam bentuk pasangan nilai kunci. @CachePut Anotasi biasanya digunakan untuk mengemas kini data cache, yang setara dengan cache menggunakan mod tulis dua kali dalam mod tulis.

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    @CachePut(value = "myCache", key = "#entity.id")
    public void saveEntity(MyEntity entity) {
        repository.save(entity);
    }
}

@CacheEvict

Kaedah yang ditandakan dengan anotasi @CacheEvict akan mengalih keluar data yang disimpan daripada cache apabila ia dipanggil. @CacheEvict Anotasi biasanya digunakan untuk memadam data cache, yang setara dengan cache menggunakan mod kegagalan dalam mod tulis.

Bagaimana untuk menggunakan cache dalam projek SpringBoot

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

     @CacheEvict(value = "myCache", key = "#id")
    public void deleteEntityById(Long id) {
        repository.deleteById(id);
    }
}

@Caching

@Caching anotasi digunakan untuk menentukan berbilang anotasi berkaitan Spring Cache pada kaedah atau kelas pada masa yang sama.

Bagaimana untuk menggunakan cache dalam projek SpringBoot

Contoh 1: Atribut @Caching dalam anotasi evict menentukan dua cache untuk menjadi tidak sah apabila kaedah saveEntity dipanggil.

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    @Cacheable(value = "myCache", key = "#id")
    public MyEntity getEntityById(Long id) {
        return repository.findById(id).orElse(null);
    }

    @Caching(evict = {
        @CacheEvict(value = "myCache", key = "#entity.id"),
        @CacheEvict(value = "otherCache", key = "#entity.id")
    })
    public void saveEntity(MyEntity entity) {
        repository.save(entity);
    }

}

Contoh 2: Apabila memanggil kaedah getEntityById, Spring akan terlebih dahulu menyemak sama ada hasilnya telah dicache dalam cache myCache. Jika ya, Spring akan mengembalikan hasil cache dan bukannya melaksanakan kaedah. Jika hasilnya belum dicache, Spring akan melaksanakan kaedah dan cache hasilnya dalam cache myCache. Selepas kaedah dilaksanakan, Spring akan mengalih keluar hasil cache daripada cache @CacheEvict berdasarkan anotasi otherCache.

@Service
public class MyService {

    @Caching(
        cacheable = {
            @Cacheable(value = "myCache", key = "#id")
        },
        evict = {
            @CacheEvict(value = "otherCache", key = "#id")
        }
    )
    public MyEntity getEntityById(Long id) {
        return repository.findById(id).orElse(null);
    }

}

Contoh 3: Apabila memanggil kaedah saveData, Spring akan terlebih dahulu mengalih keluar data daripada cache @CacheEvict berdasarkan anotasi otherCache. Spring kemudiannya akan melaksanakan kaedah dan menyimpan hasilnya ke pangkalan data atau API luaran. Selepas kaedah

dilaksanakan, Spring akan menambah hasil pada cache @CachePut, myCache dan myOtherCache berdasarkan anotasi myThirdCache. Spring juga akan menyemak sama ada keputusan telah dicache dalam @Cacheable dan myFourthCache cache berdasarkan anotasi myFifthCache. Jika hasilnya belum dicache, Spring akan cache hasilnya dalam cache yang sesuai. Jika hasilnya telah dicache, Spring akan mengembalikan hasil cache dan bukannya melaksanakan kaedah itu semula.

@Service
public class MyService {

    @Caching(
        put = {
            @CachePut(value = "myCache", key = "#result.id"),
            @CachePut(value = "myOtherCache", key = "#result.id"),
            @CachePut(value = "myThirdCache", key = "#result.name")
        },
        evict = {
            @CacheEvict(value = "otherCache", key = "#id")
        },
        cacheable = {
            @Cacheable(value = "myFourthCache", key = "#id"),
            @Cacheable(value = "myFifthCache", key = "#result.id")
        }
    )
    public MyEntity saveData(Long id, String name) {
        // Code to save data to a database or external API
        MyEntity entity = new MyEntity(id, name);
        return entity;
    }

}

@CacheConfig

通过@CacheConfig 注解,我们可以将一些缓存配置简化到类级别的一个地方,这样我们就不必多次声明相关值:

@CacheConfig(cacheNames={"myCache"})
@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    @Cacheable(key = "#id")
    public MyEntity getEntityById(Long id) {
        return repository.findById(id).orElse(null);
    }

    @CachePut(key = "#entity.id")
    public void saveEntity(MyEntity entity) {
        repository.save(entity);
    }

    @CacheEvict(key = "#id")
    public void deleteEntityById(Long id) {
        repository.deleteById(id);
    }
}

Condition & Unless

  • condition作用:指定缓存的条件(满足什么条件才缓存),可用 SpEL 表达式(如 #id>0,表示当入参 id 大于 0 时才缓存)

  • unless作用 : 否定缓存,即满足 unless 指定的条件时,方法的结果不进行缓存,使用 unless 时可以在调用的方法获取到结果之后再进行判断(如 #result == null,表示如果结果为 null 时不缓存)

//when id >10, the @CachePut works. 
@CachePut(key = "#entity.id", condition="#entity.id > 10")
public void saveEntity(MyEntity entity) {
	repository.save(entity);
}


//when result != null, the @CachePut works.
@CachePut(key = "#id", condition="#result == null")
public void saveEntity1(MyEntity entity) {
	repository.save(entity);
}

清理全部缓存

通过allEntriesbeforeInvocation属性可以来清除全部缓存数据,不过allEntries是方法调用后清理,beforeInvocation是方法调用前清理。

//方法调用完成之后,清理所有缓存
@CacheEvict(value="myCache",allEntries=true)
public void delectAll() {
    repository.deleteAll();
}

//方法调用之前,清除所有缓存
@CacheEvict(value="myCache",beforeInvocation=true)
public void delectAll() {
    repository.deleteAll();
}

SpEL表达式

Spring Cache注解中频繁用到SpEL表达式,那么具体如何使用呢?

SpEL 表达式的语法

Bagaimana untuk menggunakan cache dalam projek SpringBoot

Spring Cache可用的变量

Bagaimana untuk menggunakan cache dalam projek SpringBoot

最佳实践

通过Spring缓存注解可以快速优雅地在我们项目中实现缓存的操作,但是在双写模式或者失效模式下,可能会出现缓存数据一致性问题(读取到脏数据),Spring Cache 暂时没办法解决。最后我们再总结下Spring Cache使用的一些最佳实践。

  • 只缓存经常读取的数据:缓存可以显着提高性能,但只缓存经常访问的数据很重要。很少或从不访问的缓存数据会占用宝贵的内存资源,从而导致性能问题。

  • 根据应用程序的特定需求选择合适的缓存提供程序和策略。SpringBoot 支持多种缓存提供程序,包括 EhcacheHazelcast 和 Redis

  • 使用缓存时请注意潜在的线程安全问题。对缓存的并发访问可能会导致数据不一致或不正确,因此选择线程安全的缓存提供程序并在必要时使用适当的同步机制非常重要。

  • 避免过度缓存。缓存对于提高性能很有用,但过多的缓存实际上会消耗宝贵的内存资源,从而损害性能。在缓存频繁使用的数据和允许垃圾收集不常用的数据之间取得平衡很重要。

  • 使用适当的缓存逐出策略。使用缓存时,重要的是定义适当的缓存逐出策略以确保在必要时从缓存中删除旧的或陈旧的数据。

  • 使用适当的缓存键设计。缓存键对于每个数据项都应该是唯一的,并且应该考虑可能影响缓存数据的任何相关参数,例如用户 ID、时间或位置。

  • 常规数据(读多写少、即时性与一致性要求不高的数据)完全可以使用 Spring Cache,至于写模式下缓存数据一致性问题的解决,只要缓存数据有设置过期时间就足够了。

  • 特殊数据(读多写多、即时性与一致性要求非常高的数据),不能使用 Spring Cache,建议考虑特殊的设计(例如使用 Cancal 中间件等)。

Atas ialah kandungan terperinci Bagaimana untuk menggunakan cache dalam projek SpringBoot. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam