欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java實(shí)現(xiàn)分庫(kù)分表實(shí)踐指南

 更新時(shí)間:2024年01月26日 10:28:38   作者:醬油何在  
在開(kāi)發(fā)中我們經(jīng)常使用到分庫(kù)分表,但是一般是我們前期就已經(jīng)做了規(guī)劃,對(duì)數(shù)據(jù)庫(kù)怎么劃分,對(duì)哪些表進(jìn)行分表,這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)分庫(kù)分表的相關(guān)資料,需要的朋友可以參考下

一、為啥要分庫(kù)分表

在大型互聯(lián)網(wǎng)系統(tǒng)中,大部分都會(huì)選擇mysql作為業(yè)務(wù)數(shù)據(jù)存儲(chǔ)。一般來(lái)說(shuō),mysql單表行數(shù)超過(guò)500萬(wàn)行或者單表容量超過(guò)2GB,查詢效率就會(huì)隨著數(shù)據(jù)量的增長(zhǎng)而下降。這個(gè)時(shí)候,就需要對(duì)表進(jìn)行拆分。

那么應(yīng)該怎么拆分呢?

通常有兩種拆分方法,垂直拆分和水平拆分。

先說(shuō)垂直拆分,這個(gè)比較簡(jiǎn)單,我們可以把原先的一張表根據(jù)業(yè)務(wù)屬性拆分成多張表。比如用戶表user有很多字段,我們可以新建一張用戶屬性表user_profile,把一些不常用的字段都拆分到user_profile表里,再用user_id作為外鍵將兩張表關(guān)聯(lián)起來(lái)就可以了。

再說(shuō)水平拆分,水平拆分針對(duì)的不是表,而是數(shù)據(jù)。比如訂單表,數(shù)據(jù)量一般都會(huì)非常大。我們可以創(chuàng)建多個(gè)數(shù)據(jù)庫(kù)實(shí)例,每個(gè)實(shí)例上創(chuàng)建多張訂單表,把訂單數(shù)據(jù)相對(duì)均勻的分散存儲(chǔ)到這些表里。查詢的時(shí)候,根據(jù)分表策略可直接定位到數(shù)據(jù)在哪個(gè)表里,可以大大提高查詢效率。

下面講到的都是如何水平拆分。

二、怎么做分庫(kù)分表

分庫(kù)分表已經(jīng)有一些成熟的解決方案,本文是用ShardingSphere-JDBC框架來(lái)實(shí)現(xiàn)的。

1.什么是ShardingSphere-JDBC

ShardingSphere-JDBC定義為輕量級(jí)Java框架,在 Java 的 JDBC 層提供的額外服務(wù)。 它使用客戶端直連數(shù)據(jù)庫(kù),以 jar 包形式提供服務(wù),無(wú)需額外部署和依賴,可理解為增強(qiáng)版的 JDBC 驅(qū)動(dòng),完全兼容 JDBC 和各種 ORM 框架。

  • 適用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC;
  • 支持任何第三方的數(shù)據(jù)庫(kù)連接池,如:DBCP, C3P0, BoneCP, HikariCP 等;
  • 支持任意實(shí)現(xiàn) JDBC 規(guī)范的數(shù)據(jù)庫(kù),目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用 JDBC 訪問(wèn)的數(shù)據(jù)庫(kù)。

更多詳細(xì)內(nèi)容可直接參考:ShardingSphere官方文檔

2.ShardingSphere-JDBC分表實(shí)踐

ShardingSphere-JDBC分庫(kù)和分表配置類似,下面介紹下分表怎么實(shí)現(xiàn)。

(1)先建分表

先在mysql數(shù)據(jù)庫(kù)建10張用戶表:tb_user_0到9,建表語(yǔ)句如下,改下表名,執(zhí)行10遍即可:

CREATE TABLE `tb_user_0`  (
  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性別',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

(2)POM依賴

使用spring boot + mybatis-plus + shardingsphere-jdbc來(lái)實(shí)現(xiàn),pom主要引入的包配置如下:

  <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
        </dependency>

(3)實(shí)體類和Mapper代碼

注意,實(shí)體類和Mapper只有一個(gè)就行,注意這里的tableName注解一定要和后面配置分表策略的邏輯名一致,不然無(wú)法匹配路由策略。

@TableName(value = "tb_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主鍵,注意此處IdType必須是AUTO,不然框架就會(huì)自動(dòng)生成id,分表時(shí)生成id的策略就不生效了
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 姓名
     */
    @TableField(value = "name")
    private String name;

    /**
     * 性別
     */
    @TableField(value = "sex")
    private String sex;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }

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

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" +
            "id=" + id +
            ", name=" + name +
            ", sex=" + sex +
        "}";
    }
}
public interface UserMapper extends BaseMapper<User> {

}

