利用 ShardingSphere-JDBC 實現分庫分表實踐

2021-02-16 編程技術圈
利用ShardingSphere-JDBC實現分庫分表1. ShardingSphere概述1.1 概述

業務發展到一定程度,分庫分表是一種必然的要求,分庫可以實現資源隔離,分表則可以降低單表數據量,提高訪問效率。

分庫分表的技術方案,很久以來都有兩種理念:

這兩種方式是各有利弊的,集中式Proxy的好處是業務沒有感知,一切交給DBA把控,分布式的Proxy其支持的語言有限,比如本文要提及的ShardingShpere-
JDBC就只支持Java。

我們需要了解一點,集中式的Proxy其實現非常複雜,這要從MySQL處理SQL語句的原理說起,因為不是本文要論述的重點,因此只是簡單的提及幾點:

SQL語句要被Parser解析成抽象語法樹

SQL要被優化器解析出執行計劃

SQL語句完成解析後,發給存儲引擎

因此大部分的中間件都選擇了自己實現SQL的解析器和查詢優化器,下面是著名的中間件dble的實現示意圖:

只要有解析的過程,其性能損耗就是比較可觀的,我們也可以認為這是一種重量級的解決方案。

與之形成對比的是ShardingSphere-JDBC,其原理示意圖如下:

每一個服務都持有一個Sharing-
JDBC,這個JDBC以Jar包的形式提供,基本上可以認為是一個增強版的jdbc驅動,需要一些分庫分表的配置,業務開發人員不需要去對代碼進行任何的修改。可以很輕鬆的移植到SpringBoot,ORM等框架上。

但是這個結構也不是完美的,每一個服務持有一個proxy意味著會在MySQL服務端新建大量的連接,維持連接會增加MySQL伺服器的負載,雖然這種負載提升一般無法察覺。

1.2 概念

邏輯表  

即水平拆分的表的總稱。比如訂單業務會被拆分成t_order0,t_order1兩張表,但是他們同屬於一個邏輯表:t_order

綁定表

分片規則一直的主表和子表。比如還是上面的t_order表,其分片鍵是order_id,其子表t_order_item的分片鍵也是order_id。在規則配置時將兩個表配置成綁定關係,就不會在查詢時出現笛卡爾積。

