主頁 > 後端開發 > 分庫分表用這個就夠了

分庫分表用這個就夠了

2023-06-13 07:45:45 後端開發

一、前言

2018年寫過一篇分庫分表的文章《SpringBoot使用sharding-jdbc分庫分表》,但是存在很多不完美的地方比如:

  • sharding-jdbc的版本(1.4.2)過低,現在github上的最新版本都是5.3.2了,很多用法和API都過時了,
  • 分庫分表配置采用Java硬編碼的方式不夠靈活
  • 持久層使用的是spring-boot-starter-data-jpa,而不是主流的mybatis+mybatis-plus+druid-spring-boot-stater
  • 沒有支持自定義主鍵生成策略

二、設計思路

針對上述問題,本人計劃開發一個通用的分庫分表starter,具備以下特性:

  1. 基于ShardingSphere-JDBC版本4.1.1,官方支持的特性我們都支持
  2. 支持yaml檔案配置,無需編碼開箱即用
  3. 支持多種資料源,整合主流的mybatis
  4. 支持自定義主鍵生成策略,并提供默認的雪花演算法實作

通過查看官方檔案,可以發現starter的核心邏輯就是獲取分庫分表等配置,然后在自動配置類創建資料源注入Spring容器即可,

三、編碼實作

3.1 starter工程搭建

首先創建一個spring-boot-starter工程ship-sharding-spring-boot-starter,不會的小伙伴可以參考以前寫的教程《【SpringBoot】撰寫一個自己的Starter》,

創建自動配置類cn.sp.sharding.config.ShardingAutoConfig,并在resources/META-INF/spring.factories檔案中配置自動配置類的全路徑,

org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.sp.sharding.config.ShardingAutoConfig

然后需要在pom.xml檔案引入sharding-jbc依賴和工具包guava,

    <properties>
        <java.version>8</java.version>
        <spring-boot.version>2.4.0</spring-boot.version>
        <sharding-jdbc.version>4.1.1</sharding-jdbc.version>
    </properties>
    
      <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>${sharding-jdbc.version}</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>

3.2 注入ShardingDataSource

分庫分表配置這塊,為了方便自定義配置前綴,創建ShardingRuleConfigurationProperties類繼承sharding-jbc的YamlShardingRuleConfiguration類即可,代碼如下:

/**
 * @author Ship
 * @version 1.0.0
 * @description:
 * @date 2023/06/06
 */
@ConfigurationProperties(prefix = CommonConstants.COMMON_CONFIG_PREFIX + ".config")
public class ShardingRuleConfigurationProperties extends YamlShardingRuleConfiguration {


}

同時sharding-jbc支持自定義一些properties屬性,需要單獨創建類ConfigMapConfigurationProperties

/**
 * @Author: Ship
 * @Description:
 * @Date: Created in 2023/6/6
 */
@ConfigurationProperties(prefix = CommonConstants.COMMON_CONFIG_PREFIX + ".map")
public class ConfigMapConfigurationProperties {

    private Properties props = new Properties();


    public Properties getProps() {
        return props;
    }

    public void setProps(Properties props) {
        this.props = props;
    }
}

官方提供了ShardingDataSourceFactory工廠類來創建資料源,但是查看其原始碼發現createDataSource方法的引數是ShardingRuleConfiguration類,而不是YamlShardingRuleConfiguration

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ShardingDataSourceFactory {
    
    /**
     * Create sharding data source.
     *
     * @param dataSourceMap data source map
     * @param shardingRuleConfig rule configuration for databases and tables sharding
     * @param props properties for data source
     * @return sharding data source
     * @throws SQLException SQL exception
     */
    public static DataSource createDataSource(
            final Map<String, DataSource> dataSourceMap, final ShardingRuleConfiguration shardingRuleConfig, final Properties props) throws SQLException {
        return new ShardingDataSource(dataSourceMap, new ShardingRule(shardingRuleConfig, dataSourceMap.keySet()), props);
    }
}