(4)配置數(shù)據(jù)源和分表規(guī)則

我們引入的包是shardingsphere-jdbc-core-spring-boot-starter,直接在application.yml里配置數(shù)據(jù)源和分表規(guī)則就行。

spring:
  shardingsphere:
    datasource:
      # 數(shù)據(jù)源名稱,有幾個(gè)數(shù)據(jù)源就寫(xiě)幾個(gè),如果是分表,就會(huì)寫(xiě)多個(gè)
      names: db0
      # 為每個(gè)數(shù)據(jù)源單獨(dú)配置,注意這里要跟上面寫(xiě)的名稱一致
      db0:
        # 數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn)類型,這里使用的是Hikari
        type: com.zaxxer.hikari.HikariDataSource
        # 數(shù)據(jù)庫(kù)驅(qū)動(dòng)類,連接地址,用戶名,密碼等
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/sharding?useUnicode=true&characterEncoding=utf8&useSSL=false
        username: root
        password: 123456
    rules:
      sharding:
        tables:
          # 分表的表名,程序中對(duì)這張表的操作,都會(huì)采用下面的路由方案
          tb_user:
            # 這里是實(shí)際的數(shù)據(jù)節(jié)點(diǎn)信息,要把庫(kù)名和表名都寫(xiě)全,這里也支持使用表達(dá)式,比如下面這張$->{0..9}
            actual-data-nodes: db0.tb_user_$->{0..9}
            # 配置分表策略
            table-strategy:
              # 這里選擇的標(biāo)準(zhǔn)策略,也可以配置復(fù)雜策略,或者也可以用代碼來(lái)實(shí)現(xiàn)
              standard:
                # 分片字段,這里是用用戶id作為分片字段
                sharding-column: id
                # 這里是我們自定義的分片算法名稱,后面會(huì)有實(shí)現(xiàn)方案
                sharding-algorithm-name: user-inline
            # 主鍵生成策略
            key-generate-strategy:
              # 生成主鍵算法的名稱
              key-generator-name: snowflake
              # 主鍵字段
              column: id
        # 自定義的主鍵算法
        key-generators:
          snowflake:
            # 使用雪花算法生成主鍵
            type: SNOWFLAKE
        # 自定義的分表算法
        sharding-algorithms:
          user-inline:
            #使用inline類型實(shí)現(xiàn)
            type: inline
            props:
              #分片表達(dá)式,用id對(duì)10取模,然后分散到10個(gè)表中
              algorithm-expression: tb_user_$->{id % 10}
    props:
      # 打印日志,方便我們觀察執(zhí)行的sql語(yǔ)句
      sql-show: true

(5)寫(xiě)單測(cè)

先測(cè)試插入語(yǔ)句,如下插入100條數(shù)據(jù):

@Autowired
    private UserMapper userMapper;

    @Test
    public void insertTest() {
        for (int i=0; i<100; i++) {
            User user = new User();
            user.setName("test" + i);
            user.setSex("男");
            userMapper.insert(user);
        }
    }

執(zhí)行之后,發(fā)現(xiàn)每張表都有數(shù)據(jù)插入,但是分布并不均勻,這是由雪花算法特性導(dǎo)致的。下圖是tb_user_0表的數(shù)據(jù):

再測(cè)試下查詢語(yǔ)句,先測(cè)試用id查詢:

@Test
    public void selectByIdTest() {
        userMapper.selectById(1668501944537858050L);
    }

查詢sql語(yǔ)句如下圖,從圖中可以看出,根據(jù)id查詢的時(shí)候,會(huì)自動(dòng)走分表路由策略,查詢id為1668501944537858050L的數(shù)據(jù),會(huì)自動(dòng)去tb_user_table_0中查找。

