前言:
BitSet是Redis中提供的一种二进制数组的数据结构, 利用这种特殊的数据结构可以实现一些特定的场景.
BitSet的数据长这样: [01010010], 存放的是boolean, 且长度为8的倍数 也就是byte的长度. 且只存true, false会自动填充.
Redisson提供的RBitSet还额外提供了 与 或 非 等api.
场景:
现需要记录一个24小时的状态图, 状态可以是: 打开/关闭; 在家/离家; 等等
每15分钟记一次状态, 也可以是10分钟, 30分钟
模型:
value格式:00010100,以每15分钟的统计结果作为1bit
不足8位会自动补0
写入和读取分离, 缓存实时维护, 查询也只查缓存, 不查数据源
比如说 00:16 和 01:27 检测到状态为打开, 其余时间段为关闭
那么预期的二进制数据为: 01000100
redisson提供了相关的api:
RBitSet bitSet = redissonClient.getBitSet(key);
bitSet.set(index);
bitSet.expire(7, TimeUnit.DAYS);
对应的数组索引为1和6 , 这里就是set(1), set(6). 0(false)会自动填充
BitSet工具类:
/**
* Redis位图工具类
* @author Lynn
* @date 2023/3/13 19:38
*/
@Component
public class BitMapUtil {
private static RedissonClient redissonClient;
@Autowired
public void setRedissonClient(RedissonClient redissonClient) {
BitMapUtil.redissonClient = redissonClient;
}
private static DeviceConfigService deviceConfigService;
@Autowired
public void setDeviceConfigService(DeviceConfigService deviceConfigService) {
BitMapUtil.deviceConfigService = deviceConfigService;
}
public static RBitSet getBitSet(String key) {
return redissonClient.getBitSet(key);
}
public static void mergeList(List<StatusBo> result, RBitSet bitSet, long i) {
if (i <= bitSet.size() - 1) {
if (bitSet.get(i)) {
StatusBo bo = StatusBo.builder()
.startTime(BitMapUtil.mapIndexToStartDate(i))
.endTime(BitMapUtil.mapIndexToEndDate(i))
.build();
result.add(bo);
while (i < bitSet.size() - 1 && bitSet.get(i + 1)) {
bo.setEndTime(BitMapUtil.mapIndexToEndDate(i + 1));
i++;
}
}
i++;
mergeList(result, bitSet, i);
}
}
private static Date mapIndexToStartDate(long index) {
Map<String, String> globalConfig =
deviceConfigService.queryBaseConfig(new DeviceConfigDto(), DeviceConfigDimensionEnum.GLOBAL.getCode());
String config = globalConfig.get(WHOLE_DAY_PROTECT_INTERVAL);
int step = StringUtils.isNoBlank(config) ? Integer.parseInt(config) / 60000 : 15;
int minute = (int) (index * step);
Date now = new Date();
return new Date(now.getYear(), now.getMonth(), now.getDate(), minute / 60, minute % 60, 0);
}
private static Date mapIndexToEndDate(long index) {
Map<String, String> globalConfig =
deviceConfigService.queryBaseConfig(new DeviceConfigDto(), DeviceConfigDimensionEnum.GLOBAL.getCode());
String config = globalConfig.get(WHOLE_DAY_PROTECT_INTERVAL);
int step = StringUtils.isNoBlank(config) ? Integer.parseInt(config) / 60000 : 15;
int minute = (int) ((index + 1) * step);
Date now = new Date();
return new Date(now.getYear(), now.getMonth(), now.getDate(), minute / 60 < 24 ? minute / 60 : 23,
minute / 60 < 24 ? minute % 60 : 59, 0);
}
public static long mapDateToIndex(Date date) {
Map<String, String> globalConfig =
deviceConfigService.queryBaseConfig(new DeviceConfigDto(), DeviceConfigDimensionEnum.GLOBAL.getCode());
String config = globalConfig.get(WHOLE_DAY_PROTECT_INTERVAL);
int step = StringUtils.isNoBlank(config) ? Integer.parseInt(config) / 60000 : 15;
return (date.getHours() * 60 + date.getMinutes()) / step;
}
}