Querying Large Bitmaps by Page
To query large bitmaps or sub-bitmaps in the specified range [start, end], traditional solutions have the following bottlenecks:
- Low-efficiency data appending: If the client obtains data bit by bit via GETBIT in a loop and appends the data, high-frequency requests and computing overheads are generated, resulting in significant performance bottlenecks.
- Big key access risks: If GET is used to obtain a complete bitmap, databases need to transmit ultra-large binary data chunks. Network congestion will occur, leading to server delay jitter that could impact overall system stability.
To solve this problem, RANGEBITARRAY can be executed on databases. This enhanced bitmap command has the following advantages:
- Efficient query by block: You can obtain binary data chunks in a given range by specifying range parameters. The client can obtain and assemble a large data set by running RANGEBITARRAY multiple times.
- Avoiding big keys: Data is transferred by chunk to prevent a single operation from reaching the performance threshold of big keys and to effectively reduce server delay jitter.
- End-to-end performance optimization: RTT and server resource consumption are reduced to improve the query throughput and response stability.
You are advised to run RANGEBITARRAY to obtain massive bitmaps.
Type |
Description |
---|---|
Syntax |
RANGEBITARRAY key start end |
Time Complexity |
O(C): C indicates the length of [start, end]. |
Description |
Obtains the character string consisting of all bit values (0 and 1) in a specified range of a bitmap. |
Value |
|
Returned Value |
|
Example |
Run the following command to preset data: SETBIT foo 2 1 SETBIT foo 3 1 SETBIT foo 5 1
RANGEBITARRAY foo 0 5
"001101" |
Usage Notes
- To check whether the instance version supports this function, you can choose Service Tickets > Create Service Ticket in the upper right corner of the console and contact the customer service. To use this function, upgrade the kernel by following Upgrading a Minor Version.
- The key must be of the bitmap type. If the key is of the string type, you need to run GETBIT to convert the key.
- The semantics of start and end are the same as that of GETRANGE.
- If the client needs to run RANGEBITARRAY for multiple times and append the results, you can run PIPELINE to accelerate the execution.
- You can select the range query granularity. A finer granularity indicates a smaller delay glitch. A coarser granularity indicates a larger delay glitch. Generally, a range query granularity of 32 KB is recommended for appending the results.
Code Reference
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.Protocol; import java.nio.charset.StandardCharsets; public class BitmapRangePager { private final JedisPool jedisPool; private static final int PAGE_SIZE_BYTES = 32 * 1024; // 32 KB per page (in bytes) public BitmapRangePager(JedisPool jedisPool) { this.jedisPool = jedisPool; } public String getFullBitmap(String key) { try (Jedis jedis = jedisPool.getResource()) { // Obtains the total number of bytes in the bitmap. long totalBytes = jedis.strlen(key); if (totalBytes == 0) return ""; StringBuilder result = new StringBuilder(); // Querying data page by page (in bytes) (more intuitive) for (long byteOffset = 0; byteOffset < totalBytes; byteOffset += PAGE_SIZE_BYTES) { // Calculates the number of bytes on the current page (the size of the last page may be less than 32 KB). int currentPageBytes = (int) Math.min(PAGE_SIZE_BYTES, totalBytes - byteOffset); // Converts to a bit interval (closed interval). long bitStart = byteOffset * 8; long bitEnd = (byteOffset + currentPageBytes) * 8 - 1; // Includes the end bit. // Runs the command and appends the results. String pageBits = executeRangeBitArray(jedis, key, bitStart, bitEnd); result.append(pageBits); } return result.toString(); } } private String executeRangeBitArray(Jedis jedis, String key, long start, long end) { // Runs RANGEBITARRAY key start end. Object response = jedis.sendCommand( new Protocol.Command("RANGEBITARRAY"), SafeEncoder.encode(key), Protocol.toByteArray(start), Protocol.toByteArray(end) ); // Processes the response (ensures that the binary data is converted to a character string). if (response instanceof byte[]) { return new String((byte[]) response, StandardCharsets.US_ASCII); } else { return response.toString(); } } }
import redis import math class BitmapRangePager: def __init__(self, redis_client): self.redis = redis_client self.PAGE_SIZE_BYTES = 32 * 1024 # 32 KB per page (in bytes) def get_full_bitmap(self, key): """ Obtains a complete bitmap character string. :param key: Redis key name :return: a complete character string consisting of 0 and 1 """ # Obtains the total number of bytes in a bitmap. total_bytes = self.redis.strlen(key) if total_bytes == 0: return "" result = [] # Querying data page by page (in bytes) for byte_offset in range(0, total_bytes, self.PAGE_SIZE_BYTES): Calculates the number of bytes on the current page (the size of the last page may be less than 32 KB). current_page_bytes = min(self.PAGE_SIZE_BYTES, total_bytes - byte_offset) # Converts to a bit interval [start, end]. bit_start = byte_offset * 8 bit_end = (byte_offset + current_page_bytes) * 8 - 1 # Runs the command and collects the results. page_bits = self.execute_range_bitarray(key, bit_start, bit_end) result.append(page_bits) return ''.join(result) def execute_range_bitarray(self, key, start, end): """ Runs RANGEBITARRAY key start end. :return: a character string consisting of 0 and 1 """ # Runs a custom command supported by the server. response = self.redis.execute_command( "RANGEBITARRAY", # The actual command name needs to be adjusted. key, start, end ) # Processes the response (bytes returned by redis-py). return response.decode('ascii') # Assumes that an ASCII character string is returned. # Example if __name__ == "__main__": # Creates a Redis client. r = redis.Redis(host='localhost', port=6379, decode_responses=False) # Keeps the original returned bytes. pager = BitmapRangePager(r) full_bitmap = pager.get_full_bitmap("my_large_bitmap") # Output example (only the first 100 characters are generated) print(f"Bitmap (first 100 chars): {full_bitmap[:100]}") print(f"Total length: {len(full_bitmap)} bits")
Feedback
Was this page helpful?
Provide feedbackThank you very much for your feedback. We will continue working to improve the documentation.See the reply and handling status in My Cloud VOC.
For any further questions, feel free to contact us through the chatbot.
Chatbot