MySQLで大量のデータを追加してみる

CentOSにMySQLとMemcacheのインストールしてみる
MySQLをインストールした環境を使って、大量のデータをinsertしてみる。

【参考】
なんとなくやりたかった。MySQLのクエリのみで簡単に1億ものデータを作成する方法 | 株式会社LIG
http://liginc.co.jp/programmer/archives/2707

SQLで大量データを一気に作るでござる(MySQL編) @ t100のプログラミング脱出作戦
http://t100life.blog121.fc2.com/blog-entry-205.html

今回はデフォルトで入ってるtestデータベースで試す。

mysql> use test

手順

下記のようなテーブルを作成する。

mysql> create table sample (
  id      int         NOT NULL AUTO_INCREMENT,
  name    varchar(64) NOT NULL,
  point   int         NOT NULL,
  type    smallint    NOT NULL,
  flag    tinyint(1)  NOT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB;

ベースとなるデータの10件をINSERTする。

mysql> insert into sample (name, point, type, flag) values
('sato',       100, 1, 0),
('suzuki',     200, 2, 1),
('takahashi',  300, 3, 0),
('tanaka',     400, 4, 1),
('ito',        500, 5, 0),
('yamamoto',   600, 1, 1),
('watanabe',   700, 2, 0),
('nakamura',   800, 3, 1),
('kobayashi',  900, 4, 0),
('kato',      1000, 5, 1);

10件入ったテーブルを単純結合してsampleテーブルに入れる。
1000万件INSERTする場合は、下記のように7つ結合すればいい。

mysql> insert into sample (name, point, type, flag)
 select s1.name, s1.point, s1.type, s1.flag
 from sample s1, sample s2, sample s3, sample s4, sample s5, sample s6, sample s7;
自分のMacBookProの環境では約1分半かかった。
Query OK, 10000000 rows affected (1 min 26.64 sec)
Records: 10000000  Duplicates: 0  Warnings: 0

以上で、1000万件のデータが追加されます。

mysql> select count(*) from sample;
+----------+
| count(*) |
+----------+
| 10000010 |
+----------+

INSERTされるデータは下記のようになる。

mysql> select * from sample limit 30;
+----+-----------+-------+------+------+
| id | name      | point | type | flag |
+----+-----------+-------+------+------+
|  1 | sato      |   100 |    1 |    0 |
|  2 | suzuki    |   200 |    2 |    1 |
|  3 | takahashi |   300 |    3 |    0 |
|  4 | tanaka    |   400 |    4 |    1 |
|  5 | ito       |   500 |    5 |    0 |
|  6 | yamamoto  |   600 |    1 |    1 |
|  7 | watanabe  |   700 |    2 |    0 |
|  8 | nakamura  |   800 |    3 |    1 |
|  9 | kobayashi |   900 |    4 |    0 |
| 10 | kato      |  1000 |    5 |    1 |
| 11 | sato      |   100 |    1 |    0 |
| 12 | suzuki    |   200 |    2 |    1 |
| 13 | takahashi |   300 |    3 |    0 |
| 14 | tanaka    |   400 |    4 |    1 |
| 15 | ito       |   500 |    5 |    0 |
| 16 | yamamoto  |   600 |    1 |    1 |
| 17 | watanabe  |   700 |    2 |    0 |
| 18 | nakamura  |   800 |    3 |    1 |
| 19 | kobayashi |   900 |    4 |    0 |
| 20 | kato      |  1000 |    5 |    1 |
| 21 | sato      |   100 |    1 |    0 |
| 22 | suzuki    |   200 |    2 |    1 |
| 23 | takahashi |   300 |    3 |    0 |
| 24 | tanaka    |   400 |    4 |    1 |
| 25 | ito       |   500 |    5 |    0 |
| 26 | yamamoto  |   600 |    1 |    1 |
| 27 | watanabe  |   700 |    2 |    0 |
| 28 | nakamura  |   800 |    3 |    1 |
| 29 | kobayashi |   900 |    4 |    0 |
| 30 | kato      |  1000 |    5 |    1 |
+----+-----------+-------+------+------+

単純結合とは

10件データの入った状態で、

mysql> select * from sample;
+----+-----------+-------+------+------+
| id | name      | point | type | flag |
+----+-----------+-------+------+------+
|  1 | sato      |   100 |    1 |    0 |
|  2 | suzuki    |   200 |    2 |    1 |
|  3 | takahashi |   300 |    3 |    0 |
|  4 | tanaka    |   400 |    4 |    1 |
|  5 | ito       |   500 |    5 |    0 |
|  6 | yamamoto  |   600 |    1 |    1 |
|  7 | watanabe  |   700 |    2 |    0 |
|  8 | nakamura  |   800 |    3 |    1 |
|  9 | kobayashi |   900 |    4 |    0 |
| 10 | kato      |  1000 |    5 |    1 |
+----+-----------+-------+------+------+
10 rows in set (0.00 sec)

fromにsampleを2つ並べると、100件になる。

mysql> select * from sample s1, sample s2;
+----+-----------+-------+------+------+----+-----------+-------+------+------+
| id | name      | point | type | flag | id | name      | point | type | flag |
+----+-----------+-------+------+------+----+-----------+-------+------+------+
|  1 | sato      |   100 |    1 |    0 |  1 | sato      |   100 |    1 |    0 |
|  2 | suzuki    |   200 |    2 |    1 |  1 | sato      |   100 |    1 |    0 |
|  3 | takahashi |   300 |    3 |    0 |  1 | sato      |   100 |    1 |    0 |
|  4 | tanaka    |   400 |    4 |    1 |  1 | sato      |   100 |    1 |    0 |
|  5 | ito       |   500 |    5 |    0 |  1 | sato      |   100 |    1 |    0 |
|  6 | yamamoto  |   600 |    1 |    1 |  1 | sato      |   100 |    1 |    0 |
|  7 | watanabe  |   700 |    2 |    0 |  1 | sato      |   100 |    1 |    0 |
|  8 | nakamura  |   800 |    3 |    1 |  1 | sato      |   100 |    1 |    0 |
|  9 | kobayashi |   900 |    4 |    0 |  1 | sato      |   100 |    1 |    0 |
| 10 | kato      |  1000 |    5 |    1 |  1 | sato      |   100 |    1 |    0 |
|  1 | sato      |   100 |    1 |    0 |  2 | suzuki    |   200 |    2 |    1 |
|  2 | suzuki    |   200 |    2 |    1 |  2 | suzuki    |   200 |    2 |    1 |
|  3 | takahashi |   300 |    3 |    0 |  2 | suzuki    |   200 |    2 |    1 |
|  4 | tanaka    |   400 |    4 |    1 |  2 | suzuki    |   200 |    2 |    1 |
|  5 | ito       |   500 |    5 |    0 |  2 | suzuki    |   200 |    2 |    1 |
|  6 | yamamoto  |   600 |    1 |    1 |  2 | suzuki    |   200 |    2 |    1 |
|  7 | watanabe  |   700 |    2 |    0 |  2 | suzuki    |   200 |    2 |    1 |
|  8 | nakamura  |   800 |    3 |    1 |  2 | suzuki    |   200 |    2 |    1 |
|  9 | kobayashi |   900 |    4 |    0 |  2 | suzuki    |   200 |    2 |    1 |
| 10 | kato      |  1000 |    5 |    1 |  2 | suzuki    |   200 |    2 |    1 |
|  1 | sato      |   100 |    1 |    0 |  3 | takahashi |   300 |    3 |    0 |
・・・
| 10 | kato      |  1000 |    5 |    1 |  9 | kobayashi |   900 |    4 |    0 |
|  1 | sato      |   100 |    1 |    0 | 10 | kato      |  1000 |    5 |    1 |
|  2 | suzuki    |   200 |    2 |    1 | 10 | kato      |  1000 |    5 |    1 |
|  3 | takahashi |   300 |    3 |    0 | 10 | kato      |  1000 |    5 |    1 |
|  4 | tanaka    |   400 |    4 |    1 | 10 | kato      |  1000 |    5 |    1 |
|  5 | ito       |   500 |    5 |    0 | 10 | kato      |  1000 |    5 |    1 |
|  6 | yamamoto  |   600 |    1 |    1 | 10 | kato      |  1000 |    5 |    1 |
|  7 | watanabe  |   700 |    2 |    0 | 10 | kato      |  1000 |    5 |    1 |
|  8 | nakamura  |   800 |    3 |    1 | 10 | kato      |  1000 |    5 |    1 |
|  9 | kobayashi |   900 |    4 |    0 | 10 | kato      |  1000 |    5 |    1 |
| 10 | kato      |  1000 |    5 |    1 | 10 | kato      |  1000 |    5 |    1 |
+----+-----------+-------+------+------+----+-----------+-------+------+------+
100 rows in set (0.00 sec)

これが単純結合らしい。

乱数を設定する場合

FLOOR関数とRAND関数を使って、データを乱数で設定してみる。

n〜mの乱数は下記のように書くと生成できる。

FLOOR(RAND() * (m - n + 1) + n)

例えば、
・0〜1の場合は「FLOOR(RAND()*2)」
・1〜5の場合は「FLOOR(RAND()*5 + 1)」
・100〜1000の場合は「FLOOR(RAND()*901 + 100)」
となる。

下記のようにINSERTすると、

mysql> insert into sample (name, point, type, flag)
 select s1.name, FLOOR(RAND()*901 + 100), FLOOR(RAND()*5 + 1), FLOOR(RAND() * 2)
 from sample s1, sample s2, sample s3, sample s4, sample s5, sample s6, sample s7;
乱数を使ってもINSERTにかかる時間は乱数を使わない場合とそれほど変わりませんでした。
Query OK, 10000000 rows affected (1 min 29.92 sec)
Records: 10000000  Duplicates: 0  Warnings: 0

下記のような感じで(最初の10件以外は)乱数が設定される。

mysql> select * from sample limit 30;
+----+-----------+-------+------+------+
| id | name      | point | type | flag |
+----+-----------+-------+------+------+
|  1 | sato      |   100 |    1 |    0 |
|  2 | suzuki    |   200 |    2 |    1 |
|  3 | takahashi |   300 |    3 |    0 |
|  4 | tanaka    |   400 |    4 |    1 |
|  5 | ito       |   500 |    5 |    0 |
|  6 | yamamoto  |   600 |    1 |    1 |
|  7 | watanabe  |   700 |    2 |    0 |
|  8 | nakamura  |   800 |    3 |    1 |
|  9 | kobayashi |   900 |    4 |    0 |
| 10 | kato      |  1000 |    5 |    1 |
| 11 | sato      |   861 |    2 |    0 |
| 12 | suzuki    |   979 |    2 |    0 |
| 13 | takahashi |   302 |    5 |    1 |
| 14 | tanaka    |   700 |    4 |    0 |
| 15 | ito       |   325 |    5 |    1 |
| 16 | yamamoto  |   136 |    4 |    0 |
| 17 | watanabe  |   512 |    1 |    1 |
| 18 | nakamura  |   621 |    1 |    0 |
| 19 | kobayashi |   938 |    3 |    0 |
| 20 | kato      |   807 |    4 |    1 |
| 21 | sato      |   719 |    4 |    0 |
| 22 | suzuki    |   798 |    3 |    1 |
| 23 | takahashi |   546 |    3 |    0 |
| 24 | tanaka    |   773 |    5 |    0 |
| 25 | ito       |   481 |    3 |    0 |
| 26 | yamamoto  |   685 |    2 |    0 |
| 27 | watanabe  |   626 |    1 |    1 |
| 28 | nakamura  |   276 |    2 |    1 |
| 29 | kobayashi |   880 |    5 |    0 |
| 30 | kato      |   988 |    3 |    0 |
+----+-----------+-------+------+------+

連番を設定する場合

各テーブルのidを使えば連番を設定できなくもない。

例えば、

s7.id * 1000000 + s6.id * 100000 + s5.id * 10000 + s4.id * 1000 + s3.id * 100 + s2.id * 10 + s1.id,

のように指定すると、下記のような連番が生成できる。
1111111
1111112
1111113
1111114
1111115
1111116
1111117
1111118
1111119
1111120
1111121
・・・

そのため、下記のようにINSERTすると、

mysql> insert into sample (name, point, type, flag)
 select
  s1.name,
  s7.id * 1000000 + s6.id * 100000 + s5.id * 10000 + s4.id * 1000 + s3.id * 100 + s2.id * 10 + s1.id - 1111110,
  s1.type, s1.flag
 from sample s1, sample s2, sample s3, sample s4, sample s5, sample s6, sample s7;

pointのところが連番で設定される。

mysql> select * from sample limit 30;
+----+-----------+-------+------+------+
| id | name      | point | type | flag |
+----+-----------+-------+------+------+
|  1 | sato      |   100 |    1 |    0 |
|  2 | suzuki    |   200 |    2 |    1 |
|  3 | takahashi |   300 |    3 |    0 |
|  4 | tanaka    |   400 |    4 |    1 |
|  5 | ito       |   500 |    5 |    0 |
|  6 | yamamoto  |   600 |    1 |    1 |
|  7 | watanabe  |   700 |    2 |    0 |
|  8 | nakamura  |   800 |    3 |    1 |
|  9 | kobayashi |   900 |    4 |    0 |
| 10 | kato      |  1000 |    5 |    1 |
| 11 | sato      |     1 |    1 |    0 |
| 12 | suzuki    |     2 |    2 |    1 |
| 13 | takahashi |     3 |    3 |    0 |
| 14 | tanaka    |     4 |    4 |    1 |
| 15 | ito       |     5 |    5 |    0 |
| 16 | yamamoto  |     6 |    1 |    1 |
| 17 | watanabe  |     7 |    2 |    0 |
| 18 | nakamura  |     8 |    3 |    1 |
| 19 | kobayashi |     9 |    4 |    0 |
| 20 | kato      |    10 |    5 |    1 |
| 21 | sato      |    11 |    1 |    0 |
| 22 | suzuki    |    12 |    2 |    1 |
| 23 | takahashi |    13 |    3 |    0 |
| 24 | tanaka    |    14 |    4 |    1 |
| 25 | ito       |    15 |    5 |    0 |
| 26 | yamamoto  |    16 |    1 |    1 |
| 27 | watanabe  |    17 |    2 |    0 |
| 28 | nakamura  |    18 |    3 |    1 |
| 29 | kobayashi |    19 |    4 |    0 |
| 30 | kato      |    20 |    5 |    1 |
+----+-----------+-------+------+------+

ただ、かなり無理やり感があるので、もっとスマートなやり方があれば教えてほしい。

最初の10件が邪魔な場合は

idのみのテンポラリなテーブルを作って、

mysql> create table tmp (id int NOT NULL AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB;

10件レコードを追加する。

mysql> insert into tmp values (), (), (), (), (), (), (), (), (), ();
mysql> select * from tmp;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
|  6 |
|  7 |
|  8 |
|  9 |
| 10 |
+----+

tmpのテーブルからINSERTすれば、

mysql> insert into sample (name, point, type, flag)
 select
  ELT(t1.id, 'sato', 'suzuki', 'takahashi', 'tanaka', 'ito', 'yamamoto', 'watanabe', 'nakamura', 'kobayashi', 'kato'),
  t7.id * 1000000 + t6.id * 100000 + t5.id * 10000 + t4.id * 1000 + t3.id * 100 + t2.id * 10 + t1.id - 1111110,
  FLOOR(RAND()*5 + 1),
  FLOOR(RAND() * 2)
 from tmp t1, tmp t2, tmp t3, tmp t4, tmp t5, tmp t6, tmp t7;
ELT(N,str1,str2,str3,...)
N = 1 の場合は str1 を戻し、N = 2 の場合は str2 を戻す、というふうに続きます。

MySQL :: MySQL 5.1 リファレンスマニュアル :: 11.3 文字列関数
http://dev.mysql.com/doc/refman/5.1/ja/string-functions.html

余計な10件がなく、すっきりする。

mysql> select count(*) from sample;
+----------+
| count(*) |
+----------+
| 10000000 |
+----------+
mysql> select * from sample limit 30;
+----+-----------+-------+------+------+
| id | name      | point | type | flag |
+----+-----------+-------+------+------+
|  1 | sato      |     1 |    3 |    0 |
|  2 | suzuki    |     2 |    3 |    1 |
|  3 | takahashi |     3 |    5 |    0 |
|  4 | tanaka    |     4 |    5 |    0 |
|  5 | ito       |     5 |    3 |    1 |
|  6 | yamamoto  |     6 |    1 |    1 |
|  7 | watanabe  |     7 |    1 |    0 |
|  8 | nakamura  |     8 |    5 |    1 |
|  9 | kobayashi |     9 |    3 |    1 |
| 10 | kato      |    10 |    1 |    1 |
| 11 | sato      |    11 |    3 |    1 |
| 12 | suzuki    |    12 |    4 |    0 |
| 13 | takahashi |    13 |    2 |    0 |
| 14 | tanaka    |    14 |    5 |    0 |
| 15 | ito       |    15 |    3 |    0 |
| 16 | yamamoto  |    16 |    3 |    1 |
| 17 | watanabe  |    17 |    3 |    0 |
| 18 | nakamura  |    18 |    5 |    0 |
| 19 | kobayashi |    19 |    3 |    0 |
| 20 | kato      |    20 |    3 |    1 |
| 21 | sato      |    21 |    3 |    1 |
| 22 | suzuki    |    22 |    3 |    1 |
| 23 | takahashi |    23 |    5 |    1 |
| 24 | tanaka    |    24 |    2 |    0 |
| 25 | ito       |    25 |    4 |    1 |
| 26 | yamamoto  |    26 |    3 |    1 |
| 27 | watanabe  |    27 |    3 |    0 |
| 28 | nakamura  |    28 |    4 |    0 |
| 29 | kobayashi |    29 |    3 |    0 |
| 30 | kato      |    30 |    1 |    0 |
+----+-----------+-------+------+------+

エラーが出た場合

CentOSにMySQLとMemcacheのインストールしてみる
の環境で実行すると、INSERT時におそらく

ERROR 1206 (HY000): The total number of locks exceeds the lock table size

のエラーが出るはず。

原因は、innodb_buffer_poolの値が小さいためで、
my.cnfにinnodb_buffer_poolを指定するといいみたいです。

$ sudo vi /etc/my.cnf
[mysqld]
innodb_buffer_pool=512M	←追加

あと、設定を反映させるために再起動も必要。

$ sudo service mysqld restart

設定した結果、下記のようになってればOK。

mysql> show variables like 'innodb_buffer_pool_size';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 536870912 |
+-------------------------+-----------+
【参考】
MySQL エラー「ERROR 1206 (HY000): The total number of locks exceeds the lock table size」: 仕事を早く片付けて遊ぶための技術メモ
http://shrine-bell.seesaa.net/article/24406528.html