3

[Rails] 如何快速的對大資料量建立索引,避免 Downtime

 2 years ago
source link: https://blog.niclin.tw/2018/06/15/rails-%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E7%9A%84%E5%B0%8D%E5%A4%A7%E8%B3%87%E6%96%99%E9%87%8F%E5%BB%BA%E7%AB%8B%E7%B4%A2%E5%BC%95%E9%81%BF%E5%85%8D-downtime/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Postgres 在建立索引時,會阻塞 DML 也就是 lock 整個 table 的寫入(讀取則不影響),所以當需要對大資料量的 Table 打 index 時,會造成有 Downtime 時間,這在 Production 這種高並發的環境下是不適合的。

並發計算建立 index

Postgres 在建立索引時有提供 CONCURRENTLY 的選項可以設定(並發計算),這樣一來就不會影響 table 的寫入行為(INSERT, UPDATE, DELETE)

可以同時處理一般的寫入行為,也可以打 index, 不過是生成時間會再慢一些,但至少沒有停機時間。

在 Rails 4 之後可以直接加入 algorithm: :concurrently 在你的 migration 檔案內

class AddIndexToUsers < ActiveRecord::Migration
  disable_ddl_transaction!

  def change
    add_index :users, :alive, algorithm: :concurrently
  end
end

不過這邊要注意的是, disable_ddl_transaction! 與 algorithm: :concurrently 是一組的,必須一起使用。

如果你有其他的 migrate 要做,建議分開。

為什麼要用 disable_ddl_transaction! ?

因為在 Rails 的 migration 中預設每個執行都會包一個 transaction ,如果有失敗的情況就會整個 Rollback,但如果要使用 Postgres 當中的 Concurrently 去建立 index 的話,必須要在 transaction 之外 (concurrent indexes must be created outside a transaction.)

一般 CREATE INDEX 情況下,只會掃一次表,但如果使用這個並發計算參數時,則會引發 Database 掃兩次表,並且等待所有潛在會讀到該索引的事務結束,這樣一來會增加 CPU + I/O 的負擔,不過拿這個負擔來換 Downtime 想必是划算的。

使用 concurrently 參數時,有可能遇到創建索引失敗的狀況,比方說建立 uniqu 索引結果發現數據重複了,此時 Table 上會存在索引,這是因為帶 concurrently 參數的指令發出後,會馬上在 Log 裡面插一個 index 記錄進去,又因為這個索引建立失敗了,會被標記一個 INVALID 的狀態

"idx" btree (col) INVALID

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK