mysql_index - taka512/memo GitHub Wiki

MySQL indexのメモ

indexサイズの算出

テーブルに対するindexサイズ

SELECT TABLE_NAME, TABLE_ROWS, ROUND(DATA_LENGTH/1024/1024) Data_MB, ROUND(INDEX_LENGTH/1024/1024) Index_MB
  FROM information_schema.tables WHERE TABLE_NAME = 'テーブル名';

index別のサイズ

SELECT index_name, ROUND((SUM(stat_value) * @@innodb_page_size) / 1024 / 1024) MB
  FROM mysql.innodb_index_stats
WHERE table_name = 'テーブル名' AND stat_name = 'size'
GROUP BY index_name

オプティマイザの実行計画

SET optimizer_trace='enabled=on';
SELECT * FROM information_schema.optimizer_trace\G
SET optimizer_trace='enabled=off';

検証

テーブル定義

CREATE TABLE `sample` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `company_id` int unsigned NOT NULL,
  `warehouse_base_id` int unsigned NOT NULL,
  `type_str` varchar(12) NOT NULL,
  `type_int` int NOT NULL,
  `type_tinyint` tinyint NOT NULL,
  `type_float` float NOT NULL,
  `type_decimal` decimal(6,2) NOT NULL,
  `name` varchar(255) NOT NULL,
  `data` text,
  `created_at` datetime DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

データ投入

// 会社(1000) * 倉庫(10) * 日付(365) * 種別(4) = 14,600,000件
$id = 1;
for ($companyId = 1; $companyId <= 1000; $companyId++) {
    $dbh->beginTransaction();
    for ($wbId = 1; $wbId <= 10; $wbId++) {
        $dt = new \DateTime('2020-01-01');
        for ($day = 1; $day <= 365; $day++) {
            for ($type = 1; $type <= 4; $type++) {
                $stmt = $dbh->prepare($sql);
                $stmt->bindValue(':company_id', $companyId);
                $stmt->bindValue(':warehouse_base_id', $wbId);
                $stmt->bindValue(':type_str', 'type'.$type);
                $stmt->bindValue(':type_int', $type);
                $stmt->bindValue(':type_tinyint', $type);
                $stmt->bindValue(':type_float', $type.'.05');
                $stmt->bindValue(':type_decimal', $type.'.05');
                $stmt->bindValue(':name', str_repeat('HOGE', 50).$id);
                $stmt->bindValue(':data', str_repeat('DATA', 200).$id);
                $stmt->bindValue(':created_at', $dt->format('Y-m-d H:i:s'));
                if (($id % 100) === 0) {
                    $stmt->bindValue(':deleted_at', $dt->format('Y-m-d H:i:s'));
                } else {
                    $stmt->bindValue(':deleted_at', null);
                }
                $stmt->execute();
                $id++;
            }
            $dt->modify('+1 day');
        }
    }
    $dbh->commit();
}

基本的な事

超基本的なことから改めて説明

単体のindexは複数は使われない

複合indexは順序が大事

varcharのindexは前方一致

ユニークキーの長さは制限がある

CREATE UNIQUE INDEX uniqe_name ON sample (name)
CREATE TABLE db_name.tbl_name
  (col_name data_type UNIQUE, ...)

左辺値を関数にするとindexを使用しない

floatよりDecimal

floatだと「SELECT * FROM a WHERE f=0.9」が一致しない

オプティマイザ

オプティマイザを意識した戦略

カーディナリティを意識

カーディナリティの高いキーを左側に持ってくる

レンジを左にすると安定しない

日付みたいなデータを左に持ってくると、

日付集計用indexを個別に貼った方が良いかも

日付を個別に貼ると良い