2012-11-27 14 views
14

私のデータベースに2つのテーブルがあり、現在は何百万もの行があります。選択と挿入が遅くなり、遅くなります。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 

のようなものです、

を春+ + MySQLの5.5を休止し、シャーディングと同様にテーブルを分割し、私のテーブルを分割するというアイデアのように読んを使用しています今私は使用法に基づいて良いと思っているユーザーテーブルのために、より良いパーティション分割を使用する方法をテストしています。

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; 

私は2つのテーブル内のデータをロードすると結果がsurprizingれた2つのテーブル

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 
    // 

のパフォーマンスをチェックするための手順を作成し、より良い結果を与える非パーティションテーブルで選択/挿入します。

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 | [email protected] | 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 | [email protected] | 2012-11-27 08:03:14 | 
+--------+-------------+------------------+---------------------+ 
1 row in set (0.00 sec) 

ように、2つの質問

1)を挿入するようにuserテーブルを分割するための最良の方法いただきましたし、また、高速になるとlocation_idにFOREIGN KEYを削除することは正しい選択しますか?私はパーティションキーの基盤にアクセスする場合のみ、パーティションが良いことがわかります私の場合は、私はidだけでテーブルを読んでみたいです。なぜパーティションテーブルの挿入が遅いのですか?

2)テーブルをパーティション化するには、user_idのベースですべてのユーザーフレンドを同じパーティションに配置し、常にuser_idを使用してアクセスする必要があります。プライマリキーをfriend.idにドロップするか、プライマリキーにuser_idを追加する必要がありますか?

+0

ポストには、パフォーマンスに大きな違いはありません。 100万レコードを挿入する場合の1秒の差はかなり小さい(約0.5%)。 –

+0

AFAIKはSELECTの速度を上げるために、[indexing](http://dev.mysql.com/doc/refman/5.5/en/mysql-indexes.html)が適切な解決策です。また、[INSERTスピードアップに関するアドバイス](http://dev.mysql.com/doc/refman/5.5/en/insert-speed.html)もあります。メインラインでは、一括挿入が行われています。 –

答えて

4

まず、5.6.5以降のMysqlにアップグレードすることをお勧めします。これにより、適切にパーティション化され、パフォーマンスが最適化されます。これはGAの懸念から常に可能なわけではありませんが、私の経験では、5.5と5.6の間にパフォーマンスに違いがあり、5.6ではいくつかの他のタイプのパーティションが提供されています。

1)私の経験では、パーティション化されたセットでの挿入と更新が高速であり、クエリ内で分割されている列を含んでいるため、すべてのパーティションのすべてのレコードの数を求めると、応答が遅くなります。それはパーティションがLIKEの別個のテーブルとして機能しているために期待されます。したがって、30個のパーティションがある場合は、1つではなく30個のテーブルを読み込むようなものです。

主キーにパーティション化する値を含める必要があります。また、レコードの存続期間中は安定していなければなりません。

2)プライマリキーにuser_idとidを含めます。あなたの友人テーブルuser_idとidは、一度レコードが確立されると変更されません(つまり、変更は削除/挿入になります)。私の場合、それは「冗長」だったが、アクセスに値するもの以上のものだった。 user_id/idまたはid/user_idを選択するかどうかは、最も頻繁にアクセスするかどうかによって異なります。

最後のメモ。私は最初にデータをパーティションに分割し始めたときにたくさんのパーティションを作成しようとしましたが、ほんのわずかのものがスイートスポットに当たったようです - 6-12パーティションが私のために最適なようでした。 YMMV。

+0

ありがとう しかし、なぜ私のテストケースでより多くの時間をパーティションテーブルに挿入すると何か間違っています。 私は通常、いくつかのuser_idに基づいて友達を選択し、idフィールドを使用しませんでした – mtariq

+0

あなたの挿入物がどのように見えるのでしょうか?私はあなたが最新のMySQLバージョンでこれらを試したことを前提としていますか? – TJChambers

+0

mysqlバージョン5.5私は – mtariq

1

1。テーブルを選択するには、このSQLクエリを使用して、IDを除くすべての列を、除く:

私は何が必要答える:

私は、これは狂気である知っているあなたはFOREIGN KEYPRIMARY KEY

を削除することをお勧めしますが、それら現在のID、最後のID、次のID、そしてこのwlllが手動でIDを作成するよりも時間がかかることをコンピュータに尋ねることができます。 他の方法では、int idをjavaで手動で作成できます。

使用このSQLクエリは、しっかりと挿入します。私は速いか働くことができる私のクエリを決めることはできません

INSERT INTO user (id,NAME,email) 
VALUES ('CREATE ID WITH JAVA', 'NAME', '[email protected]') 

...

をすべてはあなたのコンピュータのパフォーマンスに依存しているため、使用することを確認してくださいそれはサーバーがすべてのタスクをすばやく完了できるためです。

およびselectの場合は、profile infoにあるprofile infoには、プロファイルIDで定義された1人のユーザーに対して1つの行が必要です。

使用mysqlの限度一つだけを必要としますが、複数のが必要な場合... ちょうど1行分のこの ような限界値を変更する場合:

select * from user where id = 999999 limit 1; 

と7行について:

私はこのクエリが速く limit ない場合よりも仕事や制限を覚えているだろうと思い
select * from user where id = 999999 limit 7; 

insertすぎ

で動作することができます友人のパーティションについては

2: 答えはノー主キーと主キー

表がもう一度何の問題

ではありませんドロップし、JavaのでIDを作成...なるように設計 javaのですインターフェイスが速く、コードにはwhile が含まれており、javaでも実行できます。 あなたはすべての友人のデータを取得する必要があります例えば... 使用このクエリは速く実行する:

