问题背景

在我们测试平台中,有很多测试手机,对于某些手机,我们预计它只能跑特定的任务,或者某个手机会有一些特定的状态,如充电中,人工操作,手机清理中,当手机处于不同的状态会有一些不同的处理动作,此时我们会有哪些解决方案呢?

首先想到的应该是打标签,在数据库中新创建一个字段,比如叫 tag,修改某个手机的状态,就修改一下这个 tag 字段。

但是这里有一个问题,如果某个手机拥有两个或者更多的标签,比如该手机正在充电,并且人工操作,那个我这个标签应该设置为多少? 如果在 mysql 中可以将该字段设置为charging,oping, 但是我们进行搜索的时候就有些麻烦了,比如我要过滤出正在充电的手机,sql 语句大概是这样的

select * from db_phone where tag like '%charging%'

即使这样可以查询到所有正在充电的手机,但是之后如果想要再进行一些排序等操作就没法进行了。

更麻烦的是更新状态,如果手机不充电了,那么要将 charging 从 tag 中移除,然后再把别的状态重新写入。

还有一种解决方法是在数据库中添加字段,但是这样的操作,每添加一个 tag 就要修改数据库,多添加一个字段,以后如果标签越来越多,字段也会越来越多,对于之前的系统稳定性也有有一些影响。

此时我们可以考虑使用 bitmap。

bitmap 简介

我们知道,数字是可以转换为二进制的,如 3 的二进制是 11, 4 是 100,bitmap 主要利用了二进制的运算,按位与、按位或操作

 0 0 1    0 0 1
&0 0 1   |0 0 1
=0 0 1   =0 0 1

我们可以将相应的位设定为对应的标识,如第一位表示是否是正在充电的手机,第二位表示为是否正在人工操作,1 为是,0 为否。

这样我们就可以通过这些二进制的数字表示手机的标签信息,如001 表示手机正在充电,010 表示手机正在进行人工操作,011 表示手机正在充电并且人工操作。

数据的查询

当我们想要查找所有手机中正在充电的手机,我们可以使用按位与运算,查找 tag 字段和001 进行与运算以后等于001的数据。

假如数据库中有以下几条数据

id devices tag
1 a,b 001
2 2,3,5 010
3 all 101
4 6,7 110

那么使用

select * from db_test where tag & 001 = 001

将会把 tag 中最后位为 1 的数据全部过滤出来即上表中的 id 为 1 和 3 的数据。

更新数据

当需要更新数据的时候,可以使用 sql 语句直接进行更新, 如

update db_test set tag=b'111' where id=1

但是更多的情况下,你是不知道三个字段的值,你只想更新其中某一位为 0 或者 1,又分为两种情况

  1. 写 1 值, 可以利用任何值和 0 做或运算,不改变原来的值,和 1 做或运算,都将变为 1 的特性进行操作,如更新某个手机为充电状态,即更新末位为 1,只需要和001进行或运算。
update db_test set tag=tag|001 where id=1
  1. 写 0 值,可以利用任何值与 1 做与运算,都不改变原值,和 0 做与运算都将变为 0 的特性,如我想解除某个手机的充电状态,即更新末位为 0,只需要和110进行与运算即可
update db_test set tag=tag&110 where id=1

bitmap 的应用

之前有一个需求,测试系统中有很多个虚拟机,如 win7, win8, win10, 不同的虚拟机内所安装的组件不同,有的安装 office, 有的既安装了 office 也安装的 360 浏览器,此时,如果有一个任务,需要在有安装 office 的虚拟机中运行,那么作为任务的派发管理,应该优先将任务分到哪台虚拟机呢?

很显然,需要优先将任务分配到只安装了 office 的虚拟机中,因为如果将任务分配到既安装 office 又安装了 360 浏览器的虚拟机中,那么如果之后有一个需要在 360 浏览器环境的系统中运行,那么此时就会因为没有可用的系统来造成资源的浪费。

此时我们就可以使用 bitmap 来解决这个问题,先将安装了 office 的所有虚拟机过滤出来,再根据 tag 值,选择一个分值最小的进行分配

SELECT * from db_test WHERE tag&001=001
ORDER BY tag ASC

这样按照升序进行排列,派发的时候选择分值最小的进行任务分配。

bitmap 的局限

使用 bitmap 的字段只有 0 和 1,对应的状态就是 或者 ,如果你的状态有多种,比如说手机电量,如果状态有充满,良好,不足,极低等状态则不能使用 bitmap,在 mysql 中 bitmap 最多只有 64 位。

并且使用 bitmap 要写好文档,否则项目维护人员根本不知道哪个位表示的是什么意思。


↙↙↙阅读原文可查看相关链接,并与作者交流