[Java from 0 to architect] Redis - Advanced (pipline, publish and subscribe, Bitmap, hyperlog, GEO)

Keywords: Java Database Redis

Java from 0 to architect Directory: [Java from 0 to architect] learning record

Some concepts: PV (Page View) visits, UV (Unique Visitor) independent visitors

Basic use of Jedis

Reference article: Redis notes Java operations redis (Jedis)

<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.1.0</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

Redis data elimination strategy

  • lft: eliminated according to the number of times the key is used
  • lru: obsolete according to the latest usage time of the key

In Redis, users are allowed to set the maximum memory size (configured in redis.conf)

maxmemory 1G
maxmemory-policy noeviction

maxmemory-policy:

  • Volatile LRU deletes the least commonly used data from the data for which the timeout is set
  • Allkeys LRU queries the least frequently used data of all keys to delete
  • Volatile random is randomly deleted from the data for which the timeout has been set
  • Allkeys random queries all keys and deletes them randomly
  • Volatile TTL queries all the data with set timeout, sorts and deletes the data about to expire
  • noeviction is the default policy. If this property is set, the deletion operation will not be performed. If the memory overflows, an error will be reported and returned
  • Volatile LFU removes the least frequently used key from all keys configured with expiration time
  • Allkeys LFU removes the least frequently used key from all keys

Advanced development of Redis

Command execution process:

pipline - command batch processing to reduce the network overhead of a large number of commands and improve operation performance

pipline application:

  1. Multiple commands can be executed in parallel without correlation

    pipline does not guarantee atomicity, but sends a bunch of commands to execute; mset this command is atomic

  2. For operations that need to be performed in batch processing, multiple different data need to be put into memory at the same time

  3. The time consumption is more at the network level. The time required to execute commands is us (microsecond)

Code implementation:

public class JedisDemo {

    private Jedis jedis = null;

    @Before
    public void init() throws Exception {
        jedis = new Jedis("192.168.48.101", 6379);
    }

    @Test
    public void testJedis() throws Exception {
        jedis.flushAll();
        Long start = System.currentTimeMillis(); // Get start time
        for (int i = 0; i < 100000; i++) {
            jedis.set("name" + i, i + "");
        }
        Long end = System.currentTimeMillis(); // Get end time
        System.out.println(end - start);
        jedis.close();
    }
    
    @Test
    public void testPipeline() throws Exception {
        jedis.flushAll();
        Long start = System.currentTimeMillis(); // Get start time
        Pipeline pipelined = jedis.pipelined();
        for (int i = 0; i < 100000; i++) {
            pipelined.set("name" + i, i + "");
        }
        pipelined.sync();
        Long end = System.currentTimeMillis(); // Get end time
        System.out.println(end - start);
        jedis.close();
    }
}

Publish subscribe - Subscribe

Understand that there is a special message middleware for publishing and subscribing


Roles in publications and Subscriptions:

  • Publisher publisher
  • Subscriber subscriber
  • channel
# Subscribe to one or more channels subscribe < channel > 
subscribe sohu:tv

# Publish < channel > < message >
publish sohu:tv "hello world"

# Unsubscribe unsubscribe

Test case:

  • producer:
public class PublisherDemo {

    private Jedis jedis = null;

    @Before
    public void init() throws Exception {
        jedis = new Jedis("192.168.48.101", 6379);
    }

    @After
    public void destroy() throws Exception {
        jedis.close();
    }

    @Test
    public void publish() throws Exception {
        for (int i = 0; i < 100; i++) {
            jedis.publish("channel02", "value" + i);
            TimeUnit.MICROSECONDS.sleep(10);
        }
    }
}
  • consumer:
public class Subscriber {

    private Jedis jedis = null;
    
    @Before
    public void init() throws Exception {
        jedis=new Jedis("192.168.48.101", 6379);
    }
    
    @After
    public void destroy() throws Exception {
        jedis.close();
    }
    
    @Test
    public void subscribe() throws Exception {
        JedisPubSub jedisPubSub = new JedisPubSub(){
            @Override
            public void onMessage(String channel, String message) {
                System.out.println("channel = " + channel);
                System.out.println("message = " + message);
            }
        };
        jedis.subscribe(jedisPubSub, "channel01", "channel02");
    }
}

Bitmap - a continuous string of binary digits (string), each bit at an offset

Bitmap is a series of consecutive binary digits (0 or 1), and the position of each bit is offset

setbit,getbit,bitcount:

# Set the specified bit
127.0.0.1:6379> setbit qq:uv 1002 1
127.0.0.1:6379> setbit qq:uv 1003 1

# Gets the value of the specified location and returns 1 or 0 
127.0.0.1:6379> getbit qq:uv 1003
(integer) 1

# bitcount: number of with statistical value of 1
127.0.0.1:6379> BITCOUNT qq:uv 
(integer) 1

bitop:

# Find a logical sum (default and 1) for one or more key s, and save the results to destkey
BITOP AND destkey key [key ...]

1# Find a logical or (default and 0) for one or more key s and save the results to destkey
BITOP OR destkey key [key ...]

# Find logical XOR for one or more key s and save the results to destkey
BITOP XOR destkey key [key ...]

# Find the logical negation of the given key and save the result to destkey
BITOP NOT destkey key

bitpos:

