
# 大Bitmap分页查询
针对大位图（Bitmap）或指定区间\[start, end\]子位图的获取场景，传统实现方案存在两大瓶颈：
- **低效数据拼接问题**：若客户端通过循环调用GETBIT逐位获取数据并拼接，会产生高频次网络请求与计算开销，存在显著的性能瓶颈。
- **大** **Key** **访问风险**：若直接使用GET获取完整位图，数据库层需传输超大二进制数据块，易引发网络阻塞及服务端延迟波动，影响系统稳定性。
为此，数据库侧提供增强型位图操作命令RANGEBITARRAY，其核心优势为：
- **分块高效查询**：支持通过范围参数直接获取位图指定区间的二进制数据块，客户端可通过多次调用完成大数据集的分段获取与组装。
- **规避大** **Key** **风险**：通过按需分块传输机制，避免单次操作触及大Key的性能临界点，有效降低服务端延迟抖动。
- **端到端性能优化**：减少网络往返次数与服务端资源消耗，实现查询吞吐量与响应稳定性的双重提升。
建议在海量位图数据读取场景优先使用此命令。
表1RANGEBITARRAY 
| **类别**    | **说明**                                                                                                                                                                                                                                                                                                                                                              |
|:---|:---|
| **语法**    | RANGEBITARRAY *key start end*                                                                                                                                                                                                                                                                                                                                       |
| **时间复杂度** | O(C)，C表示\[start, end\]区间长度。                                                                                                                                                                                                                                                                                                                                         |
| **命令描述**  | 获取Bitmap指定区间中所有bit值（0、1）组成的字符串。                                                                                                                                                                                                                                                                                                                                     |
| **选项**    | - **Key**：Key名称（Bitmap数据结构）。  - **start**：起始的偏移量（包含该值）。  - **end**：结束的偏移量（包含该值）。                                                      |
| **返回值**   | - 执行成功：返回指定区间中所有bit值（0、1）组成的字符串。  - 若key不存在：返回空字符串。  - 其他情况返回相应的异常信息（如"不支持老编码"和"Key必须为Bitmap类型"）。                                             |
| **示例**    | 提前执行如下命令预置数据： ``` SETBIT foo 2 1 SETBIT foo 3 1 SETBIT foo 5 1 ``` - 命令示例：   ``` RANGEBITARRAY foo 0 5 ``` - 返回示例：   ``` "001101" ``` |
   
#### 注意事项
- 您可以在管理控制台右上角，选择"[工单 \> 新建工单](https://console.huaweicloud.com/ticket/#/ticketindex/createIndex)"联系客服咨询实例版本是否支持该功能。如需使用该功能，您可以参考[升级GeminiDB Redis内核小版本](https://support.huaweicloud.com/redisug-nosql/nosql_02_0732.html)升级内核版本。
- Key必须是Bitmap类型，STRING类型需先用GETBIT命令进行转换。
- start和end的语义与GETRANGE命令相同。
- 客户端如果需要多次执行RANGEBITARRAY并进行拼接，可以使用PIPELINE模式加速。
- 范围查询的粒度可以根据实际情况选择，粒度越细，产生的时延毛刺就越小；粒度越粗，产生的时延毛刺就越大，通常建议32KB的范围查询粒度，再进行拼接。
 
#### 代码参考
- [Java（jedis）示例]
  ```
  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; // 32KB分页（基于字节单位）
      public BitmapRangePager(JedisPool jedisPool) {
          this.jedisPool = jedisPool;
      }
      public String getFullBitmap(String key) {
          try (Jedis jedis = jedisPool.getResource()) {
              // 获取Bitmap总字节长度
              long totalBytes = jedis.strlen(key);
              if (totalBytes == 0) return "";
              StringBuilder result = new StringBuilder();
              // 基于字节的分页循环（更直观）
              for (long byteOffset = 0; byteOffset < totalBytes; byteOffset += PAGE_SIZE_BYTES) {
                  // 计算当前页的字节数（最后一页可能不足32KB）
                  int currentPageBytes = (int) Math.min(PAGE_SIZE_BYTES, totalBytes - byteOffset);
                  
                  // 转换为bit区间（闭区间）
                  long bitStart = byteOffset * 8;
                  long bitEnd = (byteOffset + currentPageBytes) * 8 - 1; // 包含end位
                  
                  // 执行命令并拼接结果
                  String pageBits = executeRangeBitArray(jedis, key, bitStart, bitEnd);
                  result.append(pageBits);
              }
              return result.toString();
          }
      }
      private String executeRangeBitArray(Jedis jedis, String key, long start, long end) {
          // 发送自定义命令RANGEBITARRAY key start end
          Object response = jedis.sendCommand(
                  new Protocol.Command("RANGEBITARRAY"),
                  SafeEncoder.encode(key),
                  Protocol.toByteArray(start),
                  Protocol.toByteArray(end)
          );
          // 处理响应（确保二进制转字符串）
          if (response instanceof byte[]) {
              return new String((byte[]) response, StandardCharsets.US_ASCII);
          } else {
              return response.toString();
          }
      }
  }
  ```
- [Python示例]
  ```
  import redis
  import math
  class BitmapRangePager:
      def __init__(self, redis_client):
          self.redis = redis_client
          self.PAGE_SIZE_BYTES = 32 * 1024  # 32KB 分页（基于字节单位）
      def get_full_bitmap(self, key):
          """
          获取完整的 Bitmap 字符串
          :param key: Redis 键名
          :return: 由 '0'/'1' 组成的完整字符串
          """
          # 获取 Bitmap 总字节数
          total_bytes = self.redis.strlen(key)
          if total_bytes == 0:
              return ""
          result = []
          # 基于字节的分页循环
          for byte_offset in range(0, total_bytes, self.PAGE_SIZE_BYTES):
              # 计算当前页的字节数（最后一页可能不足 32KB）
              current_page_bytes = min(self.PAGE_SIZE_BYTES, total_bytes - byte_offset)
              
              # 转换为 bit 区间闭区间 [start, end]
              bit_start = byte_offset * 8
              bit_end = (byte_offset + current_page_bytes) * 8 - 1
              
              # 执行命令并收集结果
              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):
          """
          执行自定义命令 RANGEBITARRAY key start end
          :return: 由 '0'/'1' 组成的字符串
          """
          # 发送自定义命令（需服务端支持）
          response = self.redis.execute_command(
              "RANGEBITARRAY",  # 注意实际命令名可能需要调整
              key,
              start,
              end
          )
          # 处理响应（redis-py 返回 bytes 类型）
          return response.decode('ascii')  # 假设响应是 ASCII 字符串
  # 使用示例
  if __name__ == "__main__":
      # 创建 Redis 客户端
      r = redis.Redis(host='localhost', port=6379, decode_responses=False)  # 保持原始 bytes 响应
      
      pager = BitmapRangePager(r)
      full_bitmap = pager.get_full_bitmap("my_large_bitmap")
      
      # 输出示例（仅打印前100字符）
      print(f"Bitmap (first 100 chars): {full_bitmap[:100]}")
      print(f"Total length: {len(full_bitmap)} bits")
  ```
