Mysql

使用 InnoDB 隨機慢速插入

  • December 29, 2019

TL;DR :如何改進表中的插入時間,使每次插入的執行時間低於 100 毫秒?

該表由一個接收事務的網路服務使用,因此我無法對插入進行分組。Web 服務的超時時間必須在 150 毫秒以下。

  • 基礎設施: AWS
  • 應用程序(php): EC​​2 t2.micro
  • Mysql : RDS t2.small - 100GB 磁儲存

這是我在生產中所做的測量

數據狗

**左上角:**我們可以看到很多插入耗時超過 500 毫秒,有些甚至超過 1 秒。只有少數低於 50 毫秒。(紅條是第 95 個百分位)

右上角: RDS cpu 總是很低(記憶體使用量也很穩定)

**左下角:**表格中的插入數量

**右下角:**藍線是寫入 IOPS,紅線是寫入 lentency,藍條是 disk_queue_depth * 100。磁碟隊列幾乎總是低於 1。當其他一切看起來都很好時,我們可以看到寫入 lentency 的巨大峰值:IOPS 沒有增加也不是硬體資源消耗。

我創建了一個腳本來單獨測量插入時間account_transaction

// pseudo code
for ($i=0; $i<100; $i++) {
   $ts=microtime(true);
   $db->insert(data);
   echo microtime(true)-$ts;
}

100 次插入的輸出:(已排序)

0.012468814849854
0.012633085250854
0.012740135192871
0.012858867645264
0.012872934341431
...
...
...
0.038048028945923
0.040112018585205
0.040735960006714
0.042028903961182
0.42462396621704
Avg : 0.025037434101105

我幾乎總是得到相同的結果:1% 或 2% 的插入非常慢。如果我執行 1000 次插入,則相同。


我試過的

  • 禁用除 PK 之外的所有索引:沒有改進。

  • 重建所有索引:沒有改進。

  • innodb_flush_log_at_trx_commit=0: 很大的改進:

    • 慢速插入完全消失
    • 平均插入時間快 5 倍
  • 切換到更大的 RDS(具有 100GB SSD 和 1000 IOPS 預配置的 m4.large):一些改進:

    • 緩慢的插入幾乎消失了:如果有的話,可能是 0.1%,而且很少超過 100 毫秒
    • 平均插入時間快 3 倍

不幸innodb_flush_log_at_trx_commit=0的是,這不是一個選擇,因為我不能承受任何交易的損失。

我的想法

即使在基本硬體上,我希望插入在大多數情況下(<1ms)都會非常快。表只有 4 個索引和 200k 行。我認為這是錯誤的嗎?

由於測試,我懷疑瓶頸在 I/O 領域,innodb_flush_log_at_trx_commit=0但我從 AWS 獲得的指標似乎並不能證實這一點。我又錯了嗎?

表 account_transaction

CREATE TABLE `account_transaction` (
 `id` char(21) COLLATE utf8_unicode_ci NOT NULL,
 `seqid` int(9) unsigned NOT NULL AUTO_INCREMENT,
 `account_id` char(21) COLLATE utf8_unicode_ci NOT NULL,
 `token` bigint(20) unsigned DEFAULT NULL,
 ...
 ...
 `creation_date` datetime NOT NULL,
 `processing_date` datetime DEFAULT NULL,
 PRIMARY KEY (`seqid`),
 UNIQUE KEY `id` (`id`),
 KEY `consolidation_idx` (`trans_link`,`token`),
 KEY `idx_token` (`token`)
) ENGINE=InnoDB AUTO_INCREMENT=239304 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

id看起來像1473430240HBCRSPL9FPB= unix epoch + 10 個隨機字元:我們使用這種格式而不是標準 UUID 來保持良好的插入性能。始終為 21 個字元。

seqid用於跟踪行號。我們id有一個隨機部分,因此我們不能將其用於此目的。

select count(1) from account_transaction= 239k

問題

  1. 有沒有辦法消除高貸款峰值?
  2. 我應該使用更大的硬體來提高性能嗎?我的流量真的很低…
  3. 我必須確保 99.9% 的插入在 100 毫秒內完成。這在 RDS mysql 上是否現實?
  • 我建議您距離 AWS 數據中心 12 毫秒(延遲)。你被這個成本困住了。結論:注意時間。
  • 只有當客戶端和伺服器在同一個數據中心並且磁碟是 SSD 時,“<1ms”才是現實的。什麼是“磁儲存”?也許那是一個舊的、旋轉的磁碟。在這種情況下,10 毫秒是一次寫入的合理猜測。而且很難從可用的數字中確定延遲到底是什麼。(估計不到12ms。) 結論:一定要使用SSD。
  • 亞馬遜對同一硬體上其他實例的干擾有何看法?你有一個“小”實例;可能有許多其他人共享相同的 I/O。如果其中一個有尖峰,您可能會放慢速度。使用“大”,您的競爭客戶可能會更少。它可能是SSD。結論:關注平均或第 95 個百分位,而不是峰值。
  • innodb_flush_log_at_trx_commit=1 每個事務至少 需要1 次磁碟寫入。=0延遲和合併寫入,因此顯著加速。軟化它的一種方法是將多個查詢批處理到單個BEGINCOMMIT中。(只有在邏輯合理的情況下才這樣做。) 結論:更大的交易。(但是,您仍然會遇到每個事務而不是每個插入12+ms 的問題。)
  • 對索引(索引除外UNIQUE)的更新會延遲,通常可以視為零影響。
  • char(21) COLLATE utf8_unicode_ci– 總是21個字元嗎?如果沒有,請使用VARCHAR. 也許它總是ascii,而不是utf8?如果是這樣,請使用CHARACTER SET ascii.
  • PRIMARY KEY (seqid ), UNIQUE KEYid (id )– 為什麼兩者都有?如前所述,它會導致時間中包含成本,INSERT在送出之前驗證唯一性。
  • 活動似乎很低。通常,當人們遇到每秒數百或數千個查詢的問題時,他們會問像您這樣的問題。
  • 這是獲取 16 字節、按時間順序排列的 UUID 的方法:http: //mysql.rjweb.org/doc.php/uuid。(但不是那麼容易顯示。)

引用自:https://dba.stackexchange.com/questions/161358