The importance of caching is self-evident. With caching, we can avoid frequent interactions with the database, especially in the case of more queries and higher cache hit ratio, the performance improvement with caching is more obvious
Similarly, Mybatis, as an ORM framework, will definitely support caching
It supports level 1 and level 2 caching, respectively. The level 1 cache is the sqlSession level cache, and the level 2 cache can realize the cache between multiple SQLSessions
What do you mean? Read on
First-level cache
01. What is Level 1 cache
Mybatis level 1 cache is sqlSession level, because it only supports the cache under the same sqlSession, that is, the cache is only shared between the same sqlSession
02. How to implement level 1 cache
Level 1 caches are divided into two categories: Statement and session
Statement: An SQL statement
Session: a connection generated by the database, that is, a SQLsession
Mybatis supports level 1 caching by default, no special configuration is required, and it supports session range level 1 caching.
In view of the cached attributes, mybatis org. Using the class apache. Ibatis. SessionConfiuration configuration, we can see the default level for the SESSION of localCacheScope (and the second level cache is also the default open)
Note \color{#FF0000}{note} Note: The cacheEnabled attribute in the Configuration class is for on-off control of level 2 cache, not level 1 cache. Level 1 cache does not need to be configured at all. It has no switch and is supported by Mybatis by default
So, in other words, I run the service directly, and level 1 caching takes effect?
Let’s try the level 1 cache test
03. Level 1 cache test
1) Call the same method twice in the same method
public void testCache(){
User user = userMapperWithAnnotation.findById(33L);
log.info("Find User: {}", user);
User user2 = userMapperWithAnnotation.findById(33L);
log.info("Find User2: {}", user2);
}
Copy the code
2) Call directly from the run method
@SpringBootApplication
@Slf4j
@MapperScan("com.shumile.springbootmybatis.mapper")
public class SpringBootMybatisApplication
implements ApplicationRunner {
@Autowired
private UserMapperWithAnnotation userMapperWithAnnotation;
public static void main(String[] args) {
SpringApplication.run(SpringBootMybatisApplication.class, args);
}
@Override
public void run(ApplicationArguments args) throws Exception {
testCache();
}
Copy the code
Since mybatis already supports level 1 caching by default, I’m sure I only need to query the database once and then fetch the result directly from the cache the second time
3) Run the code, as shown below
Huh? What’s going on? A lie? Print two SQL statements, still query twice?
To be honest, this question has puzzled me for several hours, I thought, is there a problem with what the Internet says? Is there any special configuration for level 1 caching?
Finally, through the source tracking, finally suddenly enlightened ~
04. Source code analysis
We trace the execution of the findById() method as follows
1) The selectOne() method of DefaultSqlSession is first entered
2) Next, the selectList() method is entered
We know that the Confifuration class already supports level 1 caching by default. When executing a query, we get the corresponding configuration object that supports session caching by default (again, regardless of the cacheEnabled attribute).
So, there’s no problem here
3) Go down to the Query () method of CachingExecutor
After obtaining the corresponding executable SQL statement based on the parameters, the createCacheKey() method creates the cache key and passes the obtained key as a parameter to the following query() method
Here, we pause and go inside the createCacheKey() method to see how this Key is determined
4) Explore the implementation of createCacheKey()
It places statement. id, Offset, Limmit, Sql, and Params into the updateList in the cacheKey by executing the cacheKey.update() method, respectively. The update() method is as follows
Look again at the cachekey.equals () method
Obviously, except for hashcode, checksum, and count comparisons, we can consider CacheKey equality as long as the updatelist elements are equal. Id, Offset, Limmit, Sql, and Params are the same as each other
Look at the key assembly process, then continue to go ~
5) Find the key in the cache
In the query() method, the key value is first fetched from the cache. If the list is not in the cache, the obtained list is empty, and the database needs to be queried
6) Execute specific database query methods
7) Store the results in cache
After execution, the result of the current execution is placed in the localCache via localcache.putobject (key,list)
8) Submit results
Once this is done, the commit() method is entered
We see that the local cache is cleared when the commit() method is executed. In the future, the cache always cannot find the corresponding key value, and the SQL statement will be executed again every time to query the database
So it’s easy to see why level 1 caching is not supported
Commit () {color{#FF0000}{commit() {color{#FF0000}{commit() {color{#FF0000}{commit() {color{#FF0000}{ Our methods must be in the same transaction to support level 1 caching
Let’s verify that the transaction is started
1) Start the transaction
Next, we have method to open the transaction, in starting the class add @ EnableTransactionManagement annotations, and on the run () method to add @ Transactional annotation
Then it’s time for validation
2) Verification
The execution process of the first query is basically the same as the previous one. Again, the results are retrieved from the database and stored in the cache
Second Query
Notice, the key is the second query
We will continue to judge the value of the corresponding key from the cache. This time we can get the value of the key, namely its query result, and directly return the result set
Until the two statements have been executed, the commit() method is entered for the transaction commit operation
Take a look at the execution result graph
If the SQL statement is executed only once, the verification is successful ~
Why does level-1 cache not take effect when transactions are not enabled? Enabled transaction, level 1 cache takes effect?
Second level cache
01. What is level 2 cache
In level 1 cache, a sqlSession uses a cache, while Mybatis level 2 cache supports multiple SQLsessions sharing cache. Mybatis enables level 2 caching by default
02. How to level 2 cache
From the source analysis of level 1 caching in section 1, we also mentioned that level 2 caching is enabled by default in the Configuration class (cacheEnabled=true).
In addition to this parameter, you need to add a cache or cache-ref tag to the mapper. XML file for cache configuration
As shown below.
The cache tag is used to declare that a namespace uses level 2 caching and can be customized using the following attributes
-
Type: Indicates the type of the cache. The default value is PerpetualCache
-
Eviction: A design designed to define recycling strategies, FIFO, LRU, etc
-
FlushInterval: flushes the cache automatically at certain intervals
-
Size: indicates the maximum number of cached objects
-
ReadOnly: Indicates whether it is read-only. If it is configured as read-write, the corresponding entity class must be serialized
-
Blocking: Configures whether the blocking is enabled when the corresponding key cannot be found in the cache until the corresponding data enters the cache
-
Cache-ref indicates a cache configuration that references another namespace. Operations on both namespaces use the same cache
To implement a shared cache between two namespaces, we can import the namespace attribute of the cache-ref tag into another namespace, for example:
<cache-ref namespace="mapper.OrderMapper"/>
Copy the code
— Label instructions refer to:
Tech.meituan.com/2018/01/19/…
03. Level 2 cache test
To verify the level 2 cache of Mybatis, you need to construct multiple operations of different SQLsessions to verify that cache sharing can be realized between these SQLsessions
1) Create the mybatis-configuration. XML file
<? The XML version = "1.0" encoding = "utf-8"? > <! DOCTYPE configuration PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> <! Setting name="cacheEnabled" value="true" /> <! -- <setting name="localCacheScope" value="false"/>--> </settings> <! <environment default="mysql"> <environment id="mysql"> <! <transactionManager type="JDBC"></transactionManager> <! --dataSource indicates the connection source configuration, POOLED --> <dataSource type="POOLED"> <property name="driver" Value = "com. Mysql. JDBC Driver" > < / property > < property name = "url" value = "JDBC: mysql: / / 127.0.0.1:3306 / test" > < / property > <property name="username" value="root"></property> <property name="password" value="123456"></property> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"></mapper> </mappers> </configuration>Copy the code
2) We use this configuration file to define two SQLSessions for the same query operation
The result of calling the method is as follows
We see that the cache is not shared, that is, the secondary cache is invalidated
3) The second test
We perform the commit of sqlSession1 before executing the second SQLSession operation
Execute the code as shown below
We see that level 2 caching has been successfully implemented
Why is that?
This is because only if the first sqlSession performs a commit can the second sqlSession sense it and then fetch the cache
So, if our data changes, it certainly can’t fetch the data from the cache, otherwise the data we see on the page won’t be up to date
Mybatis has also taken this into account for us. Let’s see what happens if we update the table in the middle of the operation
4) The third test
As shown below, in between the two query operations, we execute the update() method of another sqlSession
The command output is as follows
On the last query operation, the SQL statement was also executed, so in this case, the second-level cache is invalidated
In addition, if multiple table operations are performed during execution, that is, if table A is associated with table B, if an update operation is performed on table A, table B will not be aware of it and will get dirty data, affecting normal business logic
Level 2 cache implementation principle, we can refer to level 1 cache tracking source code, their own code to further explore, there are any questions can also communicate with me at any time oh ~
Mybatis uses cacheEnabled to enable and disable level 2 caching, which is enabled by default
To use mapper. XML, you also need to enable the cache within the namespace through the cache tag. Of course, you can also add other namespaces through cache-ref to share the level 2 cache
Mybatis has limitations for multi-table queries, and it is easy to read dirty data. A third-party cache implementation is recommended
To sum up
Color {#FF0000}{level 1 cache} level 2 cache \color{#FF0000}{level 2 cache} level 2 cache
Mybatis supports level 1 cache by default. It does not need to be set on or off, but it will only take effect when the transaction is started. The specific reason is understood by tracking the execution process of the query statement
And the level 2 cache, must be in front of the sqlSession after the transaction, can support, and the use of limitations, each sqlSession execution, must be submitted operation, other sqlSession wipe can sense the change, for multiple table operation easy to get dirty data and other defects.
For example, redis is used for data storage. For distributed scenarios, we can also ensure that the cache does not fail and dirty data will not be read, so as to ensure business consistency
Skills Summary
1. Implementation method and principle of Mybatis level 1 cache
2. Implementation method and invalidation scenario of MyBatis level 2 cache
3. Summary and suggestions on the use of two-level cache
Github address of the code: github.com/helemile/he…
If there are some non-cognitive code logic problems in the project, in addition to searching around, you might as well try to track the source code, step by step analysis to solve. This will not only help you understand how it works, but also improve your problem-solving skills. Try it. Trust me, the code will smell good after a while
If you find the article helpful at all, give it a thumbs up
Well, that’s it. Learn a little every day, and time will show you how strong you are.
Next period:
Spring Boot(8) : Monitoring feature of Spring Boot
This project code has been uploaded to Github ~ if necessary, please refer to github.com/wangjie0919…
Docker chapter
How do containers communicate with each other?
Docker chapter 3: Dockerfile combat open
Docker (ii) : Docker combat, command parsing
.
SpringCloud chapter
Spring Cloud (13) : How powerful is Feign?
Spring Cloud (10) : Information Center -Kafka Classic interview questions, do you know?
Spring Cloud (9) : Registry Selection – A complete summary of four registry features
Spring Cloud (iv) : Internal debate between Eureka and ZooKeeper
.
Spring Boot chapter
Spring Boot (7) : You can not not know Mybatis caching mechanism!
Spring Boot (6) : Those nice database connection pools
Spring Boot (4) : People love to hate JPA
SpringBoot (1) : Features overview
.
translation
How to Become a Google Development Expert (GDE) – A practical Guide
Use CSS to speed up page rendering
Will WebTransport replace WebRTC in the near future?
.
Occupation, life perception
Have you ever wondered, what is the meaning of travel?
Career planning for programmers
Ten Moments that Make programmers Crash! The sixth one was unbearable!
Talk about changing jobs