1 introduction

It is usually very fast for programmers to learn the installation and use of Redis, because most operations of Redis are equivalent to the operation logic of Map. As long as you understand most APIS combined with Baidu, you can quickly put into CRUD work. Therefore, today’s Redis is not commonly used operation guide. Of course, the ones that are not commonly used here are not used every day, but there are still applications in the project.

2 install Redis

Install and native commands

3 SpringBoot2 integration with Redis

3.1 Related Dependencies

<! > <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <! -- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <! Commons -pool2</artifactId> </dependency> <! -- hutool --> <dependency> <groupId>cn. Hutool </groupId> <artifactId>hutool-all</artifactId> <version>5.7.17</version> </dependency>Copy the code

3.2 Configuration File

Spring: redis: host: 127.0.0.1 # server address port: 6379 # server port timeout: 10000 # Connection timeout: pool: max-active: 8 # Maximum number of connections max-wait: -1ms # Maximum waiting time for blocking (negative value indicates no limit) Min-idle: 0 # Minimum idle connection max-idle: 8 # Maximum idle connectionCopy the code

3.3 Configuration code (in the startup class)

@Bean
public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate<String, T> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
    return template;
}
Copy the code

Add this configuration to resolve garbled characters

4 Uncommon operations

4.1 SCAN (Must know must know)

Generally, scan is used to replace keys. When the keys command is executed, a Redis lock will be triggered, resulting in a large area of Redis operation blocking. Therefore, Redis provides scan command, which does not block the main thread and supports data return in batches by cursor, which is an ideal choice. We need to do the de-duplication, this in Java using Set to receive the return value is ok.

4.1.1 Code implementation

