Mysql数据库表字段设计优化(状态列)
⼀、传统⽤户状态设置
传统的数据库表中,涉及到状态的字段时,通常都会第⼀反应就是将其设置为0和1来表⽰。⽐如需求是,设计⼀张表来检查⽤户状态(绑定邮箱,绑定⼿机,实名认证,是否已经开通VIP),我以前会这样设计Java类。jdk怎么使用
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数组?
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)
以上只是个⼈在学习过程中的⼀些⼩总结,如有不当之处,还望海涵,希望前辈们多多指教。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论