分库分表--数据初始化迁移

  • 前言

分库分表,必不可少的步骤就是数据迁移。数据迁移又分两种,一种是初始化迁移,另一种是上线期间的数据迁移。这里先考虑的是初始化迁移。

何为初始化迁移?其实就是将老库的绝大部分数据转移到新库中,一般这种迁移都是针对的是固定的数据。

  • 场景

      1.停机迁移(不推荐)
      2.数据双写迁移
      3.采用canal中间件迁移
    

以上三种一般是数据迁移的方案。第一种和第三种场景中都有迁移固定量的数据的步骤。这里暂不说迁移的方案的优劣、选择和实现,后续再记录这些,我今天先记录一下我在初始化数据的时候碰到的问题以及解决的方案。

  • 问题

由于我想通过sql而非程序的方式来实现数据的初始化,因此这里碰到一个问题。

我需要通过hash来决定分到哪个库或者表,然而java的String类型有hashCode方法,那mysql呢?

经过一番查找之后,发现mysql并不支持hash方法,那咋办呢,只能通过存储过程来写一个自定义函数了。那就直接开始操作了!

  • 操作

由上图可知,java中String的hashCode这么实现的,关键点就是这行代码

h = 31 * h + val[i];

简单介绍下,他其实就是对字符串转成Char数组,然后进行循环。每次循环都对hash值 * 31,然后加上char的ASCII值。最后循环完成得到的值就是这个字符串最后的hashCode值。

那我们在mysql的自定义函数里面也按这个逻辑这么写就好了,如下:

DELIMITER $$
CREATE FUNCTION hash_code (user_id VARCHAR(50)) RETURNS INT
BEGIN

DECLARE result INT DEFAULT 0 ;
DECLARE num INT DEFAULT 1 ;
WHILE (num <= LENGTH(user_id)) DO

SET result = result * 31 + ASCII(SUBSTRING(user_id, num, 1)) ;
SET num = num + 1 ;
END
WHILE ;
IF result < 0 THEN

SET result = result * - 1 ;
END
IF ; RETURN result ; END$$

但是经过测试了之后发现,超过了INT的范围了,尴尬。看来只能再优化下了。

然后发现java中超过INT范围之后,他会自己进行补码。举个易懂的例子,看如下代码:

    int num = 0;
    for (int i = 0; ; i++) {
        num++;
        System.out.println(num);
    }

上面的代码无限相加,打印出来的Num会是什么,有兴趣的大家可以试一下。

我这边先直接说结果,如下:

0 --> 1 --> 2 .... --> Integer.MAX_VALUE(2147483647) --> -2147483648 --> -2147483647 --> -2147483646 .... --> 2 --> 1 --> 0

是不是找到规律了?那就直接操作了!一步一步来,先来一个相同数相加的程序

DELIMITER $$
CREATE FUNCTION int_add (num BIGINT) RETURNS BIGINT
BEGIN

DECLARE result BIGINT ;
SET result = num ;
IF result >= 0 THEN

IF 2147483647 - result >= result THEN

SET result = result + result ;
ELSE

SET result = (2147483648 - result) * - 2 ;
END
IF ;
ELSE

IF result >= - 1073741824 THEN

SET result = result + result ;
ELSE

SET result = (- 2147483648 - result) * - 2 ;
END
IF ;
END
IF ; RETURN result ; END$$

DELIMITER ;

写完发现自己有点蠢了,为了数字不超过INT的最大范围做了很多操作和判断,那我为啥不利用下BIGINT呢?超过INT也没事,超过之后再将他的数值更正回来即可。那就直接换个更简单的方法吧!

DELIMITER $$
CREATE FUNCTION int_add_new (num BIGINT, addNum BIGINT) RETURNS BIGINT
BEGIN

DECLARE result BIGINT ;
SET result = num + addNum ;
IF result > 2147483647 THEN

SET result = - 2147483648 + (result - 2147483648) ;
ELSEIF result < - 2147483648 THEN

SET result = 2147483647 - (- 2147483649 - result) ;
END
IF ; RETURN result ; END$$

DELIMITER ;

或者直接乘呢

DELIMITER $$
CREATE FUNCTION int_multiply (num BIGINT, multiplyNum BIGINT) RETURNS BIGINT
BEGIN

DECLARE result BIGINT ;
SET result = (num * addNum) % (2147483648 * 2) ;
IF result > 2147483647 THEN

SET result = - 2147483648 + (result - 2147483648) ;
ELSEIF result < - 2147483648 THEN

SET result = 2147483647 - (- 2147483649 - result) ;
END
IF ; RETURN result ; END$$

DELIMITER ;

嗯,确实看起来容易太多了。这一步ok了之后,接下去通过调用这个方法再去写HashCode方法就简单多啦,如下:

DELIMITER $$
CREATE FUNCTION hash_code (user_id VARCHAR(50), hashNum INT) RETURNS INT
BEGIN

DECLARE result BIGINT DEFAULT 0 ;
DECLARE num INT DEFAULT 1 ;
DECLARE tempNum INT;
DECLARE tempResult BIGINT;
WHILE (num <= LENGTH(user_id)) DO

SET tempNum = 1 ;
SET tempResult = result ;
WHILE (tempNum <= 30) DO

SET result = int_add_new (result, tempResult) ;
SET tempNum = tempNum + 1 ;
END
WHILE ;
SET result = int_add_new (
    result,
    ASCII(SUBSTRING(user_id, num, 1))
) ;
SET num = num + 1 ;
END
WHILE ;
IF result < 0 THEN

SET result = result * - 1 ;
END
IF ; RETURN result % hashNum ; END$$

DELIMITER ;

大功告成,直接试一下!

接下去和java程序对比一下,结果也对上啦,nice!

啊,突然想到如果大家碰到了这个问题的话

这个是因为没允许自定义函数,运行下下面的指令就ok拉!

set global log_bin_trust_function_creators=TRUE;
  • 结论

探索过程中难免会有问题,但是要一步一步来,一个问题一个问题解决。写完看下是不是有更好的方法,是不是还可以进行优化。

过程中不会的东西会有很多,但只有耐得住性子,才能学的更多!

若有问题,欢迎一起讨论,一起学习进步!

转载于:https://my.oschina.net/u/3095034/blog/3089820

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值