想把 java 中的 String.hashCode, 移植到 mysql 中,遇到 int 溢出问题

背景:

  • a. 想把 java中的String.hashCode, 移植到 mysql 中

  • b. 为什么移植? 有个数据迁移的需求, 原始数据源是单库单表, 目标数据源是8库8表; 想用mysql语句能完成分表分库的导入数据操作, 但是遇到需要用mysql语句来实现 shardingJdbc的分库分表策略;默认的PreciseTableAlgorithm策略算法中,把原始值进行了hashCode, 再进行取余分片运算

      // 核心代码
      private String doNormalShard(Collection tableNames, PreciseShardingValue value) {
          //获取hash值
          int hashCode = String.valueOf(value.getValue()).hashCode();
          if (hashCode <= 0) {
              hashCode = 0 - hashCode;
          }
          for (Object each : tableNames) {
              int mod = hashCode % tableNames.size();
              String logicTableName = String.valueOf(each);
              if (logicTableName.endsWith(mod + "")) {
                  return logicTableName;
              }
          }
          throw new UnsupportedOperationException();
      }
  • b. C语言小白; 有php, java 基础

环境:

➜  ~ gcc --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

➜  ~ mysql --version
mysql  Ver 14.14 Distrib 5.7.32, for osx10.13 (x86_64) using  EditLine wrapper

➜  ~ java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

步骤:

  1. java hashcode 核心算法
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
  1. google到一个 mysql自定义 hashCode 扩展

链接:
philipzhong.blogspot.com/2011/08/me...

核心算法截取:
uint32_t  hashcode_java(uchar* input,size_t len)
{
    uint32_t hash = 0;
    size_t i;
    for (i = 0; i < len; i++) {
       hash = 31 * hash + input[i];
    }
    return hash;
}
  1. 因 Java 的int类型是有符号的4字节; 而 C 的uint32_t类型是无符号的4字节; 溢出时表现不一样, 所以在此基础上进行改写;就把 uint32_t 换成 int32_t
核心算法截取:
int32_t  hashcode_java(uchar* input,size_t len)
{
    int32_t hash = 0;
    size_t i;
    for (i = 0; i < len; i++) {
       hash = 31 * hash + input[i];
    }

    return hash;
}
  1. 但是, mysql 安装上自定义 hashcode 扩展; 并执行 select hashcode(“xxxx”)

执行后返回的值, 竟然能突破 int32_t 的最大值, 就好像 int32_t 自动升级为 int64_t 一样

c 中 int32_t类型的变量发生溢出时, 会自动升级为 int64_t 嘛?

改写mysql扩展中核心算法,返回值直接写死 ,int32_t 的最大值加1

核心算法截取:
int32_t  hashcode_java(uchar* input,size_t len)
{
    // int32_t hash = 0;
    // size_t i;
    // for (i = 0; i < len; i++) {
    //    hash = 31 * hash + input[i];
    // }

    // debug start 
    // int32_t  min:-2,147,483,648  max:2,147,483,647
    // 调试, 使返回值 int32_t 溢出 
    // 期望 mysql 自定义函数, 返回:  -2147483648
    int32_t hash = 2147483648;
    // debug end

    return hash;
}

mysql扩展函数执行后, 返回 “2147483648”; 而不是期望的 -2147483648
c 中 int32_t类型的变量发生溢出时, 会自动升级为 int64_t 嘛?

  1. 花了点时间, 学习了下 C 的基本语法, 写了个demo; 验证下, int32_t 是否会发生溢出
// demo代码:
#include <stdio.h>

int32_t  hashcode_java()
{
    // int32_t  min:-2,147,483,648  max:2,147,483,647
    // debug 调试, 使返回值 int32_t 溢出 
    int32_t hash = 2147483648;
    return hash;
}

int main()
{
    int32_t hash = hashcode_java();
    printf("%d\n", hash);
    return 0;
}

gcc调试:

➜  ~ gcc hashCodeDemo.cc -o hashCodeDemo
hashCodeDemo.cc:7:20: warning: implicit conversion from 'long' to 'int32_t'
      (aka 'int') changes value from 2147483648 to -2147483648
      [-Wconstant-conversion]
    int32_t hash = 2147483648;
            ~~~~   ^~~~~~~~~~
1 warning generated.


➜  ~ ./hashCodeDemo
-2147483648
  1. demo中打印出的 “-2147483648” 说明, 会发生溢出; 但是为啥 mysql扩展函数执行后, 反而不会溢出呢?
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!