後端存儲實戰一

訂單系統的核心功能和數據

必備的功能,包括但不限於如下:

1.創建訂單

2.隨着購物流程更新訂單狀態

3.查詢訂單,包括用訂單數據生成各種報表

爲了支撐這些必備功能,在數據庫中,我們至少需要有這樣幾張表

1.訂單主表:也叫訂單表,保存訂單的基本信息

2.訂單商品表:保存訂單中的商品信息

3.訂單支付表:保存訂單的支付和退款信息

4.訂單優惠表:保存訂單使用的所有優惠信息

幾個表之間的關係如下:

訂單主表和後面的幾個子表都是一對多的關係,關聯的外鍵就是訂單主表的主鍵,也就是訂單號。

如何避免重複下單?

保證自己的下單接口是冪等的。

具體實現如下,進入訂單頁面時,前端請求後端獲取一個訂單號,後端將訂單號設爲唯一索引,注意不要使用訂單號當做主鍵。

這樣可以利用唯一約束,前端帶着訂單號,重複下單會插入失敗。時序流程圖如下

值得注意的一點是,插入衝突了,不要報錯給前端,返回成功就可以了。

爲啥不用訂單號來做主鍵?

生成全局唯一訂單號如果不是自增的,插入mysql innodb表的時候,底層的B+樹索引是不是會發生頁分裂等問題,影響插入性能,如果遇到大促,短時間生成大量訂單,寫入會成爲瓶頸。

所以主鍵使用db的默認的自增主鍵比較好,不過犧牲是會多建個索引

如果要使用訂單號當做主鍵,不用額外建個唯一索引,另外很多業務在設計訂單號規則的時候都不是完全隨機的,一般都是遞增的。這種情況下,頁分裂就不會特別嚴重。

總結:各有利弊,個人比較喜歡不用訂單號做主鍵

生成一個唯一訂單號放在前端,如何保證客戶繞過客戶端直接發送請求惡意亂填訂單號的問題,符合規則的訂單號,而這個訂單號有可能是後續系統生成的

1.生成的時候在redis中set下,下單時查詢,不再裏面的都拒絕

2.生成訂單號加些自己的東西,什麼secret等等,總而言之,別人看不出你的訂單號生成規律,不好模仿或者無法模仿,而你自己可以通過訂單號解析出來是不是自己後端生成的(不依賴存儲,類似於jwt的生成和認證)

ABA問題

一句話總結就是,2個更新請求,先發出的請求因爲網絡原因導致比後發出的請求到來的慢,從而把最新修改的值又更到舊版本了。

舉個例子:

訂單支付之後,小二要發貨,發貨完成後要填個快遞單號。假設說,小二填了一個單號 666,剛填完,發現填錯了,趕緊再修改成 888。對訂單服務來說,這就是 2 個更新訂單的請求。

正常情況下,訂單中的快遞單號會先更新成 666,再更新成 888,這是沒問題的。那不正常情況呢?666 請求到了,單號更新成 666,然後 888 請求到了,單號又更新成 888,但是 666 更新成功的響應丟了,調用方沒收到成功響應,自動重試,再次發起 666 請求,單號又被更新成 666 了,這數據顯然就錯了。這就是非常有名的 ABA 問題

解決方法:使用版本號

給你的訂單主表增加一列,列名可以叫 version,也即是“版本號”的意思。每次查詢訂單的時候,版本號需要隨着訂單數據返回給頁面。頁面在更新數據的請求中,需要把這個版本號作爲更新請求的參數,再帶回給訂單更新服務。訂單服務在更新數據的時候,需要比較訂單當前數據的版本號,是否和消息中的版本號一致,如果不一致就拒絕更新數據。如果版本號一致,還需要再更新數據的同時,把版本號 +1。“比較版本號、更新數據和版本號 +1”,這個過程必須在同一個事務裏面執行。具體的 SQL 可以這樣來寫:

UPDATE orders set tracking_number = 666, version = version + 1 WHERE version = 8 and order_id = 123456;

在這條 SQL 的 WHERE 條件中,version 的值需要頁面在更新的時候通過請求傳進來。通過這個版本號,就可以保證,從我打開這條訂單記錄開始,一直到我更新這條訂單記錄成功,這個期間沒有其他人修改過這條訂單數據。因爲,如果有其他人修改過,數據庫中的版本號就會改變,那我的更新操作就不會執行成功。我只能重新查詢新版本的訂單數據,然後再嘗試更新。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章