Redis cache database query source code implemented by spring AOP

Application scenario

We hope to cache the database query results in redis, so that we can directly get the results from redis when we do the same query the second time, so as to reduce the number of database reads and writes.

Problems to be solved

Where is the code to operate the cache written? It must be completely separated from the business logic code.

How to avoid dirty reading? The data read from the cache must be consistent with the data in the database.

How to generate a unique identifier for a database query result? That is, through this ID (key in redis), a query result can be uniquely determined. The same query result must be mapped to the same key. Only in this way can the correctness of the cache content be guaranteed

How to serialize query results? The query result may be a single entity object or a list.

Solution

Avoid dirty reading

If we cache the query results, once the data in the database changes, the cached results will not be available. In order to achieve this guarantee, you can expire the relevant cache before executing the update query (update, delete, insert) query of the relevant table. In this way, the program will re read the new data from the database and cache it in redis at the next query. So the question is, how do I know which caches should be expired before executing an insert? For redis, we can use the hash set data structure to make a table correspond to a hash set, and all queries on this table are saved under the set. In this way, when the table data changes, you can directly expire the set. We can customize an annotation to indicate which tables this operation is related to through the attribute of the annotation on the database query method, so that when the expiration operation is performed, we can directly know which sets should be expired from the annotation.

Generate a unique ID for the query

For mybatis, we can directly use the SQL string as the key. However, in this way, you must write a mybatis based interceptor to tightly couple your cache code with mybatis. If you change the persistence layer framework one day, your cache code will be written in vain, so this scheme is not perfect.

Think about it carefully. In fact, if the class name, method name and parameter value of the two query calls are the same, we can determine that the two query results must be the same (on the premise that the data does not change). Therefore, we can combine these three elements into a string as a key to solve the identification problem.

Serialize query results

The most convenient serialization method is to use objectoutputstream and objectinputstream provided with JDK. The advantage is that almost any object, as long as it implements the serializable interface, can be serialized and deserialized with the same set of code. However, the disadvantage is also fatal, that is, the result capacity of serialization is too large, which will consume a lot of memory in redis (about three times the corresponding JSON format). Then we only have JSON.

JSON has the advantages of compact structure and strong readability, but the drawback is that when deserializing an object, you must provide specific type parameters (class object). If it is a list object, you must also provide two kinds of information: list and element type in list in order to be deserialized correctly. This increases the complexity of the code. However, these difficulties can be overcome, so we still choose JSON as the serialization storage method.

Where is the code written

There is no doubt that the AOP is on the stage. In our example, the persistence framework uses mybatis, so our task is to intercept the calls of mapper interface methods and write the following logic through around (surround notification):

Before the method is called, the key is generated according to the class name, method name and parameter value

Query redis through key

If the cache hits, the cache result is deserialized as the return value of the method call, and the call of the proxied method is blocked.

If the cache misses, execute the proxy method to get the query result, serialize it, and put the serialized result into redis with the current key.

code implementation

Because we want to intercept the mapper interface method, we must command spring to use the dynamic proxy of JDK instead of the proxy of cglib. To do this, we need to make the following configuration:

Then define two annotations marked on the interface method to pass type parameters:

Notes are used as follows:

The AOP code is as follows:

In this way, we have completed the implementation of database query cache.

UPDATE:

It is better to set an expiration time for the hash set, so that even if the cache policy is wrong (resulting in reading dirty data), the expiration time can still be synchronized with the database:

summary

That's all about the source code of redis cache database query implemented by spring AOP. I hope it will be helpful to you. Interested friends can refer to other related topics on this site. Thank you very much for your support for programming tips!

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>