簡體   English   中英

為什么 MySQL InnoDB 插入這么慢?

[英]Why is MySQL InnoDB insert so slow?

我使用大隨機數作為鍵(來自另一個系統)。 在相當小的(如幾百萬行)表上插入和更新所花費的時間比我認為的合理要長得多。

我提煉了一個非常簡單的測試來說明。 在測試表中,我試圖讓它盡可能簡單; 我的真實代碼沒有如此簡單的布局,並且具有關系和附加索引等。 但是,更簡單的設置顯示出等效的性能。

結果如下:

creating the MyISAM table took 0.000 seconds
creating 1024000 rows of test data took 1.243 seconds
inserting the test data took 6.335 seconds
selecting 1023742 rows of test data took 1.435 seconds
fetching 1023742 batches of test data took 0.037 seconds
dropping the table took 0.089 seconds
creating the InnoDB table took 0.276 seconds
creating 1024000 rows of test data took 1.165 seconds
inserting the test data took 3433.268 seconds
selecting 1023748 rows of test data took 4.220 seconds
fetching 1023748 batches of test data took 0.037 seconds
dropping the table took 0.288 seconds

將 1M 行插入 MyISAM 需要 6 秒; 進入 InnoDB 需要3433 秒

我究竟做錯了什么? 什么配置錯誤? (MySQL 是具有默認值的普通 Ubuntu 安裝)

這是測試代碼:

import sys, time, random
import MySQLdb as db

# usage: python script db_username db_password database_name

db = db.connect(host="127.0.0.1",port=3306,user=sys.argv[1],passwd=sys.argv[2],db=sys.argv[3]).cursor()

def test(engine):

    start = time.time() # fine for this purpose
    db.execute("""
CREATE TEMPORARY TABLE Testing123 (
k INTEGER PRIMARY KEY NOT NULL,
v VARCHAR(255) NOT NULL
) ENGINE=%s;"""%engine)
    duration = time.time()-start
    print "creating the %s table took %0.3f seconds"%(engine,duration)

    start = time.time()
    # 1 million rows in 100 chunks of 10K
    data = [[(str(random.getrandbits(48)) if a&1 else int(random.getrandbits(31))) for a in xrange(10*1024*2)] for b in xrange(100)]
    duration = time.time()-start
    print "creating %d rows of test data took %0.3f seconds"%(sum(len(rows)/2 for rows in data),duration)

    sql = "REPLACE INTO Testing123 (k,v) VALUES %s;"%("(%s,%s),"*(10*1024))[:-1]
    start = time.time()
    for rows in data:
        db.execute(sql,rows)
    duration = time.time()-start
    print "inserting the test data took %0.3f seconds"%duration

    # execute the query
    start = time.time()
    query = db.execute("SELECT k,v FROM Testing123;")
    duration = time.time()-start
    print "selecting %d rows of test data took %0.3f seconds"%(query,duration)

    # get the rows in chunks of 10K
    rows = 0
    start = time.time()
    while query:
        batch = min(query,10*1024)
        query -= batch
        rows += len(db.fetchmany(batch))
    duration = time.time()-start
    print "fetching %d batches of test data took %0.3f seconds"%(rows,duration)

    # drop the table
    start = time.time()
    db.execute("DROP TABLE Testing123;")
    duration = time.time()-start
    print "dropping the table took %0.3f seconds"%duration


test("MyISAM")
test("InnoDB")

InnoDB 具有事務支持,您沒有使用顯式事務,因此 innoDB 必須在每個語句之后進行提交( “為每個插入執行日志刷新到磁盤” )。

在循環之前執行此命令:

START TRANSACTION

這在你循環之后

COMMIT

InnoDB 不能很好地處理“隨機”主鍵。 嘗試順序鍵或自動增量,我相信你會看到更好的性能。 您的“真實”鍵字段仍然可以被索引,但對於批量插入,您最好在插入完成后一次性刪除並重新創建該索引。 有興趣看看你的基准測試!

一些相關問題

我需要同時在 MyISAM 和 InnoDB 中對插入密集型應用程序進行測試。 有一個設置可以解決我遇到的速度問題。 嘗試設置以下內容:

innodb_flush_log_at_trx_commit = 2

通過閱讀此處的設置,確保您了解風險。

另請參閱https://dba.stackexchange.com/questions/12611/is-it-safe-to-use-innodb-flush-log-at-trx-commit-2/12612https://dba.stackexchange。 com/a/29974/9405

我在我的系統上得到了非常不同的結果,但這不是使用默認值。 您可能在 innodb-log-file-size 上遇到瓶頸,默認情況下為 5M。 在 innodb-log-file-size=100M 我得到這樣的結果(所有數字都以秒為單位):

                             MyISAM     InnoDB
create table                  0.001      0.276
create 1024000 rows           2.441      2.228
insert test data             13.717     21.577
select 1023751 rows           2.958      2.394
fetch 1023751 batches         0.043      0.038
drop table                    0.132      0.305

增加innodb-log-file-size將加快幾秒鍾。 通過設置innodb-flush-log-at-trx-commit=20來刪除持久性保證也會在一定程度上改善插入數量。

InnoDB 的默認值實際上非常糟糕。 InnoDB 非常依賴於 RAM,如果您調整設置,您可能會發現更好的結果。 這是我使用InnoDB 優化基礎的指南

你的 innodb 緩沖池大小是多少? 確保您已將其設置為 RAM 的 75%。 通常在 InnoDB 的主鍵順序中插入更好。 但是對於大池大小,您應該會看到良好的速度。

解決方案

  1. 創建與您當前的 PRIMARY 密鑰相同的新 UNIQUE 密鑰
  2. 添加新列id為無符號整數,auto_increment
  3. 在新的id列上創建主鍵

Bam,立即 10 倍以上的插入改進。

加速插入的事情:

  • 在大量插入空表之前,我已經從表中刪除了所有鍵
  • 然后發現我有一個問題,索引不適合內存。
  • 還發現即使沒有使用 binlog,我也有 sync_binlog=0(應該是 1)。
  • 還發現我沒有設置 innodb_buffer_pool_instances

這是一個古老的話題,但經常被搜索。 只要您意識到在最后一秒左右丟失已提交事務的風險(如上面@philip Koshy 所述),在大規模更新之前,您就可以設置這些全局參數

innodb_flush_log_at_trx_commit=0
sync_binlog=0

然后在更新完成后重新打開(如果需要)。

innodb_flush_log_at_trx_commit=1
sync_binlog=1

完全符合 ACID。

當這兩者都關閉和打開時,寫入/更新性能會有巨大差異。 根據我的經驗,上面討論的其他內容會有所不同,但只是微不足道。

影響update/insert另一件事是全文索引。 在一種情況下,具有兩個文本字段的表具有全文索引,插入 200 萬行需要 6 小時,而在刪除全文索引后僅需要 10 分鍾。 更多索引,更多時間。 因此,可能會在大量插入/更新之前刪除唯一鍵和主鍵以外的搜索索引。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM