ent M2M模型在pxc叢集中的一個大坑

天地一小儒發表於2023-05-18

ent M2M模型在pxc叢集中的一個大坑

事故簡要分析

PXC叢集3個節點,在插入資料時,如果使用資料庫自己生成的主鍵,一般順序為1,4,7,10…

這裡就是坑的源頭,在ent底層程式碼中,在做M2M模型插入時,會先插入兩個模型,再插入中間表。即,假設M2M模型為group和user(https://entgo.io/docs/schema-edges#m2m-two-types),這裡資料庫會產生3張表,users, groups 和 user_groups,其中user_groups存放userid和groupid,這樣就組成了一個多對多模型。

事故復原

假設先插入一個group,再插入一堆user,而在使用ent的CreateBulk插入user時,問題就發生了,我們在測試環境永遠無法復現出問題,而只要一上生產環境,問題必然出現。原因就在於測試環境使用單節點mysql,生產環境使用的pxc叢集!

先看插入程式碼:

// Add Group
hub := client.Group.
        Create().
        SetName("GitHub").
        SaveX(ctx)

// Add Users
bulk := make([]*User, len(users))
for i, user:= range users {
			bulk[i] = client.User.
        Create().
        SetAge(30).
        SetName("a8m").
        AddGroups(hub)
}
_, err = client.User.CreateBulk(bulk...).Save(ctx)
if err != nil {
	return errors.Wrap(err, "User.CreateBulk") // pxc叢集必然報錯
}

原始碼分析

.Save()中呼叫了BatchCreate,又跳轉nodes
Untitled

nodes中主要兩行程式碼是batchInsert和batchAddM2M,直觀理解就是,先插入users,在插入user_groups,而插入user_groups時需要拿到所有users的id,由於我們是在一個事務裡完成的,因此實際資料並未真正插入,因此ent做了一個看起來沒問題的騷操作。
Untitled 1

從batchInsert到c.insertLastIDs
Untitled 2

重點來了,MySQL資料庫id的處理結果是插入一條,剩下的資料加1,這是理想情況。比如插入時,第一條user的id是4,那剩下的id就是5,6,7,8,9…,但實際上,pxc叢集處理時並不是這樣,因此造成插入users時,id實際是4,7,10,13…,因此最終執行M2M插入操作時報錯:add m2m edge for table
Untitled 3

正確的應該是1,4 1,7。於是報錯:Error 1452: FOREIGN KEY constraint failed
Untitled 4

解決方式

PXC叢集下

  • 能不用M2M就不用M2M。M2M改O2M;
  • 必須使用M2M時,不使用CreateBulk。該批次為單條;
  • 必須使用CreateBulk時,手動指定id。自行使用分散式id。

相關文章