該如何解決配置類引數轉換的問題呢?

幸好查找官方檔案發現sharding-jdbc提供了YamlSwapper類來實作yaml配置和核心配置的轉換

/**
 * YAML configuration swapper.
 *
 * @param <Y> type of YAML configuration
 * @param <T> type of swapped object
 */
public interface YamlSwapper<Y extends YamlConfiguration, T> {
    
    /**
     * Swap to YAML configuration.
     *
     * @param data data to be swapped
     * @return YAML configuration
     */
    Y swap(T data);
    
    /**
     * Swap from YAML configuration to object.
     *
     * @param yamlConfiguration YAML configuration
     * @return swapped object
     */
    T swap(Y yamlConfiguration);
}

ShardingRuleConfigurationYamlSwapper就是YamlSwapper的其中一個實作類,

于是,ShardingAutoConfig的最終代碼如下:

package cn.sp.sharding.config;

/**
 * @author Ship
 * @version 1.0.0
 * @description:
 * @date 2023/06/06
 */
@AutoConfigureBefore(name = CommonConstants.MYBATIS_PLUS_CONFIG_CLASS)
@Configuration
@EnableConfigurationProperties(value = https://www.cnblogs.com/2YSP/archive/2023/06/12/{ShardingRuleConfigurationProperties.class, ConfigMapConfigurationProperties.class})
@Import(DataSourceHealthConfig.class)
public class ShardingAutoConfig implements EnvironmentAware {


    private Map dataSourceMap = new HashMap<>();

    @ConditionalOnMissingBean
    @Bean
    public DataSource shardingDataSource(@Autowired ShardingRuleConfigurationProperties configurationProperties,
                                         @Autowired ConfigMapConfigurationProperties configMapConfigurationProperties) throws SQLException {
        ShardingRuleConfigurationYamlSwapper yamlSwapper = new ShardingRuleConfigurationYamlSwapper();
        ShardingRuleConfiguration shardingRuleConfiguration = yamlSwapper.swap(configurationProperties);
        return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfiguration, configMapConfigurationProperties.getProps());
    }

    @Override
    public void setEnvironment(Environment environment) {
        setDataSourceMap(environment);
    }

    private void setDataSourceMap(Environment environment) {
        String names = environment.getProperty(CommonConstants.DATA_SOURCE_CONFIG_PREFIX +".names");
        for (String name : names.split(",")) {
            try {
                String propertiesPrefix = CommonConstants.DATA_SOURCE_CONFIG_PREFIX + "." + name;
                Map<String, Object> dataSourceProps = PropertyUtil.handle(environment, propertiesPrefix, Map.class);
                // 反射創建資料源
                DataSource dataSource = DataSourceUtil.getDataSource(dataSourceProps.get("type").toString(), dataSourceProps);
                dataSourceMap.put(name, dataSource);
            } catch (ReflectiveOperationException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

利用反射創建資料源,就可以解決支持多種資料源的問題,

3.3 自定義主鍵生成策略

sharding-jdbc提供了UUID和Snowflake兩種默認實作,但是自定義主鍵生成策略更加靈活,方便根據自己的需求調整,接下來介紹如何自定義主鍵生成策略,

因為我們也是用的雪花演算法,所以可以直接用sharding-jdbc提供的雪花演算法類,KeyGeneratorFactory負責生成雪花演算法實作類的實體,采用雙重校驗加鎖的單例模式,

public final class KeyGeneratorFactory {
    /**
     * 使用shardingsphere提供的雪花演算法實作
     */
    private static volatile SnowflakeShardingKeyGenerator keyGenerator = null;

    private KeyGeneratorFactory() {

    }

    /**
     * 單例模式
     *
     * @return
     */
    public static SnowflakeShardingKeyGenerator getInstance() {
        if (keyGenerator == null) {
            synchronized (KeyGeneratorFactory.class) {
                if (keyGenerator == null) {
                    // 用ip地址當作機器id,機器范圍0-1024
                    Long workerId = Long.valueOf(IpUtil.getLocalIpAddress().replace(".", "")) % 1024;
                    keyGenerator = new SnowflakeShardingKeyGenerator();
                    Properties properties = new Properties();
                    properties.setProperty("worker.id", workerId.toString());
                    keyGenerator.setProperties(properties);
                }
            }
        }
        return keyGenerator;
    }
}

雪花演算法是由1bit 不用 + 41bit時間戳+10bit作業機器id+12bit序列號組成的,所以為了防止不同節點生成的id重復需要設定機器id,機器id的范圍是0-1024,這里是用IP地址轉數字取模1024來計算機器id,存在很小概率的重復,也可以用redis來生成機器id(參考雪花演算法ID重復問題的解決方案 ),

注意: 雪花演算法坑其實挺多的,除了系統時間回溯會導致id重復,單節點并發過高也會導致重復(序列位只有12位代表1ms內最多支持4096個并發),

查看原始碼可知自定義主鍵生成器是通過SPI實作的,實作ShardingKeyGenerator介面即可,

package org.apache.shardingsphere.spi.keygen;

import org.apache.shardingsphere.spi.TypeBasedSPI;

/**
 * Key generator.
 */
public interface ShardingKeyGenerator extends TypeBasedSPI {
    
    /**
     * Generate key.
     * 
     * @return generated key
     */
    Comparable<?> generateKey();
}
  1. 自定義主鍵生成器DistributedKeyGenerator
/**
 * @Author: Ship
 * @Description: 分布式id生成器,雪花演算法實作
 * @Date: Created in 2023/6/8
 */
public class DistributedKeyGenerator implements ShardingKeyGenerator {

    @Override
    public Comparable<?> generateKey() {
        return KeyGeneratorFactory.getInstance().generateKey();
    }

    @Override
    public String getType() {
        return "DISTRIBUTED";
    }

    @Override
    public Properties getProperties() {
        return null;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
  1. 創建META-INF/services檔案夾,然后在檔案夾下創建org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator檔案,內容如下:
 cn.sp.sharding.key.DistributedKeyGenerator
  1. yaml檔案配置即可

3.4 遺留問題

Spring Boot會在專案啟動時執行一條sql陳述句檢查資料源是否可用,因為ShardingDataSource只是對真實資料源進行了封裝,沒有完全實作Datasouce介面規范,所以會在啟動時報錯DataSource health check failed,為此需要重寫資料源健康檢查的邏輯,

創建DataSourceHealthConfig類繼承DataSourceHealthContributorAutoConfiguration,然后重寫createIndicator方法來重新設定校驗sql陳述句

/**
 * @Author: Ship
 * @Description:
 * @Date: Created in 2023/6/7
 */
public class DataSourceHealthConfig extends DataSourceHealthContributorAutoConfiguration {

    private static String validQuery = "SELECT 1";

    public DataSourceHealthConfig(Map<String, DataSource> dataSources, ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
        super(dataSources, metadataProviders);
    }

    @Override
    protected AbstractHealthIndicator createIndicator(DataSource source) {
        DataSourceHealthIndicator healthIndicator = (DataSourceHealthIndicator) super.createIndicator(source);
        if (StringUtils.hasText(validQuery)) {
            healthIndicator.setQuery(validQuery);
        }
        return healthIndicator;
    }
}

最后使用@Import注解來注入

@AutoConfigureBefore(name = CommonConstants.MYBATIS_PLUS_CONFIG_CLASS)
@Configuration
@EnableConfigurationProperties(value = https://www.cnblogs.com/2YSP/archive/2023/06/12/{ShardingRuleConfigurationProperties.class, ConfigMapConfigurationProperties.class})
@Import(DataSourceHealthConfig.class)
public class ShardingAutoConfig implements EnvironmentAware {

四、測驗

假設有個訂單表資料量很大了需要分表,為了方便水平擴展,根據訂單的創建時間分表,分表規則如下:

t_order_${創建時間所在年}_${創建時間所在季度}

訂單表結構如下

CREATE TABLE `t_order_2022_3` (
  `id` bigint(20) unsigned NOT NULL COMMENT '主鍵',
  `order_code` varchar(32) DEFAULT NULL COMMENT '訂單號',
  `create_time` bigint(20) NOT NULL COMMENT '創建時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  1. 創建資料庫my_springboot,并創建8張訂單表t_order_2022_1至t_order_2023_4

訂單表

  1. 創建SpringBoot專案ship-sharding-example,并添加mybatis等相關依賴
  <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>


        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <dependency>
            <groupId>cn.sp</groupId>
            <artifactId>ship-sharding-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

  1. 創建訂單物體Order和OrderMapper,代碼比較簡單省略
  2. 自定義分表演算法需要實作PreciseShardingAlgorithm和RangeShardingAlgorithm介面的方法,它倆區別如下
介面 描述
PreciseShardingAlgorithm 定義等值查詢條件下的分表演算法
RangeShardingAlgorithm 定義范圍查詢條件下的分表演算法

創建演算法類MyTableShardingAlgorithm

/**
 * @Author: Ship
 * @Description:
 * @Date: Created in 2023/6/8
 */
@Slf4j
public class MyTableShardingAlgorithm implements PreciseShardingAlgorithm<Long>, RangeShardingAlgorithm<Long> {

    private static final String TABLE_NAME_PREFIX = "t_order_";

    @Override
    public String doSharding(Collection<String> availableTableNames, PreciseShardingValue<Long> preciseShardingValue) {
        Long createTime = preciseShardingValue.getValue();
        if (createTime == null) {
            throw new ShipShardingException("創建時間不能為空!");
        }
        LocalDate localDate = DateUtils.longToLocalDate(createTime);
        final String year = localDate.getYear() + "";
        Integer quarter = DateUtils.getQuarter(localDate);
        for (String tableName : availableTableNames) {
            String dateStr = tableName.replace(TABLE_NAME_PREFIX, "");
            String[] dateArr = dateStr.split("_");
            if (dateArr[0].equals(year) && dateArr[1].equals(quarter.toString())) {
                return tableName;
            }
        }
        log.error("分表演算法對應的表不存在!");
        throw new ShipShardingException("分表演算法對應的表不存在!");
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTableNames, RangeShardingValue<Long> rangeShardingValue) {
        //獲取查詢條件中范圍值
        Range<Long> valueRange = rangeShardingValue.getValueRange();
        // 上限值
        Long upperEndpoint = valueRange.upperEndpoint();
        // 下限值
        Long lowerEndpoint = valueRange.lowerEndpoint();

        List<String> tableNames = Lists.newArrayList();
        for (String tableName : availableTableNames) {
            String dateStr = tableName.replace(MyTableShardingAlgorithm.TABLE_NAME_PREFIX, "");
            String[] dateArr = dateStr.split("_");
            String year = dateArr[0];
            String quarter = dateArr[1];
            Long[] minAndMaxTime = DateUtils.getMinAndMaxTime(year, quarter);
            Long minTime = minAndMaxTime[0];
            Long maxTime = minAndMaxTime[1];
            if (valueRange.hasLowerBound() && valueRange.hasUpperBound()) {
                // between and
                if (minTime.compareTo(lowerEndpoint) <= 0 && upperEndpoint.compareTo(maxTime) <= 0) {
                    tableNames.add(tableName);
                }
            } else if (valueRange.hasLowerBound() && !valueRange.hasUpperBound()) {
                if (maxTime.compareTo(lowerEndpoint) > 0) {
                    tableNames.add(tableName);
                }
            } else {
                if (upperEndpoint.compareTo(minTime) > 0) {
                    tableNames.add(tableName);
                }
            }
        }
        if (tableNames.size() == 0) {
            log.error("分表演算法對應的表不存在!");
            throw new ShipShardingException("分表演算法對應的表不存在!");
        }
        return tableNames;
    }
}

  1. 在application.yaml上添加資料庫配置和分表配置
spring:
  application:
    name: ship-sharding-example


mybatis-plus:
  base-package: cn.sp.sharding.dao
  mapper-locations: classpath*:/mapper/*Mapper.xml
  configuration:
    #開啟自動駝峰命名規則(camel case)映射
    map-underscore-to-camel-case: true
    #延遲加載,需要和lazy-loading-enabled一起使用
    aggressive-lazy-loading: true
    lazy-loading-enabled: true
    #關閉一級快取
    local-cache-scope: statement
    #關閉二級級快取
    cache-enabled: false

ship:
  sharding:
    jdbc:
      datasource:
        names: ds0
        ds0:
          driver-class-name: com.mysql.cj.jdbc.Driver
          type: com.alibaba.druid.pool.DruidDataSource
          url: jdbc:mysql://127.0.0.1:3306/my_springboot?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
          username: root
          password: 1234
          initial-size: 5
          minIdle: 5
          maxActive: 20
          maxWait: 60000
          timeBetweenEvictionRunsMillis: 60000
          minEvictableIdleTimeMillis: 300000
          validationQuery: SELECT 1 FROM DUAL
          testWhileIdle: true
          testOnBorrow: false
          testOnReturn: false
          poolPreparedStatements: true
          maxPoolPreparedStatementPerConnectionSize: 20
          useGlobalDataSourceStat: true
          connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000;druid.mysql.usePingMethod=false
      config:
        binding-tables: t_order
        tables:
          t_order:
            actual-data-nodes: ds0.t_order_${2022..2023}_${1..4}
            # 配置主鍵生成策略
            key-generator:
              type: DISTRIBUTED
              column: id
            table-strategy:
              standard:
                sharding-column: create_time
                # 配置分表演算法
                precise-algorithm-class-name: cn.sp.sharding.algorithm.MyTableShardingAlgorithm
                range-algorithm-class-name: cn.sp.sharding.algorithm.MyTableShardingAlgorithm
  1. 現在可以進行測驗了,首先寫一個單元測驗測驗資料插入情況,
 @Test
    public void testInsert() {
        Order order = new Order();
        order.setOrderCode("OC001");
        order.setCreateTime(System.currentTimeMillis());
        orderMapper.insert(order);
    }

運行testInsert()方法,打開t_order_2023_2表發現已經有了一條訂單資料

image

并且該資料的create_time是1686383781371,轉換為時間為2023-06-10 15:56:21,剛好對應2023年第二季度,說明資料正確的路由到了對應的表里,

然后測驗下資料查詢情況

@Test
    public void testQuery(){
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Order::getOrderCode,"OC001");
        List<Order> orders = orderMapper.selectList(wrapper);
        System.out.println(JSONUtil.toJsonStr(orders));
    }

運行testQuery()方法后可以在控制臺看到輸出了訂單報文,說明查詢也沒問題,

[{"id":1667440550397132802,"orderCode":"OC001","createTime":1686383781371}]

五、總結

本文代碼已經上傳到github,后續會把ship-sharding-spring-boot-starter上傳到maven中央倉庫方便使用,如果覺得對你有用的話希望可以點個贊讓更多人看到??

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/554952.html

標籤:其他

上一篇:掌握Python檔案操作:從基礎到高階的全方位探索

下一篇:返回列表

標籤雲
其他(160820) Python(38222) JavaScript(25492) Java(18225) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7247) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5874) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4589) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2435) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) 功能(1967) HtmlCss(1962) Web開發(1951) C++(1933) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • 分庫分表用這個就夠了

    # 一、前言 2018年寫過一篇分庫分表的文章《[SpringBoot使用sharding-jdbc分庫分表](https://www.cnblogs.com/2YSP/p/9746981.html)》,但是存在很多不完美的地方比如: - sharding-jdbc的版本(1.4.2)過低,現在gi ......

    uj5u.com 2023-06-13 07:45:45 more
  • 掌握Python檔案操作:從基礎到高階的全方位探索

    **在本篇博客中,我們將全面、深入地探討Python中的檔案操作。檔案操作在Python編程中是不可或缺的一部分,它包含了打開、讀取、寫入和關閉檔案等各種操作。我們將從基礎的檔案操作講解到高級的檔案處理技巧,以及如何優雅地使用Python進行檔案操作。每一部分我們都會分享一些獨特的用法,并且附有具體 ......

    uj5u.com 2023-06-13 07:45:39 more
  • 【技識訓累】Java中的泛型【一】

    博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ......

    uj5u.com 2023-06-13 07:44:25 more
  • CoaXpress downlink資料決議方法

    ## 什么是downlink資料 downlink指的是相機傳輸到host采集卡的高速鏈路,其中包含了如下型別的資料: 1、Stream Data 2、Trigger Ack, Trigger; 3、Ack (reply data); 4、Event, Heartbeat ![](https://i ......

    uj5u.com 2023-06-13 07:43:58 more
  • 【python基礎】復雜資料型別-字典(遍歷)

    一個字典可能只包含幾個鍵值對,也可能包含數百萬個鍵值對,所以Python支持字典遍歷。字典可用于以各種方式存盤資訊,因此有多種遍歷字典的方式:可遍歷字典的所有鍵值對、鍵或值。 # 1.遍歷所有的鍵值對 其語法格式: ![image](https://img2023.cnblogs.com/blog/ ......

    uj5u.com 2023-06-13 07:43:14 more
  • 掌握Python檔案操作:從基礎到高階的全方位探索

    **在本篇博客中,我們將全面、深入地探討Python中的檔案操作。檔案操作在Python編程中是不可或缺的一部分,它包含了打開、讀取、寫入和關閉檔案等各種操作。我們將從基礎的檔案操作講解到高級的檔案處理技巧,以及如何優雅地使用Python進行檔案操作。每一部分我們都會分享一些獨特的用法,并且附有具體 ......

    uj5u.com 2023-06-13 07:42:50 more
  • 【python基礎】復雜資料型別-字典(增刪改查)

    # 1.初識字典 字典,是另外一種復雜的資料型別,相較于串列,字典可以將相關資訊關聯起來。比如說一個人的資訊有名字、年齡、性別等,如果用串列存盤的話,不能表示他們之間是相關聯的,而字典可以,字典是一個或多個鍵值對組成 串列的標志是[ ],**字典的標志是{ }**,其語法格式: **{** 鍵1:值 ......

    uj5u.com 2023-06-13 07:42:41 more
  • php檔案上傳之白名單00截斷實驗

    # %00截斷 **介紹:** > 0x00,%00,/00 在url中 %00 表示ascll碼中的 0 ,而ascii中0作為特殊字符保留,表示字串結束,所以當url中出現%00時就會認為讀取已結束。但是所謂的if攔截仍會讀取后面的后綴達到繞過白名單的效果。 當前版本環境: PHP版本低于5. ......

    uj5u.com 2023-06-13 07:42:14 more
  • Servlet p1 Servlet的實作

    學習課程: 【這可能是B站講的最好的Servlet教程,5小時打通Servlet全套教程丨2022最新版,輕松掌握servlet基礎+案例實操】 https://www.bilibili.com/video/BV1Kr4y1V7ZE/?share_source=copy_web&vd_source= ......

    uj5u.com 2023-06-13 07:41:56 more
  • Object原始碼閱讀

    # Object原始碼閱讀 > native:本地堆疊方法,使用C語言中實作的方法。 ```java package java.lang; public class Object { //注冊本地方法 private static native void registerNatives(); stati ......

    uj5u.com 2023-06-13 07:41:51 more