En lisant à plusieurs reprises les mêmes données, la question de l'optimisation se pose, les données ne changent pas ou changent rarement, ce sont divers ouvrages de référence et d'autres informations, c'est-à-dire la fonction d'obtention de données par clé est déterministe. Ici, tout le monde comprend probablement - nous avons besoin d'un cache! Pourquoi avez-vous besoin d'effectuer une recherche ou un calcul de données à chaque fois?
Donc, ici, je vais montrer comment créer un cache dans Java Spring, et comme il est étroitement lié à la base de données, alors comment le faire dans le SGBD en utilisant un exemple spécifique.
Table des matières
- Cache au printemps
- Cache dans les fonctions Oracle PL-SQL
Cache au printemps
Ensuite, tout se passe à peu près de la même manière, en Java, ils utilisent divers HasMap, ConcurrentMap, etc. Au printemps aussi, il existe une solution pour cela, simple, pratique, efficace. Je pense que dans la plupart des cas, cela aidera à résoudre le problème. Et donc, tout ce qui est nécessaire est d'activer le cache et d'annoter la fonction.
Rendre le cache accessible
@SpringBootApplication @EnableCaching public class DemoCacheAbleApplication { public static void main(String[] args) { SpringApplication.run(DemoCacheAbleApplication.class, args); } }
Données de recherche de la fonction de cache
@Cacheable(cacheNames="person") public Person findCacheByName(String name) {
L'annotation indique le nom du cache et il existe d'autres paramètres. Il fonctionne comme prévu, la première fois que le code est exécuté, le résultat de la recherche est placé dans le cache par clé (dans ce cas, nom) et les appels suivants ne sont plus exécutés et les données sont récupérées du cache.
Un exemple d'implémentation du référentiel «Person» à l'aide d'un cache
@Component public class PersonRepository { private static final Logger logger = LoggerFactory.getLogger(PersonRepository.class); private List<Person> persons = new ArrayList<>(); public void initPersons(List<Person> persons) { this.persons.addAll(persons); } private Person findByName(String name) { Person person = persons.stream() .filter(p -> p.getName().equals(name)) .findFirst() .orElse(null); return person; } @Cacheable(cacheNames="person") public Person findCacheByName(String name) { logger.info("find person ... " + name); final Person person = findByName(name); return person; } }
Je vérifie ce qui s'est passé
@RunWith(SpringRunner.class) @SpringBootTest public class DemoCacheAbleApplicationTests { private static final Logger logger = LoggerFactory.getLogger(DemoCacheAbleApplicationTests.class); @Autowired private PersonRepository personRepository; @Before public void before() { personRepository.initPersons(Arrays.asList(new Person("", 22), new Person("", 34), new Person("", 41))); } private Person findCacheByName(String name) { logger.info("begin find " + name); final Person person = personRepository.findCacheByName(name); logger.info("find result = " + person.toString()); return person; } @Test public void findByName() { findCacheByName(""); findCacheByName(""); } }
Dans le test, j'appelle deux fois
@Test public void findByName() { findCacheByName(""); findCacheByName(""); }
, la première fois un appel, une recherche, la deuxième fois que le résultat est extrait du cache. Ceci est visible dans la console.

De manière pratique, vous pouvez optimiser la fonctionnalité existante de manière ponctuelle. Si la fonction possède plusieurs arguments, vous pouvez spécifier le nom du paramètre à utiliser comme clé.
@Cacheable(cacheNames="person", key="#name") public Person findByKeyField(String name, Integer age) {
Il existe des schémas plus complexes pour obtenir une clé, c'est dans la documentation.
Mais bien sûr, la question sera de savoir comment mettre à jour les données dans le cache? Il y a deux annotations à cet effet.
Le premier est
@CachePut
Une fonction avec cette annotation appellera toujours le code et mettra le résultat dans le cache, afin de pouvoir mettre à jour le cache.
Je vais ajouter deux méthodes au référentiel: supprimer et ajouter une personne
public boolean delete(String name) { final Person person = findByName(name); return persons.remove(person); } public boolean add(Person person) { return persons.add(person); }
Je vais faire une recherche de personne, supprimer, ajouter, rechercher à nouveau, mais comme auparavant, j'obtiendrai le même visage du cache jusqu'à ce que j'appelle "findByNameAndPut"
@CachePut(cacheNames="person") public Person findByNameAndPut(String name) { logger.info("findByName and put person ... " + name); final Person person = findByName(name); logger.info("put in cache person " + person); return person; }
Test
@Test public void findCacheByNameAndPut() { Person person = findCacheByName(""); logger.info("delete " + person); personRepository.delete(""); findCacheByName(""); logger.info("add new person"); person = new Person("", 35); personRepository.add(person); findCacheByName(""); logger.info("put new"); personRepository.findByNameAndPut(""); findCacheByName(""); }

Une autre annotation est
@CacheEvict
Vous permet non seulement de visiter le stockage du cache, mais également d'expulser. Ce processus est utile pour supprimer des données obsolètes ou inutilisées du cache.
Par défaut, Spring utilise - ConcurrentMapCache pour le cache, si vous avez votre propre excellente classe pour organiser le cache, il est possible de le spécifier dans le CacheManager
@SpringBootApplication @EnableCaching public class DemoCacheAbleApplication { public static void main(String[] args) { SpringApplication.run(DemoCacheAbleApplication.class, args); } @Bean public CacheManager cacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(Arrays.asList( new ConcurrentMapCache("person"), new ConcurrentMapCache("addresses"))); return cacheManager; } }
Les noms des caches y sont également indiqués, il peut y en avoir plusieurs. Dans la configuration xml, cela est indiqué comme ceci:
Spring configuration.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <cache:annotation-driven/> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" name="person"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" name="addresses"/> </set> </property> </bean> </beans>
Classe de personne public class Person { private String name; private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public Integer getAge() { return age; } @Override public String toString() { return name + ":" + age; }
Structure du projet

Ici pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demoCacheAble</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>DemoCacheAble</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Cache dans les fonctions Oracle PL-SQL
Eh bien, au final, ceux qui ne négligent pas la puissance du SGBD, mais l'utilisent, peuvent utiliser la mise en cache au niveau de la base de données, en complément ou en alternative. Par exemple, dans Oracle, vous pouvez tout aussi élégamment transformer une fonction régulière en fonction avec la mise en cache du résultat en y ajoutant
RESULT_CACHE
Un exemple:
CREATE OR REPLACE FUNCTION GET_COUNTRY_NAME(P_CODE IN VARCHAR2) RETURN VARCHAR2 RESULT_CACHE IS CODE_RESULT VARCHAR2(50); BEGIN SELECT COUNTRY_NAME INTO CODE_RESULT FROM COUNTRIES WHERE COUNTRY_ID = P_CODE;
Après avoir modifié les données du tableau, le cache sera reconstruit, vous pouvez affiner la règle de cache à l'aide de
RELIES_ON (...)
Matériaux
Abstraction du cache