# bitpos: used to return the index position of the operation
# 5: Means to start searching from the 5th byte, and 8: means to start searching from the 8th byte
127.0.0.1:6379> BITPOS qq:uv 1 5 8

Application: count the number of logged in users to record the day when users log in. Only record whether they log in every day. The repeated login status is considered logged in. There is no need to record the user's operation behavior, last login time and ip address

For example, there are now the following users:
id	  name
1 	  Zhang San
3	  Li Si
8	  Wang Wu

# bitset key offset [0,1]
# When Li Si logs in, it is recorded that Li Si logs in today
bitset login_20191206 3 1
# At this time, the following information is stored in the bitmap: 00000000, 00001000

# Wang Wu logged in. Wang Wu logged in today
bitset login_20191206 8 1
# At this time, the following information is stored in the bitmap: 00000001 00001000

Code implementation:

  • Initialization data:
@Test
public void initData() throws Exception {
    // Add logged in users
    jedis.setbit("user:login:20190919", 1, "1"); // The user with id 1 has logged in
    jedis.setbit("user:login:20190919", 8, "1"); // The user with id 8 has logged in
    jedis.setbit("user:login:20190919", 12, "1"); // The user with id 12 has logged in
    jedis.setbit("user:login:20190920", 8, "1"); // The user with id 8 has logged in
    jedis.setbit("user:login:20190920", 22, "1"); // The user with id 22 has logged in
    jedis.setbit("user:login:20190921", 8, "1"); // The user with id 8 has logged in
    jedis.setbit("user:login:20190921", 24, "1"); // The user with id 22 has logged in
}
  • Statistics of login number:
@Test
public void useData() throws Exception {
    // 1. Count the number of login users in 20190919
    System.out.println(jedis.bitcount("user:login:20190919"));
    // 2 count the total number of login users in the last three days
	jedis.bitop(BitOP.OR, "user:login:last3_1", 
						  "user:login:20190919",
						  "user:login:20190920",
						  "user:login:20190921");
    System.out.println(jedis.bitcount("user:login:last3_1"));
    // 3 count the number of users who have logged in for three consecutive days
    jedis.bitop(BitOP.AND, "user:login:last3_2", 
    					   "user:login:20190919",
    					   "user:login:20190920",
    					   "user:login:20190921");
    System.out.println(jedis.bitcount("user:login:last3_2"));
}

Hyperlog - a cardinality statistical algorithm

Hyperlog is an algorithm used for cardinality Statistics (inaccurate, fuzzy value)

  • When the number or volume of input elements is very large, the space required to calculate the cardinality is always fixed and small

In Redis, each hyperlog key only needs 12KB of memory to calculate the cardinality of nearly 2 ^ 64 (about 4.2 billion) different elements

This is in sharp contrast to collections where the more elements consume more memory when calculating cardinality

Considerations for using hyperlog:

  • Can errors be tolerated (0.81%)
  • Need a single piece of data
  • Do you need to use small memory
# pfadd: add any number of elements to the specified hyperlog
pfadd qq:tv:uv 003 004 005

# pfcount: used to count the number of elements
127.0.0.1:6379> pfcount qq:tv:uv

# pfmerge: merge multiple keys into one key to automatically filter duplicate elements
pfmerge tv:uv qq:tv:uv aiqiyi:tv:uv

Code implementation:

// Add logged in users add 10000 users
Pipeline pipelined = jedis.pipelined();
for (int i = 0; i < 5000000; i++) {
    pipelined.pfadd("pf:user:login:20190919","user"+i);
}
pipelined.sync();
System.out.println(jedis.pfcount("pf:user:login:20190919"));

GEO - geographic information positioning, storage of longitude and latitude, calculation of distance between two places, calculation range

geoadd Add a geographic location
geopos Get geographic location information
geodist Get two geographic locations
georadius Gets the data within the specified range

Longitude and latitude query: https://lbs.amap.com/tools/picker

Code implementation:

  • Package latitude and longitude information:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CityPosition {
    // longitude
    private double lng;    
    // latitude
    private double lat;    
    // Merchant id
    private String key;    
}
  • Initialize the corresponding city data:
@Test
public void initData() throws Exception {
    CityPosition s1 = new CityPosition (113.264385, 23.129112, "Guangzhou");
    CityPosition s2 = new CityPosition (121.473658, 31.230378, "Shanghai");
    CityPosition s3 = new CityPosition (114.085947, 22.547, "Shenzhen");
    CityPosition s4 = new CityPosition (113.746262, 23.046237, "Dongguan");
    jedis.geoadd("city", s1.getLng(), s1.getLat(), s1.getKey());
    jedis.geoadd("city", s2.getLng(), s2.getLat(), s2.getKey());
    jedis.geoadd("city", s3.getLng(), s3.getLat(), s3.getKey());
    jedis.geoadd("city", s4.getLng(), s4.getLat(), s4.getKey());
}
  • Get the corresponding city information:
@Test
public void getInfo() throws Exception {
    System.out.println(jedis.geodist("city", "Guangzhou", "Dongguan", GeoUnit.KM));
    List<GeoRadiusResponse> datas = jedis.georadius("city", 113.264385,23.129112, 100, GeoUnit.KM);
    for (GeoRadiusResponse data : datas) {
        System.out.println(data);
        System.out.println("city: = " + new String(data.getMember()));
    }
}

Posted by alphamic on Sun, 03 Oct 2021 12:21:11 -0700