现象

    在mysql 5.1下新建key分表,可以正确查询数据。

    而直接用mysql5.5或mysql5.6启动上面的5.1实例,发现(1,1785089517)这行数据不能正确查询出来。

    1. mysql> select * from t1 where c2 is null;
    2. +------+------+
    3. | c1 | c2 |
    4. +------+------+
    5. | 2 | NULL |
    6. +------+------+
    7. 1 row in set (0.00 sec)
    8. mysql> select * from t1 where c2=1785089517;
    9. Empty set (0.00 sec)

    原因分析

    跟踪代码发现,5.1 与5.5,5.6 key hash算法是有区别的。

    1. void my_hash_sort_bin(const CHARSET_INFO *cs __attribute__((unused)),
    2. const uchar *key, size_t len,ulong *nr1, ulong *nr2)
    3. {
    4. const uchar *pos = key;
    5. for (; pos < (uchar*) key ; pos++)
    6. {
    7. nr1[0]^=(ulong) ((((uint) nr1[0] & 63)+nr2[0]) *
    8. ((uint)*pos)) + (nr1[0] << 8);
    9. nr2[0]+=3;
    10. }
    11. }

    通过此算法算出数据(1,1785089517)在第3个分区

    5.5和5.6非空值的处理算法如下

    通过此算法算出数据(1,1785089517)在第5个分区,因此,5.5,5.6查询不能查询出此行数据。

    5.1,5.5,5.6对于空值的算法还是一致的,如下

    1. if (field->is_null())
    2. {
    3. nr1^= (nr1 << 1) | 1;
    4. continue;
    5. }

    都能正确算出数据(2, null)在第3个分区。因此,空值可以正确查询出来。

    那么问题又来了,5.5后为什么算法会变化呢?原因在于官方关于字符集策略的调整,详见 。

    兼容处理

    前面讲到,由于hash 算法变化,用5.5,5.6启动5.1的实例,导致不能正确查询数据。那么5.1升级5.5,5.6就必须兼容这个问题.mysql 5.5.31以后,提供了专门的语法 ALTER TABLE … PARTITION BY ALGORITHM=1 [LINEAR] KEY … 用于兼容此问题。对于上面的例子,用5.5或5.6启动5.1的实例后执行

    1. mysql> alter table t1 PARTITION BY KEY ALGORITHM = 1 (c2) partitions 5;
    2. Records: 2 Duplicates: 0 Warnings: 0

    数据可以正确查询出来了。

    而实际上5.5,5.6的mysql_upgrade升级程序已经提供了兼容方法。mysql_upgrade 执行check table xxx for upgrade 会检查key分区表是否用了老的算法。如果使用了老的算法,会返回

    1. mysql> CHECK TABLE t1 FOR UPGRADE\G
    2. *************************** 1\. row ***************************
    3. Table: test.t1
    4. Op: check
    5. Msg_type: error
    6. Msg_text: KEY () partitioning changed, please run:
    7. ALTER TABLE `test`.`t1` PARTITION BY KEY /*!50611 ALGORITHM = 1 */ (c2)
    8. PARTITIONS 5
    9. *************************** 2\. row ***************************
    10. Table: test.t1
    11. Op: check
    12. Msg_type: status
    13. Msg_text: Operation failed
    1. ALTER TABLE `test`.`t1` PARTITION BY KEY /*!50611 ALGORITHM = 1 */ (c2) PARTITIONS 5