Mysql 5.5表分区用户和朋友

mta*_*riq 14 mysql partitioning sharding database-partitioning

我的数据库中有两个表现在有数百万行,选择和插入越来越慢.

我正在使用spring + hibernate + mysql 5.5并阅读有关分片以及对表进行分区的信息,以及分区我的表的想法,

我目前的Db结构就像

CREATE TABLE `user` (
  `id` BIGINT(20) NOT NULL,
  `name` VARCHAR(255) DEFAULT NULL,
  `email` VARCHAR(255) DEFAULT NULL,
  `location_id` bigint(20) default NULL,
  `updated_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`),
  KEY `FK3DC99772C476E06B` (`location_id`),
  CONSTRAINT `FK3DC99772C476E06B` FOREIGN KEY (`location_id`) REFERENCES `places` (`id`) 
) ENGINE=INNODB DEFAULT CHARSET=utf8


CREATE TABLE `friends` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `user_id` BIGINT(20) DEFAULT NULL,
  `friend_id` BIGINT(20) DEFAULT NULL,
  `updated_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `unique_friend` (`user_id`,`friend_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
Run Code Online (Sandbox Code Playgroud)

现在我正在测试如何更好地使用分区,对于用户表,我认为根据使用情况会很好.

CREATE TABLE `user_partition` (
  `id` BIGINT(20) NOT NULL,
  `name` VARCHAR(255) DEFAULT NULL,
  `email` VARCHAR(255) DEFAULT NULL,
  `location_id` bigint(20) default NULL,
  `updated_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`),
  KEY `FK3DC99772C476E06B` (`location_id`) 
) ENGINE=INNODB DEFAULT CHARSET=utf8
PARTITION BY HASH(id DIV 100000)
PARTITIONS 30;
Run Code Online (Sandbox Code Playgroud)

我创建了一个程序来加载两个表中的数据并检查两个表的性能

DELIMITER //
CREATE PROCEDURE load_partition_table()
BEGIN
DECLARE v INT DEFAULT 0;
    WHILE v < 1000000
    DO
    INSERT INTO user_partition (id,NAME,email)
    VALUES (v,CONCAT(v,' name'),CONCAT(v,'@yahoo.com')),
    (v+1,CONCAT(v+1,' name'),CONCAT(v+1,'@yahoo.com')),
    (v+2,CONCAT(v+2,' name'),CONCAT(v+2,'@yahoo.com')),
    (v+3,CONCAT(v+3,' name'),CONCAT(v+3,'@yahoo.com')),
    (v+4,CONCAT(v+4,' name'),CONCAT(v+4,'@yahoo.com')),
    (v+5,CONCAT(v+5,' name'),CONCAT(v+5,'@yahoo.com')),
    (v+6,CONCAT(v+6,' name'),CONCAT(v+6,'@yahoo.com')),
    (v+7,CONCAT(v+7,' name'),CONCAT(v+7,'@yahoo.com')),
    (v+8,CONCAT(v+8,' name'),CONCAT(v+8,'@yahoo.com')),
    (v+9,CONCAT(v+9,' name'),CONCAT(v+9,'@yahoo.com'))
    ;
    SET v = v + 10;
    END WHILE;
    END
    //

CREATE PROCEDURE load_table()
BEGIN
DECLARE v INT DEFAULT 0;
    WHILE v < 1000000
    DO
    INSERT INTO user (id,NAME,email)
    VALUES (v,CONCAT(v,' name'),CONCAT(v,'@yahoo.com')),
    (v+1,CONCAT(v+1,' name'),CONCAT(v+1,'@yahoo.com')),
    (v+2,CONCAT(v+2,' name'),CONCAT(v+2,'@yahoo.com')),
    (v+3,CONCAT(v+3,' name'),CONCAT(v+3,'@yahoo.com')),
    (v+4,CONCAT(v+4,' name'),CONCAT(v+4,'@yahoo.com')),
    (v+5,CONCAT(v+5,' name'),CONCAT(v+5,'@yahoo.com')),
    (v+6,CONCAT(v+6,' name'),CONCAT(v+6,'@yahoo.com')),
    (v+7,CONCAT(v+7,' name'),CONCAT(v+7,'@yahoo.com')),
    (v+8,CONCAT(v+8,' name'),CONCAT(v+8,'@yahoo.com')),
    (v+9,CONCAT(v+9,' name'),CONCAT(v+9,'@yahoo.com'))
    ;
    SET v = v + 10;
    END WHILE;
    END
    //
Run Code Online (Sandbox Code Playgroud)

结果令人惊讶,在非分区表中插入/选择给出更好的结果.

mysql> select count(*) from user_partition;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.40 sec)

mysql> select count(*) from user;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.00 sec)


mysql> call load_table();
Query OK, 10 rows affected (20.31 sec)

mysql> call load_partition_table();
Query OK, 10 rows affected (21.22 sec)

mysql> select * from user where id = 999999;
+--------+-------------+------------------+---------------------+
| id     | name        | email            | updated_time        |
+--------+-------------+------------------+---------------------+
| 999999 | 999999 name | 999999@yahoo.com | 2012-11-27 08:06:54 |
+--------+-------------+------------------+---------------------+
1 row in set (0.00 sec)

mysql> select * from user_no_part where id = 999999;
+--------+-------------+------------------+---------------------+
| id     | name        | email            | updated_time        |
+--------+-------------+------------------+---------------------+
| 999999 | 999999 name | 999999@yahoo.com | 2012-11-27 08:03:14 |
+--------+-------------+------------------+---------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

所以有两个问题

1)什么是分区user表的最佳方式,以便插入和选择也变得快速并且删除FOREIGN KEY location_id是正确的?我知道分区只有在我们访问分区键的基础上才能好,在我的情况下我只想通过id读取表.为什么插入在分区表中较慢?

2)分区friend表的最佳方法是什么,因为我想基于user_id将所有用户朋友放在同一分区中并且总是使用user_id访问它来对朋友进行分区.我应该将主键放在friend.id上还是在主键中添加user_id?

TJC*_*ers 4

首先,如果可能的话,我建议您升级到 Mysql 5.6.5 或更高版本,以确保您能够正确利用分区并获得最佳性能。由于 GA 问题,这并不总是可行,但我的经验是 5.5 和 5.6 之间的性能存在差异,并且 5.6 提供了一些其他类型的分区。

1)我的经验是,只要您在查询中包含要分区的列,分区集上的插入和更新以及选择都会更快。如果我要求对所有分区中的所有记录进行计数,我会看到响应速度较慢。这是可以预料到的,因为分区的功能就像单独的表一样,因此如果您有 30 个分区,就相当于读取 30 个表,而不仅仅是一个表。

您必须在主键中包含要分区的值,并且它必须在记录的生命周期内保持稳定。

2)我将在主键中包含 user_id 和 id - 假设您的朋友表 user_id 和 id 一旦建立记录就根本不会更改(即任何更改都将是删除/插入)。就我而言,它是“多余的”,但值得访问。选择 user_id/id 还是 id/user_id 取决于您最常访问的情况。

最后一点。当我第一次开始将数据分解为分区时,我尝试创建很多分区,结果发现只有少数几个分区似乎达到了最佳效果 - 6-12 个分区似乎最适合我。YMMV。