如何解决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 一样

int 溢出问题,String.hashCode移植到 mysql 中,数据迁移,分库分表策略

改写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;
}
int 溢出问题,String.hashCode移植到 mysql 中,数据迁移,分库分表策略

mysql扩展函数执行后, 返回 “2147483648”; 而不是期望的 -2147483648

  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


评论区(0)

评论