Cgb2005 - Jingtao 12 (redis entry case, distributed lock, springboot integration, redis, redis configuration class, cache scenario, objectmapper generic T, redis cache of commodity classification)

Cool breeze AAA 2022-02-13 07:43:34 阅读数:905

cgb2005 cgb jingtao redis entry

matters needing attention

1.redis Introductory cases : introduce jar package Writing test classes
2. Second kill business logic — Distributed locking mechanism
3.SpringBoot Integrate Redis
Compile configuration class optimization jedis Object creation
Cache application scenario analysis
Object and the JSON Interturn –ObjectMapper----> Encapsulate as a tool api
Implementation of commodity classification cache ( Trees – Select category )

1. Redis Introductory cases

Create a package in the test class , class .
Be careful : The package of the test class should also be under the package of the main startup class or its sub package .
 Insert picture description here

1.1.1 introduce jar package

<!--spring Integrate redis -->

 Insert picture description here

1.1.2 Introductory test cases

manage In test class

package com.jt.test;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.params.SetParams;
//@SpringBootTest If you need to introduce... In the test class spring Only the container mechanism uses this annotation , Such as attribute injection .
public class TestRedis {

/** * 1. Test remote redis Is the server available * Ideas : * 1. Instantiation jedis Tools API Link to (host:port) * 2. Use objects to execute redis command The method is to order * Error reporting and debugging : * 1. Check Redis.conf Whether the configuration file of is modified as required ip/ Protect / backstage * 2.redis Starting mode redis-server redis.conf * 3. Turn off firewall systemctl stop firewalld.service * */
public void test01(){

String host="";//redis Where ip Address 
int port =6379;//redis The port of 
Jedis jedis =new Jedis(host,port);// Note that this leads to redis.clients package 
jedis.set("cgb2006"," study hard ");
/** * 2. demand : * 1. towards redis Insert data k-v * 2. by key Set the timeout 60 Seconds after the failure * 3. Threads sleep 3 second * 4. obtain key The remaining life of * * Problem description : The following code exists bug, Is the data bound to be deleted ??? If there is an exception in the middle , The following data will not execute . * Problem specification : If you use redis And you need to add a timeout In general, the atomic requirements should be met . * Atomic row : Or at the same time , Or fail at the same time . Be careful : Must be completed at the same time . */
public void test02() throws InterruptedException {

/* Before optimization : Jedis jedis=new Jedis("",6379); jedis.set(" Baokemeng ", " Little fire dragon "); int a=1/0;// Throw an exception . jedis.expire(" Baokemeng ",60);// Set up key Effective time of Thread.sleep(3000);//1 second =1000 millisecond System.out.println(jedis.ttl(" Baokemeng "));// Check key The rest of the time */
/** Optimized writing : * If you want to add a timeout to the data ,redis It also provides optimized api Method . * */
Jedis jedis=new Jedis("",6379);
jedis.setex(" Baokemeng ",60," Little fire dragon ");
System.out.println(jedis.get(" Baokemeng "));
/** * 3. demand : * If you find that key Do not modify data when it already exists , If key Data is modified when it doesn't exist . * problem : Look at the code like this if els Too many levels ,redis The optimized writing method is provided , Instead of if-else. */
public void test03(){

Jedis jedis=new Jedis("",6379);
/* if(jedis.exists("redis")){ System.out.println("key Already exists , No modification "); }else{ jedis.set("redis", "aaaa"); }*/
// explain : If redisa If it exists, it will not be modified , Take the original value , If redisa No value exists " test nx Methods "
jedis.setnx("redisa", " test nx Methods ");
/** * 4. demand : * 1. When the user is asked to assign a value , If the data exists, no assignment is made .setnx * 2. It is required that in the assignment operation , You have to set the timeout time And it requires atomicity settex * problem : this 2 Two methods cannot be used at the same time , Then at the same time meet this 2 Need a new way to learn set Overload method of */
public void test04(){

Jedis jedis=new Jedis("",6379);
SetParams setParams=new SetParams();
//nx:key There is no assignment ex: second xx: Yes key Only when the value is assigned px: millisecond 
setParams.nx().ex(10);// Lock operation 
jedis.set("bbbb", " Implement business operations ",setParams );
jedis.del("bbbb"); // Unlock operation 
public void testList05() throws InterruptedException{

Jedis jedis=new Jedis("",6379);
jedis.lpush("list", "1","2","3");
public void testTx06() throws InterruptedException{

Jedis jedis=new Jedis("",6379);
//1. Open transaction 
Transaction transaction=jedis.multi();
try {

//2. Commit transaction 
} catch (Exception e) {

//3/ Roll back the transaction 

2. Second kill business logic — Distributed locking mechanism

The original price :6988 —> Present price 1 block
Problem description :1 Mobile phone 20 Everyone shows that the rush purchase is successful And paid for 1 Yuan …
Problem specification :
1.tomcat There are multiple servers
2. Database data only 1 Share ( There is only one copy of master-slave database data )
3. High concurrency is inevitable .( Multiple people rush to buy )
How to realize the rush purchase ???

2.1 Synchronous lock operation

2.1.1 The reason for oversold

analysis :a Buy a cell phone , To query the database , Database stock minus 1,b At this time, you can also access the database, but the database has not been reduced. At this time, there is a mobile phone in the database , This is going back to accessing the database minus 1.

explain :tomact Is a multithreaded operation , Multithreading preempting the same resource will inevitably lead to thread safety problems .
 Insert picture description here

2.1.2 The problem of synchronization lock

explain : Synchronous lock can only solve tomcat Internal problems , Can't solve multiple tomcat Concurrency issues .

analysis : Although in tomact A synchronous lock is added inside , however tomact There are multiple servers , There will still be thread safety issues .
 Insert picture description here

2.2 Distributed locking mechanism

Ideas :
1. Locks should use third-party operations , Locks should be public .
2. principle : If the lock is being used , Other users can't operate .
3. Strategy : The user to redis Save a key, If redis There is key Someone is using the lock , Other users are not allowed to operate . If redis There is no key , I can use the lock .
4. risk : How to solve the deadlock problem . Set the timeout .

Interview answer what is distributed lock ???
Distributed locks are generally available to everyone in a third party , Commonly used for distributed locks Redis Realization , towards Redis Add data to ,key It's a lock ,value Is the password .tomact The server accesses redis, If key If it exists, it cannot be executed ,key Can only be executed if it does not exist , And the key-value Put in redis in . Some deadlocks may occur when locking , So add timeout when locking , But the unlocking code is usually placed in finally in ,finally Anyone can use the code in , So to avoid the lock being released in advance by others, you need to add some password verification , The lock can only be removed if the passwords are consistent . Finally, the lock of my house can only be unlocked by me , Even if you don't understand it for a period of time, it will be released .

problem : Will there be simultaneous locking ??? Can't , because redis It's a single thread operation .
 Insert picture description here

3. SpringBoot Integrate Redis

3.1 Location description of configuration class

explain :
1). In the introductory case, each use redis Need to be new One Jedis object , More trouble . So it's best to give the rights of common objects to spring Containers to manage , Use wherever you need it @Autwried Note injection is enough .
2). because redis Then it will be used by other servers , So the best way is to Redis Save the configuration class to common in .

3.2 edit Pro File class

explain : Because this configuration is public , So put it in conmon In the configuration directory under the project .
 Insert picture description here

3.3 Edit configuration class JedisConfig(common in )

package com.jt.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.Jedis;
@Configuration // I am a configuration class 
@PropertySource("classpath:/properties/") // Write a living form , Import profile 
public class JedisConfig {

// Inside key No repetition , If properties and yml They all have the same key with yml Subject to 
@Value("${}") // This is a spel expression (springel) and el yes jsp The expression of 
private String host;
private Integer port;
/** * take jedis Object to spring Container management */
public Jedis jedis(){

// Because writing code is not conducive to expansion , So add the fixed configuration to the configuration file 
return new Jedis(host,port);

3.4 Cache application scenario analysis

3.4.1 What data can be cached

explain :
1. There is no need to update in real time, but it consumes a lot of data in the database .
2. Need to be updated in real time , But data that is not updated frequently .
3. At a certain time, the data that is accessed heavily and updated frequently . But the cache used for this kind of data cannot be the same as ordinary cache , This cache must be guaranteed not to be lost , Otherwise there will be big problems .
summary : Caching is suitable for small changes in data , But the business of often querying the database .

3.4.2 analysis jt-manage Those businesses are suitable for caching

1. Paging query : After adding data, the database records will be changed , On the paging page, the overall order will change , Data with large changes like this Not suitable for Do the cache .

2. Leaf category is suitable for caching : Every time the page is refreshed, no matter whether the page changes or not, a query will be carried out in the background .
 Insert picture description here
 Insert picture description here
3. Select the category suitable for caching : Just click the parent directory to access the database for query .
 Insert picture description here

3.5 Object and the JSON Interturn –ObjectMapper

3.5.1 Cause analysis

problem : Why transform ???
reason : Now the data queried in the tree directory is stored in List< EasyUITree> In this collection object , and redis Most of the types of storage required in are String type , Therefore, the queried data cannot be directly stored in redis In cache .

solve : Pass the object through Api The method in is transformed into json Save string into redis in . because Redis Essentially, String character string , When taking the value, it passes API Turn it into an object and take it out .

JSON Native offers :ObjectMapper
Ali provides :Fastjson

reflection :
1). Use it directly List< EasyUITree>.toString() Convert to string redis Can't you do it in the middle school ???
answer : no way , Although it can be converted into a string , But when there is no way to get the value, restore the string to an object .
2). use @ResponseBody This annotation converts the object into json String line ???
answer : no way , This annotation is equivalent to telling spring MVC The return value of the method is converted to JSON, Now it's time to use the data in the business layer approach , So this method is not suitable for . So I can only learn one set API Implement objects and JSON The transformation of data .

3.5.2 ObjectMapper Introductory cases

manage In test class

package com.jt.test;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jt.pojo.ItemDesc;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class TestObjectMapper {

// Simple objects and JSON Interturn :
public void test01(){

// Define a tool API object 
ObjectMapper objectMapper = new ObjectMapper();
// Create item table object test 
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(100L).setItemDesc("json test ")
.setCreated(new Date()).setUpdated(new Date());
try {

/** * 1. Convert objects to json, Because the assigned value may not be standardized , Not all values can be converted into json There are risks , * So you need to handle exceptions , Catch or throw . */
String result = objectMapper.writeValueAsString(itemDesc);
/** * 2. take json Data into objects , Strings and objects cannot be converted directly , Only through the reflection mechanism .. * Reflection : Given xxx.class Instantiate the object after the type . Make use of the object's get/set Method to assign a value to a property . * The first parameter is the data to be converted The second parameter is the object type to be converted . */
ItemDesc itemDesc2 = objectMapper.readValue(result,ItemDesc.class);
System.out.println(itemDesc2.getCreated());// Output parent's attribute creation time 
System.out.println(itemDesc2.getItemDesc());// Output your own ItemDesc attribute 
/* Output object , But the result is only 2 individual , We actually assigned a value with 4 Why is this data like this ??? Because this method we use is @Data Annotation generated , This annotation has a feature , Display only Their own properties do not display the properties of the parent , In fact, there are still some, but they don't show .*/
} catch (JsonProcessingException e) {

// Collection objects and JSON Interturn :
public void test02(){

ObjectMapper objectMapper = new ObjectMapper();
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(100L).setItemDesc("json test 1")
.setCreated(new Date()).setUpdated(new Date());
ItemDesc itemDesc2 = new ItemDesc();
itemDesc2.setItemId(100L).setItemDesc("json test 2")
.setCreated(new Date()).setUpdated(new Date());
List<ItemDesc> list = new ArrayList<>();
try {

//1. Convert objects to JSON ( In the same way )
String json = objectMapper.writeValueAsString(list);
//2. take json Convert to object Transformed json strand The type of transformation :list Collection gets the type as an object .
List<ItemDesc> list2 = objectMapper.readValue(json, list.getClass());
} catch (JsonProcessingException e) {


3.6 encapsulation ObjectMapperUtil(commom in )

3.6.1 Business description

explain : In fact, you need to use this in your business Api When converting , Exceptions often need to be handled by yourself (try–catch) Instead of throwing . But in the code try–catch Too much will lead to structural confusion , So writing tools Api Simplify .
step :
Method 1: Convert any object into JSON.
Method 2: Will be arbitrary JSON String is converted to an object .
Request to complete exception handling .

3.6.2 Defining tools API

package com.jt.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.util.StringUtils;
public class ObjectMapperUtil {

/** * explain : It's too much trouble to create an object every time you use it , A constant object can be defined only once . * advantage 1: Object only saves space * advantage 2: Objects are not allowed to be tampered with by others */
private static final ObjectMapper MAPPER = new ObjectMapper();
/** * 1. Convert any object to JSON * reflection 1: Any object should use Object Object to pick up * reflection 2: The return value is JSON strand So it should be String * reflection 3: How to convert JSON FASTJSON( Ali's )/objectMapper( Native ) */
public static String toJSON(Object object){
// Define static convenient calls 
try {

if(object == null){
 // Judge whether the data transmitted by the user is empty 
throw new RuntimeException(" Parameters passed object by null, Please check carefully ");
return MAPPER.writeValueAsString(object);// No exceptions, direct conversion 
} catch (JsonProcessingException e) {

// There was an exception in the conversion process , What to do with ? You should check for exceptions , Convert to runtime exception .
throw new RuntimeException(" The object passed does not support json conversion / Check if there is get/set Method ");
/** * 2. Will be arbitrary JSON String is converted to an object * demand : What type the user passes , What kind of object do I return * <T> T Customized a generic object , Consistency ensures that what type is passed is what type is defined , The return value is the defined generic , * This approach is generally applicable to tools API And the compilation of the source code . * */
public static <T> T toObject(String json,Class<T> target){

// String manipulation tool API
if(StringUtils.isEmpty(json) || target == null){

throw new RuntimeException(" The parameter passed cannot be null null");
try {

return MAPPER.readValue(json,target);
} catch (JsonProcessingException e) {

throw new RuntimeException("json Abnormal transformation ");

3.7 Realize the cache of commodity classification ( Select category )

3.7.1 Implementation steps

1. Definition Redis Medium key, key It must be the only one that cannot repeat . save take There should be consistency .
parendid Is the only thing that can be used as key, In order to see famous people, I intend to parentid The pre splicing keyword is used as the prefix , Generally, the prefix and characters are separated by :: Connect .
The final effect is :key = “ITEM_CAT_PARENTID::70”
2. according to key Go to redis Query in , With or without data
3. No data Then query the database for records , Then save the data to redis In order to facilitate subsequent use .
4. There's data Indicates that the user is not querying for the first time You can return the cached data directly .

3.7.2 edit ItemCatController

 /** * Business needs : Realize the display of commodity classification , Through ajax request , Dynamically obtain the data of tree structure . * url Address : http://localhost:8091/item/cat/list * Parameters : parentId = 0 Query the first level commodity classification menu . * Return value result : List<EasyUITree> ( Because the outermost layer of the required tree parameter format is []) * matters needing attention : * 1. No information is passed when the tree structure is initialized . Pass only when child nodes are expanded Id, If it doesn't pass id, And initialization show the of first-class goods id, The parent of the presentation id Namely 0. * 2. What kind of data does the page deliver , What kind of data the backend must receive */
public List<EasyUITree> findItemCatList(Long id){

// This place first tests and queries the first level menu , So first put id Write dead .
// Use the three wood operator to judge . No transmission id Or pass it on id The situation of .
Long parentId = (id==null?0L:id);// Initialization not transmitted id, According to the father id=0 You can query the first level menu information .
//return itemCatService.findItemCatList(parentId);
return itemCatService.findItemCache(parentId);

 Insert picture description here

3.7.3 edit ItemCatService

/** * Instead, query the commodity classification from the cache ( Trees ), The original method still works . * @param parentId * @return */
@Autowired(required = false)// Ensure the normal execution of subsequent operations You can lazy load ( When using, create objects )
private Jedis jedis; // Guide pack :redis.clients.jedis.Jedis;
public List<EasyUITree> findItemCache(Long parentId) {

//0. Define a common return value object 
List<EasyUITree> treeList = new ArrayList<>();
//1. Definition key
String key = "ITEM_CAT_PARENTID::"+parentId;
Long startTime = System.currentTimeMillis();// Record the start time 
//2. retrieval redis The server , Does it contain key

//3. Data exists Get cache data directly , And then it turns into an object 
String json = jedis.get(key);// according to key obtain value
long endTime = System.currentTimeMillis();// End time 
treeList = ObjectMapperUtil.toObject(json,treeList.getClass());//json Transfer object 
System.out.println(" from redis Get data in , Time consuming :"+(endTime-startTime)+" millisecond ");

//4. The data doesn't exist You should query the database first , Then save the data to the cache .
treeList = findItemCatList(parentId);// Directly call the above method of querying from the database ( Original method )
long endTime = System.currentTimeMillis();// End time 
//4.1 Save data to cache 
String json = ObjectMapperUtil.toJSON(treeList);// Object turn json
jedis.setex(key, 7*24*60*60, json);// Store in cache and set timeout 
System.out.println(" Query the database , Time consuming :"+(endTime-startTime)+" millisecond ");
return treeList;

3.7.4 Redis Speed test

test : Empty first redis cache :flushall
 Insert picture description here
 Insert picture description here
problem 1: Query cache is generally faster than query database 20 About times , Why is the first query so different ???
answer : For the first time, you need to connect to the database and establish communication , Communication takes time . After the first communication, a long connection relationship will be established ( Short time links will not close ), So you don't have to save time when establishing a connection again .
problem 2:
But the above write cache method is not good , Destroy the original code structure , Write business code and add a lot of cached code , Poor maintenance, etc … High coupling , solve — use AOP.


  1. complete Redis Case test
  2. take Redis Command to understand Official website order Set/zSet
  3. AOP Optimize cache .
    1. Use custom annotations @CacheFind Identify the cache service .( Blog )
    2. Understand the type of notification
    3. Understand pointcut expressions
    4. review AOP How it works
copyright:author[Cool breeze AAA],Please bring the original link to reprint, thank you.