再測(cè)試一下根據(jù)name字段查詢:

    @Test
    public void selectByNameTest() {
        QueryWrapper<User> qy = new QueryWrapper<>();
        qy.eq("name","test1");
        userMapper.selectList(qy);
    }

查詢sql語(yǔ)句如下圖,從圖中可以看出,如果不是根據(jù)分表字段來(lái)查詢的話,會(huì)自動(dòng)union所有分表查詢,這樣反而效率會(huì)更低。

所以,分庫(kù)分表時(shí)一定要選擇合適的字段,并且查詢的時(shí)候盡量要在查詢條件里先指定分庫(kù)分表的字段,這樣可以直接定位到表中,提高查詢效率。

3.ShardingSphere-JDBC自定義分表策略類

ShardingSphere-JDBC可支持多種分片算法,比如標(biāo)準(zhǔn)分片,復(fù)合分片等,每種分片算法有多種類型,如行表達(dá)式INLINE,時(shí)間范圍分片INTERVAL等,上面的例子我們就是用的標(biāo)準(zhǔn)分片行表達(dá)式做的。對(duì)于一些需要自定義的分片算法,我們可以通過(guò)自定義分片算法類來(lái)實(shí)現(xiàn)。

比如我們還是要實(shí)現(xiàn)取模算法,可以自定義一個(gè)UserShardingAlgorithm類來(lái)實(shí)現(xiàn)StandardShardingAlgorithm接口,實(shí)現(xiàn)doSharding接口來(lái)自定義分片算法,代碼如下:

//分片字段數(shù)據(jù)類型是什么,這里泛型就寫(xiě)什么
public class UserShardingAlgorithm implements StandardShardingAlgorithm<Long> {

    //精確分片算法實(shí)現(xiàn),collection是實(shí)際表,也就是配置文件里的actual-data-nodes內(nèi)容
    //preciseShardingValue對(duì)象包括邏輯表名,分表算法的字段和字段值
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
        //對(duì)分片字段也就是用戶id取模
        String suffix = String.valueOf(preciseShardingValue.getValue() % 10);
        //遍歷表名,找到符合要求的表,返回即可
        for (String tableName : collection) {
            if (tableName.endsWith(suffix)) {
                return tableName;
            }
        }
        throw new UnsupportedOperationException();
    }

    //范圍分片,我們暫不支持
    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
        throw new UnsupportedOperationException();
    }
    
    //初始化信息接口
    @Override
    public void init() {

    }

    //分片算法類型
    @Override
    public String getType() {
        return "USER_SHARDING";
    }
}

在配置文件里,我們只需要改一下分片算法部分的配置即可,之前的配置是這樣的:

        sharding-algorithms:
          user-inline:
            type: inline
            props:
              algorithm-expression: tb_user_$->{id % 10}

分片類型改成class_based,也就是自定義類分片算法,配置如下:

        sharding-algorithms:
          user-inline:
            type: class_based # 自定義類分片算法類型
            props:
              strategy: standard
              # 自定義算法類的路徑
              algorithmClassName: com.github.learn.sharding.algorithm.UserShardingAlgorithm

還是再跑一下上面selectById單測(cè),如下圖,可以順利去tb_user_0中查詢數(shù)據(jù),證明我們自定義的分片算法生效了:

4.主鍵生成策略

ShardingSphere-JDBC提供了兩種內(nèi)置的分布式主鍵生成器,uuid和雪花算法。

uuid:采用UUID.randomUUID()的方式產(chǎn)生分布式主鍵。

雪花算法:

雪花算法是由 Twitter 公布的分布式主鍵生成算法,它能夠保證不同進(jìn)程主鍵的不重復(fù)性,以及相同進(jìn)程主鍵的有序性。

(1)實(shí)現(xiàn)原理

在同一個(gè)進(jìn)程中,它首先是通過(guò)時(shí)間位保證不重復(fù),如果時(shí)間相同則是通過(guò)序列位保證。 同時(shí)由于時(shí)間位是單調(diào)遞增的,且各個(gè)服務(wù)器如果大體做了時(shí)間同步,那么生成的主鍵在分布式環(huán)境可以認(rèn)為是總體有序的,這就保證了對(duì)索引字段的插入的高效性。 例如 MySQL 的 Innodb 存儲(chǔ)引擎的主鍵。