select fr.friend_id, usr.* from friends as fr INNER JOIN user as usr 
ON dr.friend_id = usr.id 
where fr.user_id = 999999 LIMIT 10; 

と私は、これは 申し訳ありませんが私は唯一のMySQLについて説明することができ、十分ではなくJavaであると思います。 私はjavaの専門家ではないので、私はそれについて理解しています。

+0

挑戦的に助けますが、完全には答えなかったので、1と2の答えを更新できますか? – mtariq

+0

最初に使ったプログラミング言語は何ですか? –

+0

javaしかし、あなたはmysqlの質問どおりに任意の言語を使用することができます – mtariq

0

1)データを選択するために常にid(または主に)のみを使用する場合は、このフィールドをパーティション条件のベースとして使用することは明らかです。数値であるので、ハッシュ関数の必要はありません。単純にrange partitioningを使用してください。作成するパーティション数(境界として選択する数値)は、自分で探す必要がありますが、前述の@TJChambersと同様に、効率的でなければなりません。

誤ってテストするため、挿入が遅くなります。 ランダム性を持たずに1000000行を1つずつ挿入するだけです。唯一の違いは、分割されたテーブルの場合、余分な時間であるhashを計算する必要があることです。 しかし、あなたのケースでは、idはパーティション化の条件の基礎です。新しい行がすべてテーブルの最後に来るので、挿入することで決して何も得られません。

たとえば、GPSのローカライゼーションを使ってテーブルを作成し、latとlonでパーティション化した場合たとえば、各パーティションが異なる大陸であった場合など、挿入の違いを見ることができます。 いくつかのランダム(実際の)データを持つテーブルがあり、線形ではないランダムな値を挿入していると、違いが見られます。

パーティション化されたテーブルの選択は、もう一度間違っているため、処理が遅くなります。

@TJChambersが私の前に書きました。あなたのクエリはすべてのパーティション(多くのテーブルを扱うようなものです)で動作する必要があります。違いを確認するために、1つのパーティションからのデータを扱う場所を使用してください。例えば、実行の

select count(*) from user_partition where id<99999; 

select count(*) from user where id<99999; 

あなたは違いが表示されます。

2)これは難しいです。データの冗長性なしにパーティションを分割する方法はありません(少なくとも私の考えは考えられません)が、アクセス時間(選択速度)が最も重要な場合は、ユーザーテーブルと同じ方法でパーティションを分割することができますidの1つ)を作成し、各関係(a、b)と(b、a)の2行を挿入します。行数が倍になりますが、4つ以上のパーツに分割すると、問合せごとのレコード数が少なくて済みます。また、必要がないかどうかをチェックする条件は1つだけになります。

私は、私が実行した

CREATE TABLE `test`.`friends` (
`a` INT NOT NULL , 
`b` INT NOT NULL , 
INDEX (`a`), 
INDEX (`b`) 
) ENGINE = InnoDB; 

CREATE TABLE `test`.`friends_part` (
`a` INT NOT NULL , 
`b` INT NOT NULL , 
INDEX (`a` , `b`) 
) ENGINE = InnoDB 
PARTITION BY RANGE (a) (
    PARTITION p0 VALUES LESS THAN (1000), 
    PARTITION p1 VALUES LESS THAN (2000), 
    PARTITION p2 VALUES LESS THAN (3000), 
    PARTITION p3 VALUES LESS THAN (4000), 
    PARTITION p4 VALUES LESS THAN (5000), 
    PARTITION p5 VALUES LESS THAN (6000), 
    PARTITION p6 VALUES LESS THAN (7000), 
    PARTITION p7 VALUES LESS THAN (8000), 
    PARTITION p8 VALUES LESS THAN (9000), 
    PARTITION p9 VALUES LESS THAN MAXVALUE 
); 

delimiter // 
DROP procedure IF EXISTS fill_friends// 
create procedure fill_friends() 
begin 
    declare i int default 0; 
    declare a int; 
    declare b int; 
    while i<2000000 
    do 
    set a = rand()*10000; 
    set b = rand()*10000; 
    insert into friends values(a,b); 
    set i = i + 1; 
    end while; 
end 
// 
delimiter ; 

delimiter // 
DROP procedure IF EXISTS fill_friends_part// 
create procedure fill_friends_part() 
begin 
    insert into friends_part (select a,b from friends); 
    insert into friends_part (select b as a, a as b from friends); 
end 
// 
delimiter ; 

クエリがあり、このスキーマとでそれをテストした:

select * from friends where a=317 or b=317; 

結果セット:475 回:1.43、0.02、0.01

select * from friends_part where a=317; 

結果セット:475 回:0.10,0.00,0.00

select * from friends where a=4887 or b=4887; 

結果セット:483 回:1.33、0.01、0.01

select * from friends_part where a=4887; 

結果セット:483 回:0.06、0.01、0.00

私はデータの一意性については気にしませんでしたあなたの例では一意のインデックスを使用することができます。 また、InnoDBエンジンを使用しましたが、ほとんどのクエリが選択されていて、多くの書き込みを実行しない場合はMyISAMが優れています。 おそらくキャッシングのため2回目と3回目の実行に大きな違いはありませんが、1回目の実行では目に見える違いがあります。データベース設計の主要なルールの1つを破っているので、それはより高速ですが、最終的には手段が正当化されるため、実際に大きなテーブルには良い解決策になるかもしれません。もしあなたが1 M未満のレコードを持っているなら、私はあなたが分割せずに生き残ることができると思います。

関連する問題