在關聯查詢時,如果沒有綁定關係,則t_order和t_order_item的關聯會出現這樣一種場景:

    select * from t_order0 inner join t_order_item0 on order_id = order_id where order_id in (0, 1);
    select * from t_order0 inner join t_order_item1 on order_id = order_id where order_id in (0, 1;
    select * from t_order1 inner join t_order_item0 on order_id = order_id where order_id in (0, 1;
    select * from t_order1 inner join t_order_item1 on order_id = order_id where order_id in (0, 1;

如果配置了綁定關係,則會精確地定位到order_id所在的表,消除笛卡爾積。

廣播表

有一些表是沒有分片的必要的,比如省份信息表,全國也就30多條數據,這種表在每一個節點上都是一樣的,這種表叫做廣播表。

2. 利用SpringBoot實現分庫分表

要分庫分表首先需要有不同的數據源,我們啟動兩個mysqld進行,監聽3306和3307兩個埠,以多實例的形式模擬多數據源。

我們的分庫是以用戶ID為依據的,分表是以表本身的主鍵為依據的。下面是一張示意表:

    -- 注意,這是邏輯表,實際不存在
    create table t_order
    (
      order_id bigint not null auto_increment primary key,
      user_id bigint not null,
      name varchar(100)
    );

    CREATE TABLE `t_order_item` (
      `order_id` bigint(20) NOT NULL,
      `item` varchar(100) DEFAULT NULL,
      `user_id` bigint(20) NOT NULL,
      PRIMARY KEY (`order_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我現在有兩個數據源,每個數據源上根據order_id分成2兩表,也就是說每個實例上都應該有這兩張表:

    create table t_order0
    (
      order_id bigint not null auto_increment primary key,
      user_id bigint not null,
      name varchar(100)
    );

    create table t_order1
    (
      order_id bigint not null auto_increment primary key,
      user_id bigint not null,
      name varchar(100)
    );

    -- 這是廣播表,新建在其中一個節點上就可以
    CREATE TABLE `t_config` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `user_id` bigint(20) DEFAULT NULL,
      `config` varchar(100) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB;

    CREATE TABLE `t_order_item0` (
      `order_id` bigint(20) NOT NULL,
      `item` varchar(100) DEFAULT NULL,
      `user_id` bigint(20) NOT NULL,
      PRIMARY KEY (`order_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    CREATE TABLE `t_order_item1` (
      `order_id` bigint(20) NOT NULL,
      `item` varchar(100) DEFAULT NULL,
      `user_id` bigint(20) NOT NULL,
      PRIMARY KEY (`order_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

利用SpringBoot技術可以很快的構建一個RESTful的Web服務,下面是application.properties的內容:

    # 這裡要註冊所有的數據源
    spring.shardingsphere.datasource.names=ds0,ds1

    # 這是數據源0的配置
    spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
    spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
    spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
    spring.shardingsphere.datasource.ds0.username=root
    spring.shardingsphere.datasource.ds0.password=

    # 這是數據源1的配置
    spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
    spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3307/test?serverTimezone=GMT%2B8
    spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
    spring.shardingsphere.datasource.ds1.username=root
    spring.shardingsphere.datasource.ds1.password=

    # 分庫策略
    # 分庫的列是user_id
    spring.shardingsphere.sharding.default-database-strategy.standard.sharding-column=user_id
    spring.shardingsphere.sharding.default-database-strategy.standard.precise-algorithm-class-name=com.sinosun.demo.sharding.PreciseShardingAlgorithmImpl

    # 分表策略
    spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds$->{0..1}.t_order$->{0..1}
    spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column=order_id
    spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order$->{order_id % 2}
    spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
    spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE

    spring.shardingsphere.sharding.tables.t_order_item.actual-data-nodes=ds$->{0..1}.t_order_item$->{0..1}
    spring.shardingsphere.sharding.tables.t_order_item.table-strategy.inline.sharding-column=order_id
    spring.shardingsphere.sharding.tables.t_order_item.table-strategy.inline.algorithm-expression=t_order_item$->{order_id % 2}

    spring.shardingsphere.sharding.binding-tables=t_order, t_order_item

    # 廣播表, 其主節點是ds0
    spring.shardingsphere.sharding.broadcast-tables=t_config
    spring.shardingsphere.sharding.tables.t_config.actual-data-nodes=ds$->{0}.t_config

    spring.jpa.show-sql=true
    server.address=10.1.20.96
    server.port=8080

這是buid.gradle內容,只列舉ShardingSphere相關的:

    dependencies {
        compile group: 'org.apache.shardingsphere', name: 'sharding-jdbc-spring-boot-starter', version: '4.0.0-RC1'
        compile group: 'org.apache.shardingsphere', name: 'sharding-jdbc-spring-namespace', version: '4.0.0-RC1'
    }

下圖是工程的代碼結構,供參考:


現在開始列舉代碼:

Order.java:  

    package com.example.demo.entity;


    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    import java.util.StringJoiner;

    @Entity
    @Table(name = "t_order")
    public class Order {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private long orderId;

        @Column(name = "user_id")
        private long userId;

        @Column(name = "name")
        private String name;

        public long getOrderId() {
            return orderId;
        }

        public void setOrderId(long orderId) {
            this.orderId = orderId;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public long getUserId() {
            return userId;
        }

        public void setUserId(long userId) {
            this.userId = userId;
        }

        @Override
        public String toString() {
            return new StringJoiner(", ", Order.class.getSimpleName() + "[", "]")
                    .add("orderId=" + orderId)
                    .add("userId=" + userId)
                    .add("name='" + name + "'")
                    .toString();
        }
    }

OrderItem.java:  

    package com.example.demo.entity;

    import com.google.common.base.MoreObjects;

    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.Table;

    @Entity
    @Table(name = "t_order_item")
    public class OrderItem {
        @Id
        @Column(name = "order_id")
        private long orderId;

        @Column(name = "user_id")
        private long userId;

        @Column(name = "item")
        private String item;

        public long getOrderId() {
            return orderId;
        }

        public void setOrderId(long orderId) {
            this.orderId = orderId;
        }

        public long getUserId() {
            return userId;
        }

        public void setUserId(long userId) {
            this.userId = userId;
        }

        public String getItem() {
            return item;
        }

        public void setItem(String item) {
            this.item = item;
        }

        @Override
        public String toString() {
            return MoreObjects.toStringHelper(this)
                    .add("orderId", orderId)
                    .add("userId", userId)
                    .add("item", item)
                    .toString();
        }
    }

TConfig.java:  

    package com.example.demo.entity;

    import com.google.common.base.MoreObjects;

    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;

    @Entity
    @Table(name = "t_config")
    public class TConfig {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private int id;

        @Column(name = "user_id")
        private long userId;

        @Column(name = "config")
        private String config;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public long getUserId() {
            return userId;
        }

        public void setUserId(long userId) {
            this.userId = userId;
        }

        public String getConfig() {
            return config;
        }

        public void setConfig(String config) {
            this.config = config;
        }

        @Override
        public String toString() {
            return MoreObjects.toStringHelper(this)
                    .add("id", id)
                    .add("userId", userId)
                    .add("config", config)
                    .toString();
        }
    }

OrderDao.java:  

    package com.example.demo.dao;

    import com.example.demo.entity.Order;
    import org.springframework.data.jpa.repository.JpaRepository;

    public interface OrderDao extends JpaRepository<Order, Long> {
    }

OrderItemDao.java:  

    package com.example.demo.dao;

    import com.example.demo.entity.OrderItem;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.query.Param;

    import java.util.Optional;

    public interface OrderItemDao extends JpaRepository<OrderItem, Long> {
        //為了測試綁定表
        @Query(value = "select n from Order t inner join OrderItem n on t.orderId = n.orderId where n.orderId=:orderId")
        Optional<OrderItem> getOrderItemByOrderId(@Param("orderId") Long orderId);
    }

ConfigDao.java:  

    package com.example.demo.dao;

    import com.sinosun.demo.entity.TConfig;
    import org.springframework.data.jpa.repository.JpaRepository;

    public interface ConfigDao extends JpaRepository<TConfig, Integer> {
    }

OrderController.java:  

    package com.example.demo.controller;

    import com.example.demo.dao.OrderDao;
    import com.example.demo.entity.Order;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.Optional;

    @RestController
    public class OrderController {
        @Autowired
        private OrderDao orderDao;

        @RequestMapping(value = "/order", method = RequestMethod.GET)
        public Optional<Order> getOrderById(@RequestParam("id") Long id) {
            return this.orderDao.findById(id);
        }

        @RequestMapping(value = "/order/save", method = RequestMethod.POST)
        public Order saveOrder(@RequestParam("name") String name, @RequestParam("userid") Long userId) {
            Order order = new Order();
            order.setName(name);
            order.setUserId(userId);
            return this.orderDao.save(order);
        }
    }

OrderItemController.java:  

    package com.example.demo.controller;

    import com.example.demo.dao.OrderItemDao;
    import com.example.demo.entity.OrderItem;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.Optional;

    @RestController
    public class OrderItemController {
        @Autowired
        private OrderItemDao orderItemDao;

        @RequestMapping(value = "/orderItem", method = RequestMethod.GET)
        public Optional<OrderItem> getOrderItemById(@RequestParam(name = "id") Long id) {
            return this.orderItemDao.findById(id);
        }

        @RequestMapping(value = "/orderItem/save", method = RequestMethod.POST)
        public OrderItem saveOrderItem(@RequestParam("item") String item, @RequestParam("userid") Long userId, @RequestParam("orderid") Long orderId) {
            OrderItem orderItem = new OrderItem();
            orderItem.setUserId(userId);
            orderItem.setItem(item);
            orderItem.setOrderId(orderId);
            return this.orderItemDao.save(orderItem);
        }

        @RequestMapping(value = "/orderItem/query", method = RequestMethod.GET)
        public Optional<OrderItem> getOrderItemByOrderId(@RequestParam(name = "orderid") Long orderId) {
            return this.orderItemDao.getOrderItemByOrderId(orderId);
        }
    }

ConfigController.java:  

    package com.example.demo.controller;

    import com.example.demo.dao.ConfigDao;
    import com.example.demo.entity.TConfig;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.List;

    @RestController
    public class ConfigController {
        @Autowired
        private ConfigDao configDao;

        @RequestMapping(value = "/listConfig", method = RequestMethod.GET)
        public List<TConfig> getConfig() {
            return this.configDao.findAll();
        }
    }

這三段代碼寫完基本的功能就完備了,但是剛才配置的時候提過,我們的目的是按照user_id進行分庫,比如user_id=0則分配這條數據到ds0去,如果為1則將數據分配到ds1去,這就要求我們自己實現分庫的算法,ShardingSphere提供了接口,只需要去實現就可以了:

    package com.example.demo.sharding;

    import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
    import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

    import java.util.Collection;

    public class PreciseShardingAlgorithmImpl implements PreciseShardingAlgorithm<Long> {

        @Override
        public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
            String dbName = "ds";
            Long val = shardingValue.getValue();
            dbName += val;
            for (String each : availableTargetNames) {
                if (each.equals(dbName)) {
                    return each;
                }
            }
            throw new IllegalArgumentException();
        }
    }

這段代碼很簡單,其中有幾個地方只需要講明白了就可以。

之後用一個循環遍歷["ds0",
"ds1"]集合,當我們的dbName和其中一個相等時,就能的到正確的數據源。這就簡單的實現了根據user_id精確分配數據的目的。

這是實測例子中,shardingValue和availableTargetNames的實際值:

image.png

本次測試的請求是:

    curl -X POST \
      'http://10.1.20.96:8080/order/save?name=LiLei&userid=0' \
      -H 'Postman-Token: d5e15e85-c760-4252-a7d4-ef57b5e95c2e' \
      -H 'cache-control: no-cache'

下面看看實際效果,這是ds0的數據:

image.png

這是ds1的數據:


可以看到,所有的數據都根據user_id分布到了不同的庫中,所有的數據都根據order_id的奇偶分布到了不同的表中。

記錄下保存t_order請求返回的order_id,組裝一條POST請求寫t_order_item表:

    curl -X POST \
      'http://10.1.20.96:8080/orderItem/save?item=pen&userid=0&orderid=371698107924086785' \
      -H 'Accept: */*' \
      -H 'Cache-Control: no-cache' \
      -H 'Connection: keep-alive' \
      -H 'Host: 10.1.20.96:8080' \
      -H 'Postman-Token: 347b6c4d-0e2c-474f-b53e-6f0994db5871,24b362da-e77e-4b04-94e1-fa20dcb15845' \
      -H 'User-Agent: PostmanRuntime/7.15.0' \
      -H 'accept-encoding: gzip, deflate' \
      -H 'cache-control: no-cache' \
      -H 'content-length: '

得到結果如下:


使用這個order_id去進行聯合查詢:

    curl -X GET \
      'http://10.1.20.96:8080/orderItem/query?orderid=371698107924086785' \
      -H 'Accept: */*' \
      -H 'Cache-Control: no-cache' \
      -H 'Connection: keep-alive' \
      -H 'Host: 10.1.20.96:8080' \
      -H 'Postman-Token: d0da0523-d46e-429f-a8db-9f844cd77fe6,b61c6089-253d-4535-b473-158c037850be' \
      -H 'User-Agent: PostmanRuntime/7.15.0' \
      -H 'accept-encoding: gzip, deflate' \
      -H 'cache-control: no-cache'

得到返回如下:


測試廣播表,可以用下面的請求:

    curl -X GET \
      http://10.1.20.96:8080/listConfig \
      -H 'Accept: */*' \
      -H 'Cache-Control: no-cache' \
      -H 'Connection: keep-alive' \
      -H 'Host: 10.1.20.96:8080' \
      -H 'Postman-Token: 1c9d0349-4b6d-4a2c-834f-4e2f94194649,3dff68f4-2e10-4e96-926a-344faa5f0a19' \
      -H 'User-Agent: PostmanRuntime/7.15.0' \
      -H 'accept-encoding: gzip, deflate' \
      -H 'cache-control: no-cache'

得到的結果:


3. 利用SpringBoot實現讀寫分離

上一小節中展示了如何利用SharingSphere+SpringBoot進行數據的分片,這一小節著重描述一下如何進行讀寫分離,下一小節計劃展示如何將讀寫分離和分片結合起來。

首先還是會利用多實例來模擬,為了簡單,我沒有配置複製,而是預置了幾條數據進去,判斷能否將讀寫請求分發到不同的節點上。

首先我們新建一張表:

    create table t_order
    (
      order_id bigint not null auto_increment primary key,
      user_id bigint not null,
      name varchar(100)
    );

    -- master
    insert into t_order(user_id, name) values (0, 'zhiquan');

    -- slave 
    insert into t_order(user_id, name) values (1, 'LiLei');

我會配置slave為讀數據源,那麼讀出的數據一定是user_id=1這一條。

數據是這樣的,首先是master:


然後是slave:


接下來開始粘貼代碼,首先是配置:

application.properties:  

    spring.shardingsphere.datasource.names=ds0,ds1

    spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
    spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
    spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
    spring.shardingsphere.datasource.ds0.username=root
    spring.shardingsphere.datasource.ds0.password=

    spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
    spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3307/test?serverTimezone=GMT%2B8
    spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
    spring.shardingsphere.datasource.ds1.username=root
    spring.shardingsphere.datasource.ds1.password=

    spring.shardingsphere.masterslave.name=ms
    spring.shardingsphere.masterslave.master-data-source-name=ds0
    spring.shardingsphere.masterslave.slave-data-source-names=ds1

    server.port=8080
    spring.jpa.show-sql=true

具體的實現代碼就不粘貼了,和之前的小節沒有什麼區別。下面開始測試,首先是一個GET請求:

    curl -X GET \
      'http://localhost:8080/getOrder?orderId=2' \
      -H 'Accept: */*' \
      -H 'Accept-Encoding: gzip, deflate' \
      -H 'Cache-Control: no-cache' \
      -H 'Connection: keep-alive' \
      -H 'Host: localhost:8080' \
      -H 'Postman-Token: 028a4539-a727-47f2-8862-2eed637883d0,ffbe396f-5c33-4266-a00e-d2a0246283f3' \
      -H 'User-Agent: PostmanRuntime/7.15.2' \
      -H 'cache-control: no-cache'


如上圖,和預期是一樣的,讀取到了slave上的數據,那麼現在看看寫操作:

    curl -X POST \
      'http://localhost:8080/saveOrder?userId=123&name=HanMeimei' \
      -H 'Accept: */*' \
      -H 'Accept-Encoding: gzip, deflate' \
      -H 'Cache-Control: no-cache' \
      -H 'Connection: keep-alive' \
      -H 'Content-Length: ' \
      -H 'Host: localhost:8080' \
      -H 'Postman-Token: f0497259-a82a-4dcf-9849-3dcdae431742,77fd1308-b5e8-4882-be07-fa128e6efc4d' \
      -H 'User-Agent: PostmanRuntime/7.15.2' \
      -H 'cache-control: no-cache'


現在看看主節點的表:


如上圖,這條數據已經成功的寫入了master。

相關焦點

  • 一文快速入門分庫分表中間件 Sharding-JDBC(必修課)
    分庫分表實戰系列的開篇文章,我們在前文中回顧了一下分庫分表的基礎知識,對分庫分表的拆分方式有了一定的了解。,只要你熟悉 JDBC 就可以輕鬆應用 Sharding-JDBC 來實現分庫分表。shardingsphere 對應的 sharding-jdbc-spring-boot-starter 和 sharding-core-common 包,版本統一用的 4.0.0-RC1。
  • 分庫分表【Sharding-JDBC】入門與項目實戰
    小Hub領讀:Sharding-JDBC,這麼主流的分庫分表框架,你還不懂?來了解一下,然後入門吧!
  • 分庫分表常見概念解讀+Sharding-JDBC實戰
    之前有不少剛入坑 Java 的粉絲留言,想系統的學習一下分庫分表相關技術,可我一直沒下定決心搞,眼下趕上公司項目在使用 sharding-jdbc  對現有 MySQL 架構做分庫分表的改造,所以藉此機會出一系分庫分表落地實踐的文章,也算是自己對架構學習的一個總結。
  • 數據量大了一定要分表,分庫分表Sharding-JDBC入門與項目實戰
    最近項目中不少表的數據量越來越大,並且導致了一些資料庫的性能問題。因此想藉助一些分庫分表的中間件,實現自動化分庫分表實現。調研下來,發現Sharding-JDBC目前成熟度最高並且應用最廣的Java分庫分表的客戶端組件。
  • Sharding-jdbc的實戰入門之水平分表(一)
    經常碰到一些小夥伴的問題,就是我們到達什麼量級才會分庫分表?分庫分表經驗值mysql單表經驗一般MySQL單表1000W左右的數據是可以不需要考慮分表的。但是要避免過度設計(考慮了很多未來幾年的需求,例如一張表在未來幾年內數據預計會達到幾千萬,這個就過渡考慮了)根據數據量增長速度,選擇實現步驟第一步:不分庫不分表第二步:同庫內的分表第三步:分庫分表不要過度設計,一上來玩大的就進行分庫分表分庫如果
  • Spring Boot 採用Sharding-JDBC 實現Mybaits的分庫分表功能
    背景在開發大數據量的應用時為了減少單表數據量經常會使用到分庫分表功能,以前對分庫分表功能都是自己在代碼上單獨對需要分庫分表的實體進行特殊邏輯處理
  • 分庫分表的4個面試連環炮問題!不會就慘了
    三、面試題剖析1、為什麼要分庫分表?(設計高並發系統的時候,資料庫層面該如何設計?)說白了,分庫分表是兩回事兒,大家可別搞混了,可能是光分庫不分表,也可能是光分表不分庫,都有可能。我先給大家拋出來一個場景。
  • Sharding-Sphere 3.0.0.M4 正式發布 - OSCHINA - 中文開源技術...
    API調整Maven坐標調整:將<artifactId>sharding-jdbc</artifactId>調整為<artifactId>sharding-jdbc-core</artifactId>。
  • sqltoy-orm-4.17.5 發布,支持 QueryExecutor 中定義分庫分表
    開源地址:更新內容1、 支持QueryExecutor、EntityQuery中使用分庫分表策略配置 (原本只支持xml中定義)//分庫dbSharding(String strategy, String... paramNames)//分表tableSharding(String strategy, String[] tables, String... paramNames)
  • Sharding-Sphere 3.0.0 正式發布 - OSCHINA - 中文開源技術交流社區
    ISSUE #610 Route unicast for DQL without tableISSUE #701 Caching parsed results to improve performanceISSUE #773 Support sharding and autoincrement key of INSERT without column names
  • 簡單粗暴的分庫分表設計方案
    來源於:https://zhuanlan.zhihu.com/p/374386521.數據散列模式數據散列模式主要是通過hash算法將數據隨機寫入(分庫)分表中,用以提高資料庫的負載能力,這種設計方案下分表欄位通常需要被包含在分表中。優點:可以解決有局部熱點的數據的負載均衡,並整體提高資料庫的負載能力。
  • 分庫分表:TiDB,求別搶飯碗!
    但是引入了中間件肯定就會增加各方面的維護成本,這篇帶大家了解一款替代分庫分表的解決方案:分布式資料庫:TiDB 前言 如今硬體的性價比越來越高,網絡傳輸速度越來越快,資料庫分層的趨勢逐漸顯現,人們已經不再強求用一個解決方案來解決所有的存儲問題,而是通過分層,讓緩存與資料庫負責各自擅長的業務場景。
  • 我們為什麼要分庫分表?
    在文章開頭先拋幾個問題:(1)什麼時候才需要分庫分表呢?我們的評判標準是什麼?(2)一張表存儲了多少數據的時候,才需要考慮分庫分表?分表說完了分庫,那什麼時候分表呢?如果系統處於高速發展階段,拿商城系統來說,一天下單量可能幾十萬,那資料庫中的訂單表增長就特別快,增長到一定階段資料庫查詢效率就會出現明顯下降。
  • 分庫分表就能無限擴容嗎,解釋得太好了!
    分庫分表如果你的公司產品很受歡迎,業務繼續高速發展,數據越來越多,SQL 操作越來越慢,那麼資料庫就會成為瓶頸,那麼你肯定會想到分庫分表,不論通過 ID hash 或者 range 的方式都可以。如下圖:這下應該沒問題了吧。任憑你用戶再多,並發再高,我只要無限擴容資料庫,無限擴容應用,就可以了。
  • MongoDB sharding遷移那些事(三)
    ,你應該知道的 關於 sharding 遷移,會分3個部分來介紹,本文為第三部分 負載均衡及遷移策略 chunk 遷移流程 Ba...如果不了解 MongoDB Sharded Cluster 原理,請先閱讀關於 sharding 遷移,會分3個部分來介紹,本文為第三部分負載均衡及遷移策略chunk 遷移流程Balancer 運維管理在前面2個部分裡,介紹了 MongoDB sharding 的遷移策略以及
  • MySQL:網際網路公司常用分庫分表方案匯總
    作者:尜尜人物cnblogs.com/littlecharacter/p/9342129.html本文目錄一、資料庫瓶頸二、分庫分表三、分庫分表工具四、分庫分表步驟五、分庫分表問題非partition key的查詢問題非partition key跨庫跨表分頁查詢問題
  • 數據蔣堂 | 時序數據從分表到分庫
    在查詢數據時一般都會有時間段參數,應用程式可以根據這個參數計算出該查詢涉及哪些分表,然後將這些分表UNION起來拼到SQL語句的FROM後面。查詢不涉及的時間段對應的分表不會被拼進來,這樣就可以有效減少數據遍歷的範圍,從而提高性能。這個方案在單個資料庫時沒啥毛病,但是不是能推廣到多個資料庫的情況呢?
  • Django分表的兩個方案
    由來知乎上的一個問題:Django 分表 怎麼實現?
  • 一個億級分庫分表項目的實戰全過程解析
    分庫分表的文章網上非常多,但是大多內容比較零散,以講解知識點為主,沒有完整地說明一個大表的切分、新架構設計、上線的完整過程。因此,我結合去年做的一個大型分庫分表項目,來復盤一下完整的分庫分表從架構設計到發布上線的實戰總結。為什麼需要做分庫分表?這個相信大家多少都有所了解。