使用雪花算法生成的主鍵,二進(jìn)制表示形式包含 4 部分,從高位到低位分表為:1bit 符號(hào)位、41bit 時(shí)間戳位、10bit 工作進(jìn)程位以及 12bit 序列號(hào)位。

  • 符號(hào)位(1bit)

預(yù)留的符號(hào)位,恒為零。

  • 時(shí)間戳位(41bit)

41 位的時(shí)間戳可以容納的毫秒數(shù)是 2 的 41 次冪,一年所使用的毫秒數(shù)是:365 * 24 * 60 * 60 * 1000。 通過(guò)計(jì)算可知:

  • Math.pow(2,41)/(365*24*60*60*1000L);

結(jié)果約等于 69.73 年。 Apache ShardingSphere 的雪花算法的時(shí)間紀(jì)元從 2016年11月1日 零點(diǎn)開(kāi)始,可以使用到 2086 年,相信能滿足絕大部分系統(tǒng)的要求。

  • 工作進(jìn)程位(10bit)

該標(biāo)志在 Java 進(jìn)程內(nèi)是唯一的,如果是分布式應(yīng)用部署應(yīng)保證每個(gè)工作進(jìn)程的 id 是不同的。 該值默認(rèn)為 0,可通過(guò)屬性設(shè)置。

  • 序列號(hào)位(12bit)

該序列是用來(lái)在同一個(gè)毫秒內(nèi)生成不同的 ID。如果在這個(gè)毫秒內(nèi)生成的數(shù)量超過(guò) 4096 (2 的 12 次冪),那么生成器會(huì)等待到下個(gè)毫秒繼續(xù)生成。

雪花算法主鍵的詳細(xì)結(jié)構(gòu)見(jiàn)下圖。

(2)配置信息

在ShardingSphere-JDBC中,雪花算法提供了三個(gè)屬性。

worker-id:工作機(jī)器唯一標(biāo)識(shí)

max-vibration-offset:最大抖動(dòng)上限值,范圍[0, 4096)。注:若使用此算法生成值作分片值,建議配置此屬性。此算法在不同毫秒內(nèi)所生成的 key 取模 2^n (2^n一般為分庫(kù)或分表數(shù)) 之后結(jié)果總為 0 或 1。為防止上述分片問(wèn)題,建議將此屬性值配置為 (2^n)-1。如果有10個(gè)分表,可將此值設(shè)置為9,這樣數(shù)據(jù)分布會(huì)更均勻一下。

max-tolerate-time-difference-milliseconds:最大容忍時(shí)鐘回退時(shí)間,單位:毫秒,默認(rèn)10毫秒

(3)多節(jié)點(diǎn)worker-id配置

服務(wù)器可能是有多個(gè)節(jié)點(diǎn)的,此時(shí)如果worker-id用同一個(gè)配置,有可能會(huì)產(chǎn)生重復(fù)的id,因此每個(gè)節(jié)點(diǎn)的worker-id最好是不同的。我們可以用ip地址的一部分來(lái)作為節(jié)點(diǎn)的worker-id,worker-id是十位,我們直接取ip地址的后10位即可,一般都是不會(huì)重復(fù)的。比如機(jī)器的IP為192.168.1.108,二進(jìn)制表示:11000000 10101000 00000001 01101100,截取最后10位 01 01101100,轉(zhuǎn)為十進(jìn)制364,設(shè)置workerId為364。

實(shí)現(xiàn)方式如下:

首先是配置文件,要加入work-id屬性配置:

        key-generators:
          user-id-generator:
            type: SNOWFLAKE
            props:
              max-vibration-offset: 9
              worker-id: ${workerId}

然后,加一個(gè)配置類,在static代碼塊中獲取ip地址,取后十位,作為worker-id。

@Configuration
public class WorkerIdConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(WorkerIdConfig.class);

    static {
        try {
            InetAddress address = InetAddress.getLocalHost();
            // IP地址byte[]數(shù)組形式,這個(gè)byte數(shù)組的長(zhǎng)度是4,數(shù)組0~3下標(biāo)對(duì)應(yīng)的值分別是192,168,1,108
            byte[] ipAddressByteArray = address.getAddress();
            // workerId取ip地址后十位
            long workerId = ((ipAddressByteArray[ipAddressByteArray.length - 2] & 0x03) << 8) + (ipAddressByteArray[ipAddressByteArray.length - 1] & 0xFF);
            LOGGER.info("當(dāng)前機(jī)器 workerId: {}", workerId);
            System.setProperty("workerId", String.valueOf(workerId));
        } catch (Exception e) {
            LOGGER.error("worker id failed:{}", e.getMessage(), e);
        }
    }
}

