<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          位圖法在mongodb中的應(yīng)用

          共 15064字,需瀏覽 31分鐘

           ·

          2022-11-29 11:22


          JAVA前線 


          歡迎大家關(guān)注公眾號「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)




          1 需求背景

          假設(shè)系統(tǒng)用戶一共有三種角色:普通用戶、管理員、超級管理員,現(xiàn)在需要設(shè)計一張用戶角色表記錄這類信息。我們不難設(shè)計出如下方案:


          idnamesuperadminnormal
          101用戶一100
          102用戶二010
          103用戶三001
          104用戶四111

          用戶一具有超級管理員角色,用戶二具有管理員角色,用戶三具有普通用戶角色,用戶四同時具有三種角色。


          2 發(fā)現(xiàn)問題

          如果新增加一種角色呢?可以新增一個字段:


          idnamesuperadminnormalnew
          101用戶一1000
          102用戶二0100
          103用戶三0010
          104用戶四1110

          按照上述一個字段表示一種角色設(shè)計表,功能沒有問題,優(yōu)點是容易理解結(jié)構(gòu)清晰,但是我們想一想有沒有什么問題?筆者遇到過如下問題:

          在復(fù)雜業(yè)務(wù)環(huán)境一份數(shù)據(jù)可能會使用在不同場景,例如上述數(shù)據(jù)存儲在MySQL數(shù)據(jù)庫,這一份數(shù)據(jù)還會被用在如下場景:

          • 檢索數(shù)據(jù)需要同步一份到ES
          • 使用此表通過Flink計算業(yè)務(wù)指標(biāo)
          • 訂閱此表Binlog消息進(jìn)行業(yè)務(wù)處理

          如果表結(jié)構(gòu)發(fā)生變化,數(shù)據(jù)源之間需要重新對接,業(yè)務(wù)方也要進(jìn)行代碼修改,這樣開發(fā)成本非常高。有沒有辦法避免此類問題?


          3 解決方案

          我們可以使用位圖法,同一個字段可以表示多個業(yè)務(wù)含義。先設(shè)計如下數(shù)據(jù)表,userFlag字段暫時不填:


          idnameuser_flag
          101用戶一暫時不填
          102用戶二暫時不填
          103用戶三暫時不填
          104用戶四暫時不填

          位圖每一個bit表示一種角色:

          使用位圖法表示如下數(shù)據(jù):

          idnamesuperadminnormal
          101用戶一100
          102用戶二010
          103用戶三001
          104用戶四111

          用戶一位圖如下,十進(jìn)制數(shù)值等于4:


          用戶二位圖如下,十進(jìn)制數(shù)值等于2:


          用戶三位圖如下,十進(jìn)制數(shù)值等于1:


          用戶四位圖如下,十進(jìn)制數(shù)值等于7:


          現(xiàn)在可以填寫數(shù)據(jù)表第三列:


          idnameuser_flag
          101用戶一4
          102用戶二2
          103用戶三1
          104用戶四7

          4 代碼實例

          本文結(jié)合mongodb實現(xiàn)思路有兩種:

          • 方案一:取出二進(jìn)制字段在應(yīng)用層運算
          • 方案二:在數(shù)據(jù)層直接運算二進(jìn)制字段

          4.1 用戶實體

          用戶實體對應(yīng)數(shù)據(jù)表user:

          @Document(collection = "user")
          public class User {

           @Id
           @Field("_id")
           private String id;

           @Field("userId")
           private String userId;

           @Field("role")
           private Long role;
          }

          4.2 用戶角色

          定義枚舉時不要直接定義為1、2、4這類數(shù)字,應(yīng)該采用位移方式進(jìn)行定義,這樣使用者可以明白設(shè)計者的意圖。

          public enum UserRoleEnum {

              // 1 -> 00000001
              NORMAL(1L << 0"普通用戶"),

              // 2 -> 00000010
              MANAGER(1L << 1"管理員"),

              // 4 -> 00000100
              SUPER(1L << 2"超級管理員"),

              ;

              private Long code;
              private String description;

              private UserRoleEnum(Long code, String description) {
                  this.code = code;
                  this.description = description;
              }

              // 新增角色 -> 位或操作
              // oldRole -> 00000001 -> 普通用戶角色
              // addRole -> 00000010 -> 新增管理員角色
              // newRole -> 00000011 -> 具有普通用戶和管理員角色
              public static Long addRole(Long oldRole, Long addRole) {
                  return oldRole | addRole;
              }

              // 刪除角色 -> 異或操作
              // oldRole -> 00000011 -> 普通用戶和管理員角色
              // delRole -> 00000010 -> 刪除管理員角色
              // newRole -> 00000001 -> 普通用戶角色
              public static Long removeRole(Long oldRole, Long delRole) {
                  return oldRole ^ delRole;
              }

              // 是否具有某種角色 -> 位與操作
              // allRole -> 00000011 -> 普通用戶和管理員角色
              // qryRole -> 00000001 -> 查詢是否具有管理員角色
              // resRole -> 00000001 -> 具有管理員角色
              public static boolean hasRole(Long role, Long queryRole) {
                  Long resRole = (role & queryRole);
                  return queryRole == resRole;
              }
          }

          4.3 數(shù)據(jù)準(zhǔn)備

          新增用戶一到用戶四:

          db.user.insertMany([
              {
                  "userId": "user1",
                  "role": NumberLong(4)
              },
              {
                  "userId": "user2",
                  "role": NumberLong(2)
              },
              {
                  "userId": "user3",
                  "role": NumberLong(1)
              },
              {
                  "userId": "user4",
                  "role": NumberLong(7)
              }
          ])

          4.4 應(yīng)用層運算

          應(yīng)用層運算有三個關(guān)鍵步驟:

          • 查詢用戶角色
          • 內(nèi)存計算新角色
          • 更新數(shù)據(jù)庫

          @Service
          public class UserBizService {

              @Resource
              private MongoTemplate mongoTemplate;

              // 查詢用戶
              public User getUser(String userId) {
                  Query query = new Query();
                  Criteria criteria = Criteria.where("userId").is(userId);
                  query.addCriteria(criteria);
                  User user = mongoTemplate.findOne(query, User.class);
                  return user;
              }

              // 新增角色
              public boolean addRole(String userId, Long addRole) {

                  // 查詢用戶角色
                  User user = getUser(userId);

                  // 計算新角色
                  Long finalRole = UserRoleEnum.addRole(user.getRole(), addRole);

                  // 更新數(shù)據(jù)庫
                  Query query = new Query();
                  Criteria criteria = Criteria.where("userId").is(userId);
                  query.addCriteria(criteria);
                  Update update = new Update();
                  update.set("role", finalRole);
                  mongoTemplate.updateFirst(query, update, User.class);
                  return true;
              }

              // 刪除角色
              public boolean removeRole(String userId, Long delRole) {

                  // 查詢用戶角色
                  User user = getUser(userId);

                  // 計算新角色
                  Long finalRole = UserRoleEnum.removeRole(user.getRole(), delRole);

                  // 更新數(shù)據(jù)庫
                  Query query = new Query();
                  Criteria criteria = Criteria.where("userId").is(userId);
                  query.addCriteria(criteria);
                  Update update = new Update();
                  update.set("role", finalRole);
                  mongoTemplate.updateFirst(query, update, User.class);
                  return true;
              }

              // 查詢用戶是否具有某種角色
              public boolean queryRole(String userId, Long queryRole) {

                  // 查詢用戶角色
                  User user = getUser(userId);

                  // 計算是否具有某種角色
                  return UserRoleEnum.hasRole(user.getRole(), queryRole);
              }
          }

          4.5 數(shù)據(jù)層運算

          4.5.1 位運算操作符

          (1) 查詢操作符

          操作符含義
          bitsAllClear指定二進(jìn)制位全為0
          bitsAllSet指定二進(jìn)制位全為1
          bitsAnyClear任意指定二進(jìn)制位為0
          bitsAnySet任意指定二進(jìn)制位為1

          下列語句可以查出用戶四:


          • 0-2三個位置全部等于1
          db.user.find({
              "role": {
                  $bitsAllSet: [0, 1, 2]
              }
          })

          • 0-2任意一個位置等于1
          db.user.find({
              "role": {
                  $bitsAnySet: [0, 1, 2]
              }
          })

          • 3-7位置全部等于0
          db.user.find({
              "role": {
                  $bitsAllClear: [3, 4, 5, 6, 7]
              }
          })

          • 3-7位置任意等于0
          db.user.find({
              "role": {
                  $bitsAnyClear: [3, 4, 5, 6, 7]
              }
          })

          (2) 計算操作符

          操作符含義操作
          and位與查詢角色
          or位或新增角色
          xor位異或刪除角色

          • user3新增超級管理員角色
          db.user.update({
              "userId": "user3"
          }, {
              $bit: {
                  "role": {
                      or: NumberLong(4)
                  }
              }
          })

          • user4刪除普通用戶角色
          db.user.update({
              "userId": "user4"
          }, {
              $bit: {
                  "role": {
                      xor: NumberLong(1)
                  }
              }
          })

          4.5.2 代碼實例

          @Service
          public class UserBizService {

              /*
               * 新增角色
               */

              public boolean addRoleBit(String userId, Long addRole) {
                  Query query = new Query();
                  Criteria criteria = Criteria.where("userId").is(userId);
                  query.addCriteria(criteria);
                  Update update = new Update();
                  update.bitwise("role").or(addRole);
                  mongoTemplate.updateFirst(query, update, User.class);
                  return true;
              }

              /**
               * 刪除角色
               */

              public boolean removeRoleBit(String userId, Long addRole) {
                  Query query = new Query();
                  Criteria criteria = Criteria.where("userId").is(userId);
                  query.addCriteria(criteria);
                  Update update = new Update();
                  update.bitwise("role").xor(addRole);
                  mongoTemplate.updateFirst(query, update, User.class);
                  return true;
              }

              /**
               * 查詢rolePosition位置全部等于0的用戶
               *
               * 表示不具有rolePositions中所有角色的用戶
               */

              public List<User> queryRoleAllClear(List<Integer> rolePositions) {
                  Criteria criteria = Criteria.where("role").bits().allClear(rolePositions);
                  List<User> users = mongoTemplate.query(User.class).matching(criteria).all();
                  return users;
              }

              /**
               * 查詢rolePosition位置任一等于0的用戶
               *
               * 表示不具有rolePositions中任一角色的用戶
               */

              public List<User> queryRoleAnyClear(List<Integer> rolePositions) {
                  Criteria criteria = Criteria.where("role").bits().anyClear(rolePositions);
                  List<User> users = mongoTemplate.query(User.class).matching(criteria).all();
                  return users;
              }

              /**
               * 查詢rolePosition位置全部等于1的用戶
               *
               * 表示具有rolePositions中所有角色的用戶
               */

              public List<User> queryRoleAllSet(List<Integer> rolePositions) {
                  Criteria criteria = Criteria.where("role").bits().allSet(rolePositions);
                  List<User> users = mongoTemplate.query(User.class).matching(criteria).all();
                  return users;
              }

              /**
               * 查詢rolePosition位置任一等于1的用戶
               *
               * 表示具有rolePositions中任一角色的用戶
               */

              public List<User> queryRoleAnySet(List<Integer> rolePositions) {
                  Criteria criteria = Criteria.where("role").bits().anySet(rolePositions);
                  List<User> users = mongoTemplate.query(User.class).matching(criteria).all();
                  return users;
              }
          }

          5 文章總結(jié)

          本文我們從一個簡單案例開始,分析了直接新增字段的優(yōu)缺點。新增字段方案遇到最多問題就是在復(fù)雜業(yè)務(wù)場景中,需要新增數(shù)據(jù)對接工作量,增加了開發(fā)維護(hù)成本。

          我們又介紹了位圖法,一個字段就可以表示多種業(yè)務(wù)含義,減少了字段冗余,節(jié)省了對接開發(fā)成本。同時位圖法增加了代碼理解成本,數(shù)據(jù)庫字段含義不直觀,需要進(jìn)行轉(zhuǎn)義,大家可以根據(jù)業(yè)務(wù)需求場景選擇。



          JAVA前線 


          歡迎大家關(guān)注公眾號「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)


          瀏覽 49
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人黄色电影免费在线观看 | 夜夜操网址 | 亚洲 日韩 欧美 国产 | 嫩草91| 操逼啦|