preface
Redis is hot in today’s e-commerce environment, but in addition to dramatically improving performance, there are also problems with using Redis. The most common ones are: cache avalanche, cache breakdown, and cache penetration. This article will focus on these three problems and their solutions. For the basics of Redis interview, you can read the first article in this series: [Redis in ali’s Interview] series: Basics of Redis
Body: The interview begins
Young man, I see you have Redis on your resume, so have you learned anything about cache avalanche? Cache avalanche is when a large number of Redis keys fail at the same time, and suddenly the cache seems to be empty, and all requests hit the database. At this time, if it is instantaneous high concurrency and large flow, it can be disastrous for the database. And if the system doesn’t have a circuit breaker downgrade, it’s basically a dead beat. But what are the common scenarios for cache avalanches?
Here I will take an example: for example, in the second kill system, there will often be data warm-up before the start of the activity, what means, is to advance the relevant SKU and inventory information into the cache, generally into the cache data will set an expiration time, if these SKU expiration time is set the same. If 5000 requests are received at the beginning of the second kill, and these keys are all invalid at the same time, the single Redis can hold 5000QPS without any pressure, then the cache crashes, and all requests are directly sent to MySQL, so the database is bound to fail. Maybe it will “wow” to alert, but before the DBA can hear the sound, it will directly hang up. And no matter how much you restart the database, as long as Redis doesn’t have hot data, MySQL will immediately be overwhelmed by new traffic.
Development: Here Shaoxia used Jmeter to pressure test the standalone database. The parameters set are shown in the figure below. Parameter description:
- Number of Threads(Users) : Specifies the Number of Threads or the Number of requested users. 2000 concurrency is set.
- Ramp-up period(seconds) : specifies the request interval. This is set to 0, indicating simultaneous requests.
- Loop Count: Number of loop requests per thread.
When all hotspot data can be obtained from Redis, the pressure test results are as follows:
- Samples: Indicates how many requests you made in total for this test, and if you simulated 10 users and looping 10 times for each user, it would show 100.
- Average: Average response time — By default, this is the Average response time of a single Request. When Transaction Controller is used, the Average response time can also be displayed in units of Transaction.
- Median: The response time of 50% of users.
- X% Line: X% user response time.
- Min: indicates the minimum response time.
- Max: indicates the maximum response time.
- Error% : Number of incorrect requests/total requests in this test.
Throughput: Indicates the number of requests completed per Second by default. Note that 99% of requests are 7ms in response and MySQL throughput is 265.4/s. \
When a cache avalanche occurs in Redis
Note that 99% of the requests reached 5136ms and throughput was reduced by more than half.
Ok, so how do you deal with cache avalanche in development? There are three ways to do this. One is to add a random value to the expiration time of each key to ensure that they do not expire at the same time. Second, in the clustered Redis environment, distributing these hot data to different Redis libraries can also avoid the risk of all failure at the same time; The third option is more simple and crude, and does not set the expiration time at all, but only synchronously updates the cache when MySQL data is updated. What do you know about cache penetration and cache penetration, and how they relate to cache avalanche? Breakdown with cache cache is a bit like an avalanche, but have a bit different, cache because the cache invalidation of large area, an avalanche collapse type database, and breakdown of different cache is a cache breakdown is a key is very hot, constantly carrying high concurrency, high concurrent flow concentration on a visit to this key, when the key in the instant of failure, Continuous large concurrency breaks through the cache and requests the database directly, like cutting a hole in an undamaged bucket. Cache penetration is data that is not in the cache or the database. The user may make malicious requests or access expired data that has been physically deleted. MySQL > select * from user where id=-1; MySQL > select * from user where id=-1; MySQL > select * from user where id=-1; How do you solve these two problems? There are two main solutions to cache breakdown. One is to set the hotspot key to never expire. The other is to use mutex. Only one user can obtain the key at a time. If the key fails, it will be queried from MySQL and updated to Redis. Cache penetration is more exquisite, cache penetration is mainly likely to be malicious attacks by hackers or industry competitors, especially for external API interfaces, such as paging parameters if not verified, the caller said nothing, first request it a hundred million! A few more concurrent calls, and if this were during 618, it’s pretty obvious what would happen. So the first thing we can do is check the key parameters in the interface and intercept these requests at the code level. In fact, we need to have a “distrust” mentality in development, because you don’t know who is calling your interface. The second way is the famous Bloom filter, the principle is also very simple, is to use efficient data structure and algorithm to quickly determine whether your key exists in the database, if not, you return, you go to check MySQL refresh to Redis and return the result set. \
summary
Redis is now the representative of NoSQL, and it is good for personal growth to use and master its principles. \
The relevant code of this example is as follows for reference only (please leave a message if you need the complete source code) :
/ * * *@author Carson
* @date 20-8-20 下午5:00
*/
@RestController
public class CommonController {
private final Logger logger = LoggerFactory.getLogger(CommonController.class);
@Autowired
private JdbcTemplate template;
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping(value = "/showValue", method = RequestMethod.GET)
public String showName(@RequestParam("key") Integer key) {
// query from cache
ValueOperations valueOperations = redisTemplate.opsForValue();
String val = (String) valueOperations.get(key);
if (StringUtils.isBlank(val)) {
logger.warn("Cache avalanche!!");
// query from mysql
String sql = "SELECT user_name FROM cps_user_info WHERE id=?";
val = template.queryForObject(
sql, new Object[]{key}, String.class);
if (StringUtils.isNotBlank(val)) {
valueOperations.set(key, val, 6000L, TimeUnit.SECONDS); }}return "Value:" + val;
}
@RequestMapping(value = "/insertRedis", method = RequestMethod.GET)
public void insertRedis(a) {
Integer key = 1;
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, "SEU".10L, TimeUnit.SECONDS); }}Copy the code