總結(jié) 

到此這篇關(guān)于Java實(shí)現(xiàn)分庫(kù)分表的文章就介紹到這了,更多相關(guān)Java分庫(kù)分表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring?Boot應(yīng)用程序中如何使用Keycloak詳解

    Spring?Boot應(yīng)用程序中如何使用Keycloak詳解

    這篇文章主要為大家介紹了Spring?Boot應(yīng)用程序中如何使用Keycloak詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • JAVA NIO實(shí)現(xiàn)簡(jiǎn)單聊天室功能

    JAVA NIO實(shí)現(xiàn)簡(jiǎn)單聊天室功能

    這篇文章主要為大家詳細(xì)介紹了JAVA NIO實(shí)現(xiàn)簡(jiǎn)單聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • SpringBoot中使用異步調(diào)度程序的高級(jí)方法

    SpringBoot中使用異步調(diào)度程序的高級(jí)方法

    本文主要介紹了SpringBoot中使用異步調(diào)度程序的高級(jí)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • SpringCloud:feign對(duì)象傳參和普通傳參及遇到的坑解決

    SpringCloud:feign對(duì)象傳參和普通傳參及遇到的坑解決

    這篇文章主要介紹了SpringCloud:feign對(duì)象傳參和普通傳參及遇到的坑解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringBoot實(shí)現(xiàn)文件斷點(diǎn)續(xù)傳功能詳解

    SpringBoot實(shí)現(xiàn)文件斷點(diǎn)續(xù)傳功能詳解

    在處理大文件傳輸或網(wǎng)絡(luò)不穩(wěn)定的情況下,文件斷點(diǎn)續(xù)傳功能顯得尤為重要,本文將詳細(xì)介紹如何使用Spring Boot實(shí)現(xiàn)文件的斷點(diǎn)續(xù)傳功能,需要的可以了解下
    2025-04-04
  • Spring MVC深入學(xué)習(xí)之啟動(dòng)初始化過(guò)程

    Spring MVC深入學(xué)習(xí)之啟動(dòng)初始化過(guò)程

    最近因?yàn)楣ぷ鞯脑蛟趯W(xué)習(xí)Spring MVC,為了更深入的學(xué)習(xí)Spring MVC,下面這篇文章主要給大家介紹了關(guān)于Spring MVC深入學(xué)習(xí)之啟動(dòng)初始化過(guò)程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-07-07
  • SpringSecurity自動(dòng)登錄流程與實(shí)現(xiàn)詳解

    SpringSecurity自動(dòng)登錄流程與實(shí)現(xiàn)詳解

    這篇文章主要介紹了SpringSecurity自動(dòng)登錄流程與實(shí)現(xiàn)詳解,所謂的自動(dòng)登錄是在訪問(wèn)鏈接時(shí)瀏覽器自動(dòng)攜帶上了Cookie中的Token交給后端校驗(yàn),如果刪掉了Cookie或者過(guò)期了同樣是需要再次驗(yàn)證的,需要的朋友可以參考下
    2024-01-01
  • Java中BigDecimal與0比較的一個(gè)坑實(shí)戰(zhàn)記錄

    Java中BigDecimal與0比較的一個(gè)坑實(shí)戰(zhàn)記錄

    BigDecimal屬于大數(shù)據(jù),精度極高,不屬于基本數(shù)據(jù)類型,屬于java對(duì)象,下面這篇文章主要給大家介紹了關(guān)于Java中BigDecimal與0比較的一個(gè)坑的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • Spring集成Seata方式(案例演示)

    Spring集成Seata方式(案例演示)

    這篇文章主要介紹了Spring集成Seata方式,本案例使用Seata-All演示,結(jié)合實(shí)例代碼給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • springmvc使用@notNull注解驗(yàn)證請(qǐng)求參數(shù)方式

    springmvc使用@notNull注解驗(yàn)證請(qǐng)求參數(shù)方式

    這篇文章主要介紹了springmvc使用@notNull注解驗(yàn)證請(qǐng)求參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教<BR>
    2024-01-01

最新評(píng)論