插入数据

    在阅读本页面之前,你需要准备以下事项:

    插入行

    假设你需要插入多行数据,那么会有两种插入的办法,假设需要插入 3 个玩家数据:

    • 一个多行插入语句

    • 多个单行插入语句

      1. INSERT INTO `player` (`id`, `coins`, `goods`) VALUES (1, 1000, 1);
      2. INSERT INTO `player` (`id`, `coins`, `goods`) VALUES (2, 230, 2);
      3. INSERT INTO `player` (`id`, `coins`, `goods`) VALUES (3, 300, 5);

    一般来说使用一个多行插入语句,会比多个单行插入语句快。

    • SQL
    • Java
    • Golang
    • Python

    在 SQL 中插入多行数据的示例:

    1. CREATE TABLE `player` (`id` INT, `coins` INT, `goods` INT);
    2. INSERT INTO `player` (`id`, `coins`, `goods`) VALUES (1, 1000, 1), (2, 230, 2);

    有关如何使用此 SQL,可查阅文档部分,按文档步骤使用客户端连接到 TiDB 集群后,输入 SQL 语句即可。

    在 Java 中插入多行数据的示例:

    1. // ds is an entity of com.mysql.cj.jdbc.MysqlDataSource
    2. try (Connection connection = ds.getConnection()) {
    3. connection.setAutoCommit(false);
    4. PreparedStatement pstmt = connection.prepareStatement("INSERT INTO player (id, coins, goods) VALUES (?, ?, ?)"))
    5. // first player
    6. pstmt.setInt(1, 1);
    7. pstmt.setInt(2, 1000);
    8. pstmt.setInt(3, 1);
    9. pstmt.addBatch();
    10. // second player
    11. pstmt.setInt(1, 2);
    12. pstmt.setInt(2, 230);
    13. pstmt.setInt(3, 2);
    14. pstmt.addBatch();
    15. pstmt.executeBatch();
    16. connection.commit();
    17. } catch (SQLException e) {
    18. e.printStackTrace();
    19. }

    MySQL JDBC Driver 还提供了一个集成配置项:useConfigs。当它配置为 maxPerformance 时,相当于配置了一组配置,以 mysql:mysql-connector-java:8.0.28 为例,useConfigs=maxPerformance 包含:

    你可以自行查看 mysql-connector-java-{version}.jar!/com/mysql/cj/configurations/maxPerformance.properties 来获得对应版本 MySQL JDBC Driver 的 包含配置。

    在此处给出一个较为的通用场景的 JDBC 连接字符串配置,以 Host: 127.0.0.1,Port: 4000,用户名: root,密码: 空,默认数据库: test为例:

    1. jdbc:mysql://127.0.0.1:4000/test?user=root&useConfigs=maxPerformance&useServerPrepStmts=true&prepStmtCacheSqlLimit=2048&prepStmtCacheSize=256&rewriteBatchedStatements=true&allowMultiQueries=true

    有关 Java 的完整示例,可参阅:

    在 Golang 中插入多行数据的示例:

    1. import (
    2. "database/sql"
    3. "strings"
    4. _ "github.com/go-sql-driver/mysql"
    5. )
    6. type Player struct {
    7. ID string
    8. Coins int
    9. Goods int
    10. }
    11. func bulkInsertPlayers(db *sql.DB, players []Player, batchSize int) error {
    12. tx, err := db.Begin()
    13. if err != nil {
    14. return err
    15. }
    16. stmt, err := tx.Prepare(buildBulkInsertSQL(batchSize))
    17. if err != nil {
    18. return err
    19. }
    20. defer stmt.Close()
    21. for len(players) > batchSize {
    22. if _, err := stmt.Exec(playerToArgs(players[:batchSize])...); err != nil {
    23. tx.Rollback()
    24. return err
    25. }
    26. players = players[batchSize:]
    27. }
    28. if len(players) != 0 {
    29. if _, err := tx.Exec(buildBulkInsertSQL(len(players)), playerToArgs(players)...); err != nil {
    30. tx.Rollback()
    31. return err
    32. }
    33. }
    34. if err := tx.Commit(); err != nil {
    35. tx.Rollback()
    36. return err
    37. return nil
    38. }
    39. func playerToArgs(players []Player) []interface{} {
    40. var args []interface{}
    41. for _, player := range players {
    42. args = append(args, player.ID, player.Coins, player.Goods)
    43. }
    44. return args
    45. }
    46. func buildBulkInsertSQL(amount int) string {
    47. return "INSERT INTO player (id, coins, goods) VALUES (?, ?, ?)" + strings.Repeat(",(?,?,?)", amount-1)
    48. }

    有关 Golang 的完整示例,可参阅:

    在 Python 中插入多行数据的示例:

    1. import MySQLdb
    2. connection = MySQLdb.connect(
    3. host="127.0.0.1",
    4. port=4000,
    5. user="root",
    6. password="",
    7. database="bookshop",
    8. autocommit=True
    9. )
    10. with get_connection(autocommit=True) as connection:
    11. with connection.cursor() as cur:
    12. player_list = random_player(1919)
    13. for idx in range(0, len(player_list), 114):
    14. cur.executemany("INSERT INTO player (id, coins, goods) VALUES (%s, %s, %s)", player_list[idx:idx + 114])

    有关 Python 的完整示例,可参阅:

    • 数据导出工具:。可以导出 MySQL 或 TiDB 的数据到本地或 Amazon S3 中。
    • 数据导入工具:TiDB Lightning。可以导入 Dumpling 导出的数据、CSV 文件,或者 。同时支持在本地盘或 Amazon S3 云盘读取数据。
    • 数据同步工具:TiDB Data Migration。可同步 MySQL、MariaDB、Amazon Aurora 数据库到 TiDB 中。且支持分库分表数据库的迁移。
    • 数据备份恢复工具:。相对于 Dumpling,BR 更适合大数据量的场景。

    避免热点

    在设计表时需要考虑是否存在大量插入行为,若有,需在表设计期间对热点进行规避。请查看部分,并遵从选择主键时应遵守的规则

    更多有关热点问题的处理办法,请参考 文档。

    在插入的表主键为 AUTO_RANDOM 时,这时默认情况下,不能指定主键。例如 数据库中,可以看到 users 表id 字段含有 AUTO_RANDOM 属性。

    此时,不可使用类似以下 SQL 进行插入:

    将会产生错误:

    1. 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 的列。这时,你有两种解决办法处理此错误:

    • (推荐) 插入语句中去除此列,使用 TiDB 帮你初始化的 AUTO_RANDOM 值。这样符合 AUTO_RANDOM 的语义。

      1. INSERT INTO `bookshop`.`users` (`balance`, `nickname`) VALUES (0.00, 'nicky');
    • 如果你确认一定需要指定此列,那么可以使用 通过更改用户变量的方式,允许在插入时,指定 AUTO_RANDOM 的列。

      1. INSERT INTO `bookshop`.`users` (`id`, `balance`, `nickname`) VALUES (1, 0.00, 'nicky');

    使用 HTAP