<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>

          從橫向和縱向兩個(gè)維度尋求復(fù)雜問題的答案

          共 21610字,需瀏覽 44分鐘

           ·

          2021-08-30 05:55


          JAVA前線 


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



          1 多維度思維

          在知乎上看到了這個(gè)有意思的問題:一頭牛重800公斤,一座橋承重700公斤,牛應(yīng)該怎么過橋。初看題目我們不難得出兩個(gè)方案:橋梁加固、等待牛體重降至700公斤。

          這兩個(gè)方案顯然是正確的,但是我們不能就此止步。因?yàn)檫@類問題考察的是思維方法論,直接給出答案反而非最重要,對于這個(gè)問題我們可以從合理性、結(jié)構(gòu)化、可行性三個(gè)維度進(jìn)行分析。


          1.1 合理性分析

          一頭800公斤的牛要通過承重700公斤的橋,這個(gè)需求本身合理嗎?我們可以從必要性、緊迫性、替代性這三個(gè)維度提出三個(gè)問題:

          第一個(gè)問題問必要性:牛為什么要過橋,到底什么事情非要過橋不可

          第二個(gè)問題問緊迫性:如果非要過橋,那么這個(gè)過橋的需求是否緊急

          第三個(gè)問題問替代性:有沒有什么替代方案,是否可以坐船或者繞路走


          1.2 結(jié)構(gòu)化分析

          如果經(jīng)過討論結(jié)果是牛非過橋不可,那么我們再思考牛怎么過橋的方案。這里可以使用結(jié)構(gòu)化思維,將大問題拆分為小維度,盡量做到不遺漏和不重復(fù)。影響過橋的因素有這幾個(gè)維度:橋的維度、牛的維度、資源維度、環(huán)境維度。

          橋的維度:加固橋使承重大于800公斤

          牛的維度:等待牛的體重小于700公斤

          資源維度:使用一臺(tái)吊機(jī)把牛運(yùn)過去

          環(huán)境維度:取消環(huán)境重力


          1.3 可行性分析

          我們從橋的維度、牛的維度、資源維度、環(huán)境維度給出了方案,那么選擇哪個(gè)方案呢?這就需要我們進(jìn)行可行性評估,因時(shí)因地在資源制約下選擇當(dāng)前最合適的方案。

          加固橋方案經(jīng)濟(jì)成本較高,等待牛的體重小于700公斤時(shí)間成本較高,取消環(huán)境重力技術(shù)難度較高,所以使用一臺(tái)吊機(jī)把牛運(yùn)過去這個(gè)方案目前看來最合適。


          1.4 多維度思考

          經(jīng)過我們從合理性、結(jié)構(gòu)化、可行性三個(gè)維度梳理之后,雖然答案沒有什么新穎之處,但是思維過程很清晰,思考方法也可以應(yīng)用在其它問題。之所以思維過程清晰,是因?yàn)槲覀儧]有一上來直接給出答案,而是從多個(gè)維度對為題進(jìn)行了分析,所以增加維度可以使思考過程更清晰。


          2 縱向思維與橫向思維

          思考維度可以從多方面進(jìn)行充實(shí),其中最常見的是增加橫向和縱向兩個(gè)維度,本文也著重討論兩個(gè)維度??傮w而言,橫向擴(kuò)展的是思考廣度,縱向擴(kuò)展的是思考深度,而應(yīng)用在不同場景中細(xì)節(jié)又各有不同。


          2.1 時(shí)間管理四象限

          時(shí)間管理理論四象限法則根據(jù)重要和緊急兩個(gè)維度,建立了一個(gè)四象限坐標(biāo),可以幫助我們解決主次不分的問題。我們分配工作時(shí)間時(shí)可以結(jié)合四象限法則,重要且緊急的任務(wù)優(yōu)先級(jí)最高,而不要急于處理不重要且不緊急的任務(wù)。



          2.2 金字塔原理

          金字塔思維的核心思想并不復(fù)雜:一件事情可以總結(jié)出一個(gè)中心思想,這個(gè)中心思想可以由三至七個(gè)論點(diǎn)支持,每個(gè)論點(diǎn)再可以由三至七個(gè)論據(jù)支持,基本結(jié)構(gòu)如下圖:



          金字塔原理內(nèi)在結(jié)構(gòu)可以從縱向和橫向兩個(gè)維度分析,縱向結(jié)構(gòu)體現(xiàn)了結(jié)論先行和以上統(tǒng)下原則,橫向結(jié)構(gòu)體現(xiàn)了歸類分組和邏輯遞進(jìn)原則。關(guān)于金字塔原理詳細(xì)分析請參看我的文章:結(jié)構(gòu)化思維如何指導(dǎo)技術(shù)系統(tǒng)優(yōu)化。

          文章分析到這里,我們發(fā)現(xiàn)縱向和橫向思維有助于厘清思路和增加條理性,下面我們看看縱向和橫向思維怎樣幫助程序員處理復(fù)雜問題。


          3 架構(gòu)設(shè)計(jì)如何應(yīng)用縱橫思維

          我們分析一個(gè)創(chuàng)建訂單業(yè)務(wù)場景,當(dāng)前有ABC三種訂單類型,A類型訂單價(jià)格9折,物流最大重量不能超過8公斤,不支持退款。B類型訂單價(jià)格8折,物流最大重量不能超過5公斤,支持退款。C類型訂單價(jià)格7折,物流最大重量不能超過1公斤,支持退款。按照需求字面含義平鋪直敘地寫代碼也并不難:

          public class OrderServiceImpl implements OrderService {

              @Resource
              private OrderMapper orderMapper;

              @Override
              public void createOrder(OrderBO orderBO) {
                  if (null == orderBO) {
                      throw new RuntimeException("參數(shù)異常");
                  }
                  if (OrderTypeEnum.isNotValid(orderBO.getType())) {
                      throw new RuntimeException("參數(shù)異常");
                  }
                  // A類型訂單
                  if (OrderTypeEnum.A_TYPE.getCode().equals(orderBO.getType())) {
                      orderBO.setPrice(orderBO.getPrice() * 0.9);
                      if (orderBO.getWeight() > 9) {
                          throw new RuntimeException("超過物流最大重量");
                      }
                      orderBO.setRefundSupport(Boolean.FALSE);
                  }
                  // B類型訂單
                  else if (OrderTypeEnum.B_TYPE.getCode().equals(orderBO.getType())) {
                      orderBO.setPrice(orderBO.getPrice() * 0.8);
                      if (orderBO.getWeight() > 8) {
                          throw new RuntimeException("超過物流最大重量");
                      }
                      orderBO.setRefundSupport(Boolean.TRUE);
                  }
                  // C類型訂單
                  else if (OrderTypeEnum.C_TYPE.getCode().equals(orderBO.getType())) {
                      orderBO.setPrice(orderBO.getPrice() * 0.7);
                      if (orderBO.getWeight() > 7) {
                          throw new RuntimeException("超過物流最大重量");
                      }
                      orderBO.setRefundSupport(Boolean.TRUE);
                  }
                  // 保存數(shù)據(jù)
                  OrderDO orderDO = new OrderDO();
                  BeanUtils.copyProperties(orderBO, orderDO);
                  orderMapper.insert(orderDO);
              }
          }

          上述代碼從功能上完全可以實(shí)現(xiàn)業(yè)務(wù)需求,但是程序員不僅要滿足功能,還需要思考代碼的可維護(hù)性。如果新增一種訂單類型,或者新增一個(gè)訂單屬性處理邏輯,那么我們就要在上述邏輯中新增代碼,如果處理不慎就會(huì)影響原有邏輯。

          為了避免牽一發(fā)而動(dòng)全身這種情況,設(shè)計(jì)模式中的開閉原則要求我們面向新增開放,面向修改關(guān)閉,我認(rèn)為這是設(shè)計(jì)模式中最重要的一條原則:

          當(dāng)需求變化時(shí)通過擴(kuò)展而不是通過修改已有代碼來實(shí)現(xiàn)變化,這樣就保證代碼穩(wěn)定性。擴(kuò)展也不是隨意擴(kuò)展,因?yàn)槭孪榷x了算法,擴(kuò)展也是根據(jù)算法擴(kuò)展,用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。標(biāo)準(zhǔn)意義的二十三種設(shè)計(jì)模式說到底最終都是在遵循開閉原則

          如何改變平鋪直敘的思考方式?這就要為問題分析加上縱向和橫向兩個(gè)維度,我選擇使用分析矩陣方法,其中縱向表示策略,橫向表示場景。



          3.1 縱向做隔離

          縱向維度表示策略,不同策略在邏輯上和業(yè)務(wù)上應(yīng)該是隔離的,本實(shí)例包括優(yōu)惠策略、物流策略和退款策略,策略作為抽象,不同訂單類型去擴(kuò)展這個(gè)抽象,策略模式非常適合這種場景。

          3.1.1 優(yōu)惠策略

          // 優(yōu)惠策略
          public interface DiscountStrategy {
              public void discount(OrderBO orderBO);
          }

          // A類型訂單優(yōu)惠策略
          @Component
          public class TypeADiscountStrategy implements DiscountStrategy {

              @Override
              public void discount(OrderBO orderBO) {
                  orderBO.setPrice(orderBO.getPrice() * 0.9);
              }
          }

          // A類型訂單優(yōu)惠策略
          @Component
          public class TypeBDiscountStrategy implements DiscountStrategy {

              @Override
              public void discount(OrderBO orderBO) {
                  orderBO.setPrice(orderBO.getPrice() * 0.8);
              }
          }

          // A類型訂單優(yōu)惠策略
          @Component
          public class TypeCDiscountStrategy implements DiscountStrategy {

              @Override
              public void discount(OrderBO orderBO) {
                  orderBO.setPrice(orderBO.getPrice() * 0.7);
              }
          }

          // 優(yōu)惠策略工廠
          @Component
          public class DiscountStrategyFactory implements InitializingBean {
              private Map<String, DiscountStrategy> strategyMap = new HashMap<>();

              @Resource
              private TypeADiscountStrategy typeADiscountStrategy;
              @Resource
              private TypeBDiscountStrategy typeBDiscountStrategy;
              @Resource
              private TypeCDiscountStrategy typeCDiscountStrategy;

              public DiscountStrategy getStrategy(String type) {
                  return strategyMap.get(type);
              }

              @Override
              public void afterPropertiesSet() throws Exception {
                  strategyMap.put(OrderTypeEnum.A_TYPE.getCode(), typeADiscountStrategy);
                  strategyMap.put(OrderTypeEnum.B_TYPE.getCode(), typeBDiscountStrategy);
                  strategyMap.put(OrderTypeEnum.C_TYPE.getCode(), typeCDiscountStrategy);
              }
          }

          // 優(yōu)惠策略執(zhí)行器
          @Component
          public class DiscountStrategyExecutor {
              private DiscountStrategyFactory discountStrategyFactory;

              public void discount(OrderBO orderBO) {
                  DiscountStrategy discountStrategy = discountStrategyFactory.getStrategy(orderBO.getType());
                  if (null == discountStrategy) {
                      throw new RuntimeException("無優(yōu)惠策略");
                  }
                  discountStrategy.discount(orderBO);
              }
          }

          3.1.2 物流策略

          // 物流策略
          public interface ExpressStrategy {
              public void weighing(OrderBO orderBO);
          }

          // A類型訂單物流策略
          @Component
          public class TypeAExpressStrategy implements ExpressStrategy {

              @Override
              public void weighing(OrderBO orderBO) {
                  if (orderBO.getWeight() > 9) {
                      throw new RuntimeException("超過物流最大重量");
                  }
              }
          }

          // B類型訂單物流策略
          @Component
          public class TypeBExpressStrategy implements ExpressStrategy {

              @Override
              public void weighing(OrderBO orderBO) {
                  if (orderBO.getWeight() > 8) {
                      throw new RuntimeException("超過物流最大重量");
                  }
              }
          }

          // C類型訂單物流策略
          @Component
          public class TypeCExpressStrategy implements ExpressStrategy {

              @Override
              public void weighing(OrderBO orderBO) {
                  if (orderBO.getWeight() > 7) {
                      throw new RuntimeException("超過物流最大重量");
                  }
              }
          }

          // 物流策略工廠
          @Component
          public class ExpressStrategyFactory implements InitializingBean {
              private Map<String, ExpressStrategy> strategyMap = new HashMap<>();

              @Resource
              private TypeAExpressStrategy typeAExpressStrategy;
              @Resource
              private TypeBExpressStrategy typeBExpressStrategy;
              @Resource
              private TypeCExpressStrategy typeCExpressStrategy;

              @Override
              public void afterPropertiesSet() throws Exception {
                  strategyMap.put(OrderTypeEnum.A_TYPE.getCode(), typeAExpressStrategy);
                  strategyMap.put(OrderTypeEnum.B_TYPE.getCode(), typeBExpressStrategy);
                  strategyMap.put(OrderTypeEnum.C_TYPE.getCode(), typeCExpressStrategy);
              }

              public ExpressStrategy getStrategy(String type) {
                  return strategyMap.get(type);
              }
          }

          // 物流策略執(zhí)行器
          @Component
          public class ExpressStrategyExecutor {
              private ExpressStrategyFactory expressStrategyFactory;

              public void weighing(OrderBO orderBO) {
                  ExpressStrategy expressStrategy = expressStrategyFactory.getStrategy(orderBO.getType());
                  if (null == expressStrategy) {
                      throw new RuntimeException("無物流策略");
                  }
                  expressStrategy.weighing(orderBO);
              }
          }

          3.1.3 退款策略

          // 退款策略
          public interface RefundStrategy {
              public void supportRefund(OrderBO orderBO);
          }

          // A類型訂單退款策略
          @Component
          public class TypeARefundStrategy implements RefundStrategy {

              @Override
              public void supportRefund(OrderBO orderBO) {
                  orderBO.setRefundSupport(Boolean.FALSE);
              }
          }

          // B類型訂單退款策略
          @Component
          public class TypeBRefundStrategy implements RefundStrategy {

              @Override
              public void supportRefund(OrderBO orderBO) {
                  orderBO.setRefundSupport(Boolean.TRUE);
              }
          }

          // C類型訂單退款策略
          @Component
          public class TypeCRefundStrategy implements RefundStrategy {

              @Override
              public void supportRefund(OrderBO orderBO) {
                  orderBO.setRefundSupport(Boolean.TRUE);
              }
          }

          // 退款策略工廠
          @Component
          public class RefundStrategyFactory implements InitializingBean {
              private Map<String, RefundStrategy> strategyMap = new HashMap<>();

              @Resource
              private TypeARefundStrategy typeARefundStrategy;
              @Resource
              private TypeBRefundStrategy typeBRefundStrategy;
              @Resource
              private TypeCRefundStrategy typeCRefundStrategy;

              @Override
              public void afterPropertiesSet() throws Exception {
                  strategyMap.put(OrderTypeEnum.A_TYPE.getCode(), typeARefundStrategy);
                  strategyMap.put(OrderTypeEnum.B_TYPE.getCode(), typeBRefundStrategy);
                  strategyMap.put(OrderTypeEnum.C_TYPE.getCode(), typeCRefundStrategy);
              }

              public RefundStrategy getStrategy(String type) {
                  return strategyMap.get(type);
              }
          }

          // 退款策略執(zhí)行器
          @Component
          public class RefundStrategyExecutor {
              private RefundStrategyFactory refundStrategyFactory;

              public void supportRefund(OrderBO orderBO) {
                  RefundStrategy refundStrategy = refundStrategyFactory.getStrategy(orderBO.getType());
                  if (null == refundStrategy) {
                      throw new RuntimeException("無退款策略");
                  }
                  refundStrategy.supportRefund(orderBO);
              }
          }

          3.2 橫向做編排

          橫向維度表示場景,一種訂單類型在廣義上可以認(rèn)為是一種業(yè)務(wù)場景,在場景中將獨(dú)立的策略進(jìn)行串聯(lián),模板方法設(shè)計(jì)模式適用于這種場景。

          模板方法模式定義一個(gè)操作中的算法骨架,一般使用抽象類定義算法骨架。抽象類同時(shí)定義一些抽象方法,這些抽象方法延遲到子類實(shí)現(xiàn),這樣子類不僅遵守了算法骨架約定,也實(shí)現(xiàn)了自己的算法。既保證了規(guī)約也兼顧靈活性。這就是用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。

          // 創(chuàng)建訂單服務(wù)
          public interface CreateOrderService {
              public void createOrder(OrderBO orderBO);
          }

          // 抽象創(chuàng)建訂單流程
          public abstract class AbstractCreateOrderFlow {

              @Resource
              private OrderMapper orderMapper;

              public void createOrder(OrderBO orderBO) {
                  // 參數(shù)校驗(yàn)
                  if (null == orderBO) {
                      throw new RuntimeException("參數(shù)異常");
                  }
                  if (OrderTypeEnum.isNotValid(orderBO.getType())) {
                      throw new RuntimeException("參數(shù)異常");
                  }
                  // 計(jì)算優(yōu)惠
                  discount(orderBO);
                  // 計(jì)算重量
                  weighing(orderBO);
                  // 退款支持
                  supportRefund(orderBO);
                  // 保存數(shù)據(jù)
                  OrderDO orderDO = new OrderDO();
                  BeanUtils.copyProperties(orderBO, orderDO);
                  orderMapper.insert(orderDO);
              }

              public abstract void discount(OrderBO orderBO);

              public abstract void weighing(OrderBO orderBO);

              public abstract void supportRefund(OrderBO orderBO);
          }

          // 實(shí)現(xiàn)創(chuàng)建訂單流程
          @Service
          public class CreateOrderFlow extends AbstractCreateOrderFlow {

              @Resource
              private DiscountStrategyExecutor discountStrategyExecutor;
              @Resource
              private ExpressStrategyExecutor expressStrategyExecutor;
              @Resource
              private RefundStrategyExecutor refundStrategyExecutor;

              @Override
              public void discount(OrderBO orderBO) {
                  discountStrategyExecutor.discount(orderBO);
              }

              @Override
              public void weighing(OrderBO orderBO) {
                  expressStrategyExecutor.weighing(orderBO);
              }

              @Override
              public void supportRefund(OrderBO orderBO) {
                  refundStrategyExecutor.supportRefund(orderBO);
              }
          }

          3.3 復(fù)雜架構(gòu)設(shè)計(jì)

          上述實(shí)例業(yè)務(wù)和代碼并不復(fù)雜,其實(shí)復(fù)雜業(yè)務(wù)場景也不過是簡單場景的疊加、組合和交織,無外乎也是通過縱向做隔離、橫向做編排尋求答案。



          縱向維度抽象出能力池這個(gè)概念,能力池中包含許多能力,不同的能力按照不同業(yè)務(wù)維度聚合,例如優(yōu)惠能力池,物流能力池,退款能力池。我們可以看到兩種程度的隔離性,能力池之間相互隔離,能力之間也相互隔離。

          橫向維度將能力從能力池選出來,按照業(yè)務(wù)需求串聯(lián)在一起,形成不同業(yè)務(wù)流程。因?yàn)槟芰梢匀我饨M合,所以體現(xiàn)了很強(qiáng)的靈活性。除此之外,不同能力既可以串行執(zhí)行,如果不同能力之間沒有依賴關(guān)系,也可以如同流程Y一樣并行執(zhí)行,提升執(zhí)行效率。


          4 數(shù)據(jù)分片如何應(yīng)用縱橫思維

          現(xiàn)在有一個(gè)電商數(shù)據(jù)庫存放訂單、商品、支付三張業(yè)務(wù)表。隨著業(yè)務(wù)量越來越大,這三張業(yè)務(wù)數(shù)據(jù)表也越來越大,查詢性能顯著降低,數(shù)據(jù)拆分勢在必行。那么數(shù)據(jù)拆分也可以從縱向和橫向兩個(gè)維度進(jìn)行。


          4.1 縱向分表

          縱向拆分就是按照業(yè)務(wù)拆分,我們將電商數(shù)據(jù)庫拆分成三個(gè)庫,訂單庫、商品庫。支付庫,訂單表在訂單庫,商品表在商品庫,支付表在支付庫。這樣每個(gè)庫只需要存儲(chǔ)本業(yè)務(wù)數(shù)據(jù),物理隔離不會(huì)互相影響。



          4.2 橫向分表

          按照縱向拆分方案之后我們已經(jīng)有三個(gè)庫了,平穩(wěn)運(yùn)行了一段時(shí)間。但是隨著業(yè)務(wù)增長,每個(gè)單庫單表的數(shù)據(jù)量也越來越大,逐漸到達(dá)瓶頸。

          這時(shí)我們就要對數(shù)據(jù)表進(jìn)行橫向拆分,所謂橫向拆分就是根據(jù)某種規(guī)則將單庫單表數(shù)據(jù)分散到多庫多表,從而減小單庫單表的壓力。

          橫向拆分策略有很多方案,最重要的一點(diǎn)是選好ShardingKey,也就是按照哪一列進(jìn)行拆分,怎么分取決于我們訪問數(shù)據(jù)的方式。


          4.2.1 范圍分片

          如果我們選擇的ShardingKey是訂單創(chuàng)建時(shí)間,那么分片策略是拆分四個(gè)數(shù)據(jù)庫分別存儲(chǔ)每季度數(shù)據(jù),每個(gè)庫包含三張表分別存儲(chǔ)每個(gè)月數(shù)據(jù):



          這個(gè)方案的優(yōu)點(diǎn)是對范圍查詢比較友好,例如我們需要統(tǒng)計(jì)第一季度的相關(guān)數(shù)據(jù),查詢條件直接輸入時(shí)間范圍即可。這個(gè)方案的問題是容易產(chǎn)生熱點(diǎn)數(shù)據(jù)。例如雙11當(dāng)天下單量特別大,就會(huì)導(dǎo)致11月這張表數(shù)據(jù)量特別大從而造成訪問壓力。


          4.2.2 查表分片

          查表法是根據(jù)一張路由表決定ShardingKey路由到哪一張表,每次路由時(shí)首先到路由表里查到分片信息,再到這個(gè)分片去取數(shù)據(jù)。我們分析一個(gè)查表法思想應(yīng)用實(shí)際案例。

          Redis官方在3.0版本之后提供了官方集群方案RedisCluster,其中引入了哈希槽(slot)這個(gè)概念。一個(gè)集群固定有16384個(gè)槽,在集群初始化時(shí)這些槽會(huì)平均分配到Redis集群節(jié)點(diǎn)上。每個(gè)key請求最終落到哪個(gè)槽計(jì)算公式是固定的:

          SLOT = CRC16(key) mod 16384

          一個(gè)key請求過來怎么知道去哪臺(tái)Redis節(jié)點(diǎn)獲取數(shù)據(jù)?這就要用到查表法思想:

          (1) 客戶端連接任意一臺(tái)Redis節(jié)點(diǎn),假設(shè)隨機(jī)訪問到節(jié)點(diǎn)A
          (2) 節(jié)點(diǎn)A根據(jù)key計(jì)算出slot值
          (3) 每個(gè)節(jié)點(diǎn)都維護(hù)著slot和節(jié)點(diǎn)映射關(guān)系表
          (4) 如果節(jié)點(diǎn)A查表發(fā)現(xiàn)該slot在本節(jié)點(diǎn),直接返回?cái)?shù)據(jù)給客戶端
          (5) 如果節(jié)點(diǎn)A查表發(fā)現(xiàn)該slot不在本節(jié)點(diǎn),返回給客戶端一個(gè)重定向命令,告訴客戶端應(yīng)該去哪個(gè)節(jié)點(diǎn)請求這個(gè)key的數(shù)據(jù)
          (6) 客戶端向正確節(jié)點(diǎn)發(fā)起連接請求

          查表法方案優(yōu)點(diǎn)是可以靈活制定路由策略,如果我們發(fā)現(xiàn)有的分片已經(jīng)成為熱點(diǎn)則修改路由策略。缺點(diǎn)是多一次查詢路由表操作增加耗時(shí),而且路由表如果是單點(diǎn)也可能會(huì)有單點(diǎn)問題。


          4.2.3 哈希分片

          相較于范圍分片,哈希分片可以較為均勻?qū)?shù)據(jù)分散在數(shù)據(jù)庫中。我們現(xiàn)在將訂單庫拆分為4個(gè)庫編號(hào)為[0,3],每個(gè)庫包含3張表編號(hào)為[0,2],如下圖如所示:



          我們選擇使用orderId作為ShardingKey,那么orderId=100這個(gè)訂單會(huì)保存在哪張表?因?yàn)槭欠謳旆直?,第一步確定路由到哪一個(gè)庫,取模計(jì)算結(jié)果表示庫表序號(hào):

          db_index = 100 % 4 = 0

          第二步確定路由到哪一張表:

          table_index = 100 % 3 = 1

          第三步數(shù)據(jù)路由到0號(hào)庫1號(hào)表:


          在實(shí)際開發(fā)中路由邏輯并不需要我們手動(dòng)實(shí)現(xiàn),因?yàn)橛性S多開源框架通過配置就可以實(shí)現(xiàn)路由功能,例如ShardingSphere、TDDL框架等等。


          5 文章總結(jié)

          復(fù)雜問題不過是簡單問題的疊加、組合和交織,橫向和縱向兩個(gè)維度拆分問題不失為一種好方法。縱向做隔離是指將不同業(yè)務(wù)形態(tài)進(jìn)行隔離,能力池之間進(jìn)行隔離,能力之間也進(jìn)行隔離。橫向做編排是指從能力池中靈活選擇出能力,進(jìn)行組合和編排,形成形態(tài)各異的業(yè)務(wù)流程,希望本文對大家有所幫助。



          JAVA前線 


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


          瀏覽 107
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  中文字幕性爱 | 黄色网页在线观看 | 小早川怜子一区二区三区 | 天天操天天撸视频免费 | 三级在线视频网站 |