一、传统用户状态设置

    传统的数据库表中,涉及到状态的字段时,通常都会第一反应就是将其设置为0和1来表示。比如需求是,设计一张表来检查用户状态(绑定邮箱,绑定手机,实名认证,是否已经开通VIP),我以前会这样设计Java类。

UserInfo

@Getter
@Setter
public class UserInfo extends baseDomain{
  private boolean realAuth;
  private boolean bindPhone;
  private boolean bindEmail;
  private boolean vip;


}

如果是用0和1来表示,咋看起来挺合理的,但是仔细想想,是否真的能够满足我们的需求?(也不符合我们作为完美主义者的追求)


二、使用二进制来表示状态

    我们使用二进制来表示状态看看效果。首先给每一个状态指定分配一个二进制,如

boolean realAuth  0001;

boolean bindPhonge 0010;

boolean vip 1000;

boolean bindEmail 0100;

    那还有一个问题,就是在数据库中,使用int类型还是long类型?


诚然,初始状态码(java int 32 long 64),int 可以表示31种(除去0000),long可以表示63种(除去0000),当然不可能将0000赋值给初始状态,一般来讲,选择int还是long是根据具体业务需求来决定的。

为什么Java中的BitSet使用long数组做内部存储,而不使用int数组或者byte数组?

(链接:https://www.zhihu.com/question/21061816/answer/17047941

JDK选择long数组作为BitSet的内部存储结构是出于性能的考虑,因为BitSet提供and和or这种操作,

需要对两个BitSet中的所有bit位做and或者or,实现的时候需要遍历所有的数组元素。

使用long能够使得循环的次数降到最低,所以Java选择使用long数组作为BitSet的内部存储结构。

从数据在栈上的存储来说,使用long和byte基本是没有什么差别的,除了编译器强制地址对齐的时候,

使用byte最多会浪费7个字节(强制按照8的倍数做地址对其),另外从内存读数组元素的时候,

也是没有什么区别的,因为汇编指令有对不同长度数据的mov指令。所以说,

JDK选择使用long数组作为BitSet的内部存储结构的根本原因就是在and和or的时候减少循环次数,提高性能

既然使用二进制表示状态的改变情况,如何进行运算的,又是如何知道通过二进制来获取状态?来试试状态改变时,二进制的运算是怎么样的。

    首先,假设用户已经绑定手机号码(响应国家要求,大多数网站注册时都已经需要绑定手机),绑定手机之后的状态码为:0010,这时用户申请开通了vip,

则 0010 || 1000  = 1010 

只需要判断标志位是否为1即可,所以我们可以得到一个约定:

① 结合当前状态码添加一个状态码:当前状态码 || 要添加的状态码

② 判断当前用户是否拥有某种状态:当前状态码 || 要判断的状态码(如果等于0则说明没有,如果>0则有)

Eg:

demo: 1010 & 0010 得到 0010 > 0

       

 判断是否绑定邮箱:


1010 & 0100 得到 0000

③ 给当前用户去除某种状态(例如用户取消了VIP):当前状态码 ^ 要移除的状态码(相同为0,不同为1)

Eg:

    移除邮箱

1010 ^ 1000 得到 0010


三、使用二进制方式表示状态码的利弊权衡

通过上述的例子,我们可以使用一个属性值表示多个状态。非常经典的页很常见的Linux系统中,使用的正是状态码,这里就不展开细讲了,有兴趣的朋友可以移步这里查看


    但坏处也是非常明显的,即有上限,查询复杂(SQL语句中也需要使用 || ^ & 来判断),例如,查询系统所有的vip会员,无法使用一条查询语句(只能使用判断状态码的方式)。还有一个很麻烦的情况就是,只要数据库中的某一列参与了运算,该列上添加的索引(记录的是列的原始值)就会失效。

    这里提供几种解决方案:

    ① 把经常参与查询的条件单独列成条件(列)

    ② 给列添加索引的时候注意,哪些列适合进行索引(区分度不高的列不适合,比如一个列是性别(只有男或者女),那不适合做索引,因为只能区分出一半的人)。

    ③ 在组合查询中,将运算放置在过滤条件最后

    ④ 使用全文检索(solr || Lucene)


以上只是个人在学习过程中的一些小总结,如有不当之处,还望海涵,希望前辈们多多指教。