想把 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)
步骤:
- 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;
}
- 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;
}
- 因 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;
}
- 但是, mysql 安装上自定义 hashcode 扩展; 并执行 select hashcode(“xxxx”)
执行后返回的值, 竟然能突破 int32_t 的最大值, 就好像 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 的基本语法, 写了个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
- demo中打印出的 “-2147483648” 说明, 会发生溢出; 但是为啥 mysql扩展函数执行后, 反而不会溢出呢?