The use of the bitmap

This chapter describes how to use a bitmap. The underlying type of a bitmap is also a string. Generally speaking, each key is a string, but a special command is used to perform bit operations on the string. The commands in this chapter may be unfamiliar to those who are not familiar with them. You are advised to start with application scenarios to improve your interest.

Note: The following screenshot test data is not fully explained, sometimes I do not understand the display, you can leave a message in the comment section

I. Bitmap feature Description

The underlying structure is string type. 2. The maximum length of the bit is 2^32-1, meaning that the maximum storage capacity of each key value is 512 MB

2. Use of related commands

This chapter still starts with commands. If you feel bored, you are advised to start with application scenarios and then go back to the use of commands.

(a) setbig

The command format is SETBIT key offset value

Function: Set a bit value

The demo:

Getbit (2)

The command format is GETBIT key offset

Function: Get a bit value

The demo:

(3) bitcount

BITCOUNT key [start end]

Function: Count the number of bits in the string set to 1

The demo:

(4) bitop

BITOP operation destkey key [key…]

Function: Perform bitwise operations on one or more string keys that store binary bits, and save the result to destkey

The demo:

First set a batch of data for keys test1000 and test4000

Logical union test1000 and test4000, and save the result to resultandSimilarly, other bit operations are not demonstrated

(5) the BITFIELD

Format: # key [GET type offset] [the SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP | | SAT FAIL]

Function: This command treats a Redis string as an array of bits and can address variable-length bit widths and any unbyte aligned field of specified integer bits.

Test1000: Fetch a five-digit unsigned number from test1000. The binary sum is 29

(6) bitpos

BITPOS key bit [start] [end]

Function: Returns the first bit in a string to be set to 1 or 0. Start and end represent bytes, 8 bits of a byte. Start =0 and end=2 indicate search within the first three bytes

The demo:

First set a batch of data (this is set from 1, but the data is not set from zero, default is 0)

View the bits whose range is first set to 1 from the first byte to the second byte

View the first bit set to 0

View bits whose range is set to 0 for the first time from the first byte to the second byte

Three, application scenarios

(I) To realize the check-in function

1, requirements,

Front Desk User:

  • User check-in, whether the check-in today, monthly check-in recordsCopy the code

Background management:

  • Count how many users check in every dayCopy the code

2, design

  • Redis bitmap is used for storage
  • Store all daily user check-ins: the bitmap’s key is designed as userSign+ date, such as userSign_20211122, and offen takes the user’S ID
  • Store a single user’s monthly check-in: The bitmap key is userSign+ ID + date, for example, userSign_ user ID_202111, and offen is the day of the month
  • For user check-in, set the value of the corresponding key and the corresponding offset to 1

3. Code development

Foreground function interface

The following interface implementation is more detailed, mainly for easy viewing. Normal project development is bound to return multiple types of data wrapped in a single interface. For example, whether the current day check-in, a month check-in calendar, and consecutive check-in times are encapsulated into one interface

A. User check-in interface
@APIOperation (" Check-in ") @postMapping (value = "/userSign") public RestResponse userSign() throws Exception{ UserId = 123456L; // userId = 123456L; LocalDate localDate = LocalDate.now(); // Monthly sign-in key String userKey = "userSign_"+ long.valueof (userId)+"_"+ String.valueof (localDate.getYear()) + localDate.getMonth().getValue(); String signKey = "userSign_"+"_"+ String.valueof (localDate.getYear()) + localDate.getMonth().getValue(); String signKey = "userSign_"+"_"+ String.valueof (localDate.getYear()) + localDate.getMonth().getValue(); Int date = localdate.getDayofMonth (); / / users monthly check-in set a Boolean result. = redisTemplate opsForValue () setBit (userKey, new Long (date), true); If (result){return new RestResponse(60001," You checked in today! Come back tomorrow "); } else {/ / collection of all users monthly check-in redisTemplate opsForValue () setBit (signKey, userId, true); Return new RestResponse(0," check in successful "); }}Copy the code
B. Obtain monthly check-in data of users
@apiOperation (" Obtain monthly user check-in data ") @postMapping (value = "/getSignData") public RestResponse getSignData(Integer year, Integer month) throws Exception{ LocalDate selectLocalDate = LocalDate.of(year,month,1); / / query have fewer days in int lengthMonth = selectLocalDate. LengthOfMonth (); UserId = 123456L; // userId = 123456L; String userKey = "userSign_"+ Long.valueOf(userId)+"_"+String.valueOf(year) +String.valueOf(month); long mask = 0b1; List<Long> signList = (List<Long>) redistemplate. execute((RedisCallback<List<Long>>) con -> con.bitField(userKey.getBytes(), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(lengthMonth+1)).valueAt(0))); List<Long> userSignList = new ArrayList<>(); if (! CollectionUtils.isEmpty(signList)) { long sign = signList.get(0) == null ? 0 : signList.get(0); for (int i = selectLocalDate.lengthOfMonth(); i > 0; Usersignlist.add ((sign & mask)); // sign >>= 1; }} // reverse list collections.reverse (userSignList); System.out.println(userSignList); Return new RestResponse(0," Obtained successfully ",userSignList); }Copy the code
C. Have you checked in today
@apiOperation (" check in today ") @getMapping (value = "/checkSign") public RestResponse checkSign() throws Exception{ UserId = 123456L; // userId = 123456L; LocalDate localDate = LocalDate.now(); // Monthly sign-in key String userKey = "userSign_"+ long.valueof (userId)+"_"+ String.valueof (localDate.getYear()) + localDate.getMonth().getValue(); Int date = localdate.getDayofMonth (); / / users monthly check-in set a Boolean result. = redisTemplate opsForValue () getBit (userKey, new Long (date)); If (result){return new RestResponse(0," check in today "); }else{return new RestResponse(0," not checked in today "); }}Copy the code
D. The total check-in times of the user in that month
@APIOperation (" Total check-in times of the month ") @getMapping (value = "/countSignTimes") public RestResponse countSignTimes() throws Exception{ UserId = 123456L; // userId = 123456L; LocalDate localDate = LocalDate.now(); // Monthly sign-in key String userKey = "userSign_"+ long.valueof (userId)+"_"+ String.valueof (localDate.getYear()) + localDate.getMonth().getValue(); Long count = (Long) redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount(userKey.getBytes())); Return new RestResponse(0," Checked in "+ string.valueof (count)+" times "); }Copy the code

Background Function interface

A. Count how many users check in every day
@apiOperation (" Count how many users check in every day ") @getMapping (value = "/countAllUserSignTimes") public RestResponse countAllUserSignTimes(Integer year, Integer month,Integer day) throws Exception{// User ID. Normally, we need to obtain LocalDate from token or session. LocalDate = localdate.now (); String signKey = "userSign_"+ String.valueof (localdate.getYear ()) + localDate.getMonth().getValue() + localDate.getDayOfMonth(); Long count = (Long) redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount(signKey.getBytes())); Return new RestResponse(0," this day has "+ string.valueof (count)+" User check-in "); }Copy the code

4, test,

Here’s a simple test of the functionality using the tool Postman, from top to bottom

(1) User check-in

(2) Obtain monthly check-in data of users

(3) Whether you have checked in today

(4) Check-in times of the month

(5) Count how many users check in every day

(2) To achieve user activity

If necessary, you can comment and add later

(3) Record the landing status

If necessary, you can comment and add later