package com.example.demo.redis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ScanOptions; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @component public class RedisScan {/** * get a batch of keys with the specified prefix eg: Key :* Obtain all keys: start key * * @param pattern key matches the regular * @param count Number of keys obtained at a time * @return */ public Set<String> scan(String) pattern, int count) { return redisTemplate.execute((RedisCallback<Set<String>>) connection -> { Set<String> keysTmp = new HashSet<>(); try (Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder() .match(pattern) .count(count).build())) { while (cursor.hasNext()) { keysTmp.add(new String(cursor.next(), StandardCharsets.UTF_8)); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); } return keysTmp; }); } public void batchDelete(String pattern, String name, String name, String name, String name, String name, String name, String name) int step) { while (scan(pattern, step).size() > 0) { Set<String> keys = scan(pattern, step); redisTemplate.delete(keys); Public void mock(String keyPrefix, int count) {Map<String, String> map = new HashMap<>(); for (int i = 0; i < count; i++) { map.put(keyPrefix + i, String.valueOf(i)); } redisTemplate.opsForValue().multiSet(map); } @Resource private RedisTemplate<String, Object> redisTemplate; private static final Logger LOGGER = LoggerFactory.getLogger(RedisScan.class); }Copy the code

Provides scan method and batch delete method, provides mock data method, has been tested

4.1.2 test

package com.example.demo.redis; import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.TimeInterval; import com.example.demo.ApplicationTests; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; public class RedisScanTest extends ApplicationTests { @Autowired RedisScan redisScan; private static final Logger LOGGER = LoggerFactory.getLogger(RedisScanTest.class); @Test public void testMock() { TimeInterval timer = DateUtil.timer(); redisScan.mock("mock:", 10000); Logger. info(" time: {}ms", timer.interval()); } @Test public void batchDeleteTest() { TimeInterval timer = DateUtil.timer(); redisScan.batchDelete("mock:*", 1000); Logger. info(" time: {}ms", timer.interval()); }}Copy the code

4.2 BitMap (Need to know)

Redis’s common data types can also fulfill the requirements handled by Bitmaps, which are not created for a specific business, but to save memory

  • 1. Storage is based on the smallest unit bit, so it is very space-saving.
  • 2. Set time complexity O(1), read time complexity O(n), the operation is very fast.
  • 3. Storage of binary data, which is very fast when performing relevant calculations.
  • 4. Convenient capacity expansion

4.2.1 Preset requirements

A platform has 1E users and the online status of each user needs to be marked

4.2.2 Code implementation

package com.example.demo.redis; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component public class RedisBitMap { @Resource private RedisTemplate redisTemplate; private static final String ONLINE_STATUS = "online_status"; ** @param userId * @param online */ public void setOnlineStatus(int userId, boolean online) { redisTemplate.opsForValue().setBit(ONLINE_STATUS, userId, online); } /** ** public Long getOnlineCount() {return (Long) redistemplate.execute ((RedisCallback<Long>) con -> con.bitCount(ONLINE_STATUS.getBytes())); }}Copy the code

Holdings test

package com.example.demo.redis; import com.example.demo.ApplicationTests; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; public class RedisBitMapTest extends ApplicationTests { @Autowired RedisBitMap redisBitMap; private static final Logger LOGGER = LoggerFactory.getLogger(RedisBitMapTest.class); @test public void setOnlineStatusTest() {// for (int I = 0; i < 10000; I++) {/ / set the even odd don't online online redisBitMap. SetOnlineStatus (I, I % 2 = = 0). } } @Test public void onlineCountTest() { Long i = redisBitMap.getOnlineCount(); LOGGER.info("oline count = {}", i); }}Copy the code

4.3 HyperLogLog (Required)

HyperLogLog, you can think of its function as a storage optimized Set, suitable for large sites for user behavior statistics business scenarios, if you are a small site, use Set. HyperLogLog count statistics have a certain error, the maximum error is less than 1%, so HyperLogLog is not suitable for 100 percent accurate statistics of the scene, website traffic statistics can usually accept such error.

4.3.1 the pros and cons

  • The cardinal number is not large, the amount of data is not large, will be a bit overqualified waste of space
  • There are limitations, that is, you can only count the cardinal number, and there is no way to know what the specific content is, right

4.3.2 Sample code

package com.example.demo.redis; import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.TimeInterval; import com.example.demo.ApplicationTests; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.RedisTemplate; import javax.annotation.Resource; /** * RedisHyperLoglog extends ApplicationTests {@resource; /** * RedisHyperLoglog extends ApplicationTests {@resource private RedisTemplate<String, String> redisTemplate; @Test public void hllTest() { TimeInterval timer = DateUtil.timer(); String key = "pv_hll:20211220"; For (int I = 1; i < 1000; i++) { redisTemplate.opsForHyperLogLog().add(key, String.valueOf(i)); } Long size = redisTemplate.opsForHyperLogLog().size(key); LOGGER. The info (" size = {}, time-consuming = {} ms ", the size, the timer. The interval ()); } @test public void setTest() {TimeInterval timer = dateutil.timer (); String key = "pv_set:20211220"; For (int I = 1; i < 1000; i++) { redisTemplate.opsForSet().add(key, String.valueOf(i)); } Long size = redisTemplate.opsForSet().size(key); LOGGER. The info (" size = {}, time-consuming = {} ms ", the size, the timer. The interval ()); / / operating 999 times return 999} private static final Logger Logger. = LoggerFactory getLogger (RedisHyperLogLogTest. Class); }Copy the code

Skilled use of Bitmap and HyperLogLog, combined with redis autoincrement and other operations, you will have the technical basis of website behavior statistics business, the next step is to understand PV, UV IP and other statistical business needs, to do a BI system (behavior identity)

4.4 Geographic Information GEO

Mainstream databases provide geographic information support, including storage, analysis, data generation and so on, for geographic information related business products, Redis GEO operation has the advantages of simple operation, high performance, but Redis GEO only provides point support, the lack of line and surface support

4.4.1 Code examples

package com.example.demo.redis; import org.springframework.data.geo.*; import org.springframework.data.redis.connection.RedisGeoCommands; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @component public class RedisGeo {@resource private RedisTemplate RedisTemplate; private static final String CITY = "city"; Public void add(String name, double x, double x, double x, double x, double x) double y) { redisTemplate.opsForGeo().add(CITY, new Point(x, y), name); } /** * distance(km) ** @param city1 * @param city2 * @return */ public double distance(String city1, String city2) String city2) { Distance distance = redisTemplate.opsForGeo().distance(CITY, city1, city2, RedisGeoCommands.DistanceUnit.KILOMETERS); return distance.getValue(); } @param city @param distance * @return */ public List<Map<String, Object>> circum(String city, List<Point> positions = redistemplate.opsforgeo ().position(CITY, CITY); List<Map<String, Object>> cityList = new ArrayList<>(); if (CollectionUtils.isEmpty(positions)) { return cityList; } Point point = positions.stream().findFirst().get(); Circle circle = new Circle(point, new Distance(distance, Metrics.KILOMETERS)); RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(5) ; GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo() .radius(CITY, circle, args); for (GeoResult<RedisGeoCommands.GeoLocation<String>> result : results) { RedisGeoCommands.GeoLocation<String> content = result.getContent(); String name = content.getName(); Point cityPoint = content.getPoint(); Distance cityDistance = result.getDistance(); // To demonstrate the use of these apis, I wrap the return value as map map <String, Object> cityMap = new HashMap<>(); cityMap.put("name", name); cityMap.put("lng", cityPoint.getX()); cityMap.put("lat", cityPoint.getY()); cityMap.put("distance", cityDistance.getValue()); cityList.add(cityMap); } return cityList; }}Copy the code

4.4.2 test

package com.example.demo.redis; import com.example.demo.ApplicationTests; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; import java.util.Map; public class RedisGeoTest extends ApplicationTests { @Autowired private RedisGeo redisGeo; @test public void addTest() {// Add some city points redisGeo. Add (" Beijing ", 116.405285, 39.904989); RedisGeo. Add (" Wuhan ", 114.311582, 30.598467); RedisGeo. Add (" Zhengzhou ", 113.631419, 34.753439); RedisGeo. Add (" guangzhou ", 113.271431, 23.135336); RedisGeo. Add (" Nanning ", 108.373451, 22.822607); } @test public void distanceTest() {// Double distance = redisGeo. Distance (" Beijing ", "Wuhan "); LOGGER.info("distance = {}km", distance); } @test public void circumTest() {List<Map<String, Object>> circumCity = redisgeo-.circum (" Beijing ", 1000); LOGGER.info("circum city = {}", circumCity); } private static final Logger LOGGER = LoggerFactory.getLogger(RedisGeoTest.class); }Copy the code

Redis Geo basically covers mainstream point-location related scenes, such as nearby people, nearby shops and so on. If we are familiar with these apis, we can solve these needs. If we don’t have such needs, we can also serve as a knowledge store. MySQL spatial data storage and functions (juejin. Cn)

4.5 Message Queues

The new version of Redis provides official message queue support (although this is an inevitable choice for Redis authors, since so many people use Redis lists to process messages). We introduce the list-based message queue implementation and the official provision of message queues

4.5.1, based on the List

Production, consumption

package com.example.demo.redis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; */ @Component public class RedisQueueOfList {private static final Logger Logger = LoggerFactory.getLogger(RedisQueueOfList.class); @Resource private RedisTemplate<String, String> redisTemplate; private static final String TOPIC = "redis_queue"; /** @param MSG */ public void send(String MSG) {redistemplate.opsForList ().leftpush (TOPIC, MSG); } @construct public void listener() {logger. info(" consumer started...") ); new Thread() { public void run() { while (true) { String msg = redisTemplate.opsForList().rightPop(TOPIC, 1, TimeUnit.SECONDS); if (msg == null) { continue; Logger. info("queue MSG = {}", MSG); } } }.start(); }}Copy the code

The test interface

package com.example.demo.redis.controller;

import com.example.demo.redis.RedisQueueOfList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("redis-queue")
public class RedisQueueController {

    @GetMapping("/send")
    public void send(String msg) {
        queueOfList.send(msg);
    }

    @Autowired
    private RedisQueueOfList queueOfList;
}
Copy the code

Redis List-based message queue is simply a List push, consumer cycle pop, can be used in small scale, message volume scenarios recommend using more professional message queue middleware (Kafka, RocketMQ…)

4.5.2 of Channel

Redis publish subscription (PUB/SUB) is a message communication model: the sender (PUB) sends the message and the subscriber (sub) receives the message.

Production and consumption

package com.example.demo.redis; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import org.springframework.stereotype.Component; import javax.annotation.Resource; @component public class RedisChannel {/** * @param MSG */ public void send(String MSG) { redisTemplate.convertAndSend(CHANNEL, msg); } /** * register message listener ** @param connectionFactory * @param listenerAdapter * @return */ @bean public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { RedisMessageListenerContainer container = new RedisMessageListenerContainer();  container.setConnectionFactory(connectionFactory); container.addMessageListener(listenerAdapter, new PatternTopic(CHANNEL)); return container; } /** * message listener adapter, binding message handler, Call the business method of the message handler using reflection technology * * @param receiver * @return */ @bean public MessageListenerAdapter messageListenerAdapter(RedisChannelReceiver receiver) { return new MessageListenerAdapter(receiver, "receiver"); } @Resource private RedisTemplate<String, String> redisTemplate; private static final String CHANNEL = "redis_channel"; }Copy the code

Message processing

package com.example.demo.redis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; Component public class RedisChannelReceiver {public void Receiver (String MSG) {// TODO message processing LOGGER.info("receiver msg = {}", msg); } private static final Logger LOGGER = LoggerFactory.getLogger(RedisChannelReceiver.class); }Copy the code

The test interface

package com.example.demo.redis.controller;

import com.example.demo.redis.RedisChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("redis-channel")
public class RedisChannelController {

    @GetMapping("send")
    public void send(String msg) {
        redisChannel.send(msg);
    }

    @Autowired
    private RedisChannel redisChannel;
}
Copy the code

At the end of may

Redis can do a lot of things, but the premise is to have an accurate estimate of the amount of data, the code may not be standardized, mainly in order to demonstrate the use of API, we choose to eat, have a gain of students give a thumbs up bar 0^0

6 My other articles

Redis and local Cache combination – Nuggets (juejin. Cn)

Ali Demand Challenge – 5 login failures within 10 minutes, need to wait 30 minutes to login

Understand user login verification process (attached picture) – Juejin (juejin. Cn)

My other articles cover business solutions, caches, database operations, message queues, SaaS platforms, etc. If you are interested, check out my home page