データの挿入
このドキュメントでは、さまざまなプログラミング言語で SQL 言語を使用して TiDB にデータを挿入する方法について説明します。
始める前に
このドキュメントを読む前に、次の準備が必要です。
行を挿入する
複数行のデータを挿入するには、2 つの方法があります。たとえば、 3人のプレーヤーのデータを挿入する必要がある場合。
複数行の挿入ステートメント:
INSERT INTO `player` (`id`, `coins`, `goods`) VALUES (1, 1000, 1), (2, 230, 2), (3, 300, 5);複数の単一行挿入ステートメント:
INSERT INTO `player` (`id`, `coins`, `goods`) VALUES (1, 1000, 1); INSERT INTO `player` (`id`, `coins`, `goods`) VALUES (2, 230, 2); INSERT INTO `player` (`id`, `coins`, `goods`) VALUES (3, 300, 5);
一般に、 multi-line insertion statement
は複数のsingle-line insertion statements
よりも高速に実行されます。
- SQL
- Java
- Golang
CREATE TABLE `player` (`id` INT, `coins` INT, `goods` INT);
INSERT INTO `player` (`id`, `coins`, `goods`) VALUES (1, 1000, 1), (2, 230, 2);
この SQL の使用方法の詳細については、 TiDBクラスタへの接続を参照し、クライアントを使用して TiDB クラスターに接続した後、手順に従って SQL ステートメントを入力します。
// ds is an entity of com.mysql.cj.jdbc.MysqlDataSource
try (Connection connection = ds.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement pstmt = connection.prepareStatement("INSERT INTO player (id, coins, goods) VALUES (?, ?, ?)"))
// first player
pstmt.setInt(1, 1);
pstmt.setInt(2, 1000);
pstmt.setInt(3, 1);
pstmt.addBatch();
// second player
pstmt.setInt(1, 2);
pstmt.setInt(2, 230);
pstmt.setInt(3, 2);
pstmt.addBatch();
pstmt.executeBatch();
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
デフォルトの MySQL JDBCDriver設定により、一括挿入のパフォーマンスを向上させるには、いくつかのパラメーターを変更する必要があります。
パラメータ | 意味 | 推奨シナリオ | 推奨Configuration / コンフィグレーション |
---|---|---|---|
useServerPrepStmts | サーバー側を使用して準備済みステートメントを有効にするかどうか | プリペアドステートメントを複数回使用する必要がある場合 | true |
cachePrepStmts | クライアントが準備済みステートメントをキャッシュするかどうか | useServerPrepStmts=true 時 | true |
prepStmtCacheSqlLimit | プリペアドステートメントの最大サイズ (デフォルトで 256 文字) | プリペアドステートメントが256文字を超える場合 | プリペアドステートメントの実際のサイズに従って構成されます |
prepStmtCacheSize | プリペアドステートメントキャッシュの最大数 (デフォルトでは 25) | 準備済みステートメントの数が 25 を超える場合 | 準備されたステートメントの実際の数に従って構成されます |
rewriteBatchedStatements | バッチステートメントを書き換えるかどうか | 一括操作が必要な場合 | true |
allowMultiQueries | バッチ操作を開始する | クライアントのバグは、 rewriteBatchedStatements = true とuseServerPrepStmts = true のときにこれを設定する必要があるためです。 | true |
MySQL JDBCDriverは、統合された構成も提供しuseConfigs
。 maxPerformance
で構成されている場合は、一連の構成を構成することと同じです。 mysql:mysql-connector-java:8.0.28
を例にとると、 useConfigs=maxPerformance
には以下が含まれます。
cachePrepStmts=true
cacheCallableStmts=true
cacheServerConfiguration=true
useLocalSessionState=true
elideSetAutoCommits=true
alwaysSendSetIsolation=false
enableQueryTimeouts=false
connectionAttributes=none
useInformationSchema=true
mysql-connector-java-{version}.jar!/com/mysql/cj/configurations/maxPerformance.properties
をチェックして、対応するバージョンの MySQL JDBC DriverのuseConfigs=maxPerformance
に含まれる構成を取得できます。
以下は、JDBC 接続文字列構成の一般的なシナリオです。この例では、ホスト: 127.0.0.1
、ポート: 4000
、ユーザー名: root
、パスワード: null、デフォルト データベース: test
:
jdbc:mysql://127.0.0.1:4000/test?user=root&useConfigs=maxPerformance&useServerPrepStmts=true&prepStmtCacheSqlLimit=2048&prepStmtCacheSize=256&rewriteBatchedStatements=true&allowMultiQueries=true
Java での完全な例については、以下を参照してください。
package main
import (
"database/sql"
"strings"
_ "github.com/go-sql-driver/mysql"
)
type Player struct {
ID string
Coins int
Goods int
}
func bulkInsertPlayers(db *sql.DB, players []Player, batchSize int) error {
tx, err := db.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare(buildBulkInsertSQL(batchSize))
if err != nil {
return err
}
defer stmt.Close()
for len(players) > batchSize {
if _, err := stmt.Exec(playerToArgs(players[:batchSize])...); err != nil {
tx.Rollback()
return err
}
players = players[batchSize:]
}
if len(players) != 0 {
if _, err := tx.Exec(buildBulkInsertSQL(len(players)), playerToArgs(players)...); err != nil {
tx.Rollback()
return err
}
}
if err := tx.Commit(); err != nil {
tx.Rollback()
return err
}
return nil
}
func playerToArgs(players []Player) []interface{} {
var args []interface{}
for _, player := range players {
args = append(args, player.ID, player.Coins, player.Goods)
}
return args
}
func buildBulkInsertSQL(amount int) string {
return "INSERT INTO player (id, coins, goods) VALUES (?, ?, ?)" + strings.Repeat(",(?,?,?)", amount-1)
}
Golang での完全な例については、以下を参照してください。
一括挿入
大量のデータを TiDB クラスターにすばやくインポートする必要がある場合は、データ移行のためにPingCAPが提供するさまざまなツールを使用することをお勧めします。 INSERT
ステートメントを使用することは、効率的ではなく、例外やその他の問題を自分で処理する必要があるため、最善の方法ではありません。
一括挿入に推奨されるツールは次のとおりです。
- データのエクスポート: Dumpling . MySQL または TiDB データをローカルまたは Amazon S3 にエクスポートできます。
- データのインポート: TiDB Lightning . Dumplingのエクスポート データ、 CSVファイル、またはAmazon Auroraから TiDB にデータを移行するをインポートできます。また、ローカル ディスクまたはAmazon S3 クラウド ディスクからのデータの読み取りもサポートしています。
- データの複製: TiDB データ移行 . MySQL、MariaDB、および Amazon Auroraデータベースを TiDB に複製できます。また、シャードされたインスタンスとテーブルをソース データベースからマージおよび移行することもサポートしています。
- データのバックアップと復元: バックアップと復元 (BR) . Dumplingと比較して、 BRはビッグデータのシナリオにより適しています。
ホットスポットを避ける
テーブルを設計するときは、多数の挿入操作があるかどうかを考慮する必要があります。その場合、テーブルの設計中にホットスポットを避ける必要があります。 主キーを選択セクションを参照し、 主キー選択時のルールに従ってください。
ホットスポットの問題を処理する方法の詳細については、 ホットスポットの問題のトラブルシューティングを参照してください。
AUTO_RANDOM
主キーを持つテーブルにデータを挿入する
挿入するテーブルの主キーがAUTO_RANDOM
属性の場合、デフォルトでは主キーを指定できません。たとえば、 bookshop
データベースでは、 users
テーブルのid
フィールドにAUTO_RANDOM
属性が含まれていることがわかります。
この場合、次のような SQL を使用して挿入することはできません。
INSERT INTO `bookshop`.`users` (`id`, `balance`, `nickname`) VALUES (1, 0.00, 'nicky');
エラーが発生します:
ERROR 8216 (HY000): Invalid auto random: Explicit insertion on auto_random column is disabled. Try to set @@allow_auto_random_explicit_insert = true.
挿入時にAUTO_RANDOM
列を手動で指定することはお勧めしません。
このエラーを処理するには、次の 2 つの解決策があります。
(推奨) この列を挿入ステートメントから削除し、TiDB が初期化した
AUTO_RANDOM
の値を使用します。これはAUTO_RANDOM
のセマンティクスに適合します。INSERT INTO `bookshop`.`users` (`balance`, `nickname`) VALUES (0.00, 'nicky');この列を指定する必要があることが確実な場合は、
SET
ステートメントを使用して、ユーザー変数を変更することにより、挿入時にAUTO_RANDOM
列を指定できるようにすることができます。SET @@allow_auto_random_explicit_insert = true; INSERT INTO `bookshop`.`users` (`id`, `balance`, `nickname`) VALUES (1, 0.00, 'nicky');
HTAP を使用する
TiDB では、HTAP 機能により、データを挿入するときに追加の操作を実行する必要がなくなります。追加の挿入ロジックはありません。 TiDB はデータの整合性を自動的に保証します。テーブルを作成した後、列指向の列指向のレプリカ同期をオンにするを使用してクエリを直接高速化するだけです。