Mysql数据库表字段设计优化(状态列)

一、传统用户状态设置

    传统的数据库表中,涉及到状态的字段时,通常都会第一反应就是将其设置为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系统中,使用的正是状态码,这里就不展开细讲了,有兴趣的朋友可以移步这里查看

https://blog.csdn.net/qq_36221862/article/details/56012469

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

    这里提供几种解决方案:

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

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

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

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


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

<div class="post-text" itemprop="text"> <p>I'm currently looking at the code I've produced and I'm trying to improve its readability and quality. Here's an example of how I'd approach a simple problem, which I've always felt was a poor solution.</p> <p>Users table with the below "statuses", or elevation levels in this example</p> <ol> <li>Employee</li> <li>Supervisor</li> <li>Admin</li> </ol> <p>(these could be anything, for example, a project could have an "incomplete", "on hold" and "complete" status)</p> <p>In the past, I've always stored this as an INT value then when printing out the accounts and their statuses, I will write a function to convert this back to text, to be printed on a page e.g.</p> <p><code>if userLevel === 3 { return "Admin"; };</code></p> <p>This has always felt like a bad solution, and it's not very flexible. The addition of any new types of account would require the function to be updated. This format also makes it very difficult to read page restrictions.</p> <p>If I were to add the line:</p> <p><code>if userLevel !== 2 OR 3 { // Redirect to error page };</code></p> <p>You can see how this could become confusing down the line.</p> <p>Recently I've started storing these "statuses" as a string purely so it's more readable. But now I've been getting more into Laravel, I wonder if I should have additional database tables devoted solely to this.</p> <p>So my question - what is the appropriate data type for this kind of data, and what would be the ideal way to interact with it?</p> </div>
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页