購物車系統的存儲設計

1 主要功能

在用戶選購商品時,下單前,暫存用戶想購買的商品。

購物車對數據可靠性要求不高,性能也無特別要求,在整個電商系統是相對容易設計和實現的一個子系統。

購物車系統的主要功能:

支撐這些功能,存儲模型如何設計?

只要一個 “購物車” 實體。

2 主要屬性

打開京東購物車頁面:SKUID(商品 ID)、數量、加購時間和勾選狀態

圖片備註:圖片來源於網絡,僅供本文介紹、評論及說明某問題,適當引用。

“勾選狀態” 屬性,即在購物車界面,每件商品前面的那個小對號,表示在結算下單時,是否要包含這件商品。至於商品價格和總價、商品介紹等都能實時從其他系統獲取,無需購物車系統保存。

購物車功能簡單,但設計購物車系統的存儲時,仍有一些問題需考慮。

3 原則

3.1 思考

3.1.1 用戶未登錄,在瀏覽器中加購,關閉瀏覽器再打開,剛纔加購的商品還在嗎?

存在。

若用戶未登錄,加購的商品也會被保存在用戶的電腦。即使關閉瀏覽器再打開,購物車的商品仍存在。

3.1.2 用戶未登錄,在瀏覽器中加購,然後登錄,剛纔加購的商品還在嗎?

存在。

若用戶先加購,再登錄。登錄前加購的商品就會被自動合併到用戶名下,所以登錄後購物車中仍有登錄前加購的商品。

3.1.3 關閉瀏覽器再打開,上一步加購的商品還在嗎?

不存在。

關閉瀏覽器再打開,這時又變爲未登錄狀態,但是之前未登錄時加購的商品已經被合併到剛剛登錄的用戶名下了,所以購物車是空的。

3.1.4 再打開手機,用相同的用戶登錄,第二步加購的商品還在嗎?

存在。使用手機登錄相同的用戶,看到的就是該用戶的購物車,這時無論你在手機 App、電腦還是微信中登錄,只要相同用戶,看到就是同一購物車,所以第 2 步加購的商品是存在的。

若不仔細把這些問題考慮清楚,用戶使用購物車時,就會感覺不好用,不是加購的商品莫名其妙丟了,就是購物車莫名其妙多出一些商品。

要解決上面這些問題,只要在存儲設計時,把握如下

3.2 原則

購物車系統需保存兩類購物車:

4 “暫存購物車” 存儲設計

4.1 保存在客戶端 or 服務端?

若存在服務端,則每個暫存購物車都得有個全局唯一標識,這不易設計。保存在服務端,還要浪費服務端資源。所以,肯定保存在客戶端:

客戶端存儲可選擇不多:

選擇哪種更好?各有優劣。這場景中,使用 Cookie 和 LocalStorage 最關鍵區別:

使用 Cookie 存儲,實現簡單,加減購物車、合併購物車過程,由於服務端可讀寫 Cookie,這樣全部邏輯都可在服務端實現,並且客戶端和服務端請求的次數也相對少。

使用 LocalStorage 存儲,實現相對複雜,客戶端和服務端都要實現業務邏輯,但 LocalStorage 好在其存儲容量比 Cookie 的 4KB 上限大得多,而且不用像 Cookie 那樣,無論用不用,每次請求都要帶着,可節省帶寬。

所以,選擇 Cookie 或 LocalStorage 存儲 “暫存購物車” 都行,根據優劣勢選型即可:

不管哪種存儲,暫存購物車保存的

4.2 數據格式

都一樣。參照實體模型設計,JSON 表示:

{
    "cart"[
        {
            "SKUID": 8888,
            "timestamp": 1578721136,
            "count": 1,
            "selected"true
        },
        {
            "SKUID": 6666,
            "timestamp": 1578721138,
            "count": 2,
            "selected"false
        }
    ]
}

5 用戶購物車 存儲設計

用戶購物車須保證多端數據同步,數據須保存在服務端。常規思路:設計一張購物車表,把數據存在 MySQL。表結構同樣參照實體模型:

圖片img

需在 user_id 建索引,因爲查詢購物車表,都以 user_id 作爲查詢條件。

也可選擇更快的 Redis 保存購物車數據:

如:

{
    "KEY": 6666,
    "VALUE"[
        {
            "FIELD": 8888,
            "FIELD_VALUE"{
                "timestamp": 1578721136,
                "count": 1,
                "selected"true
            }
        },
        {
            "FIELD": 6666,
            "FIELD_VALUE"{
                "timestamp": 1578721138,
                "count": 2,
                "selected"false
            }
        }
    ]
}

爲便理解,用 JSON 表示 Redis 中 HASH 的數據結構:

讀寫性能,Redis 比 MySQL 快得多,Redis 就一定比 MySQL 好嗎?

5.1 MySQL V.S Redis 存儲

綜合比較下來,考慮到需求變化,推薦 MySQL 存儲購物車數據。若追求性能或高併發,也可選擇使用 Redis。

設計存儲架構過程就是不斷抉擇過程。很多情況下,可選擇方案不止一套,選擇時需考慮實現複雜度、性能、系統可用性、數據可靠性、可擴展性等。這些條件每一個都不是絕對不可以犧牲的,不要讓一些 “所謂的常識” 禁錮思維。

比如,一般認爲數據絕不可丟,即不能犧牲數據可靠性。但用戶購物車存儲,使用 Redis 替代 MySQL,就是犧牲數據可靠性換取高性能。很低概率的丟失少量數據可接受。性能提升帶來的收益遠大於丟失少量數據而付出的代價,這選擇就值得。

如果說不考慮需求變化這個因素,犧牲一點點數據可靠性,換取大幅性能提升,Redis 是最優解。

6 總結

在給購物車設計存儲時,爲確保:

除了每個用戶的 “用戶購物車”,還要實現一個“暫存購物車” 保存用戶未登錄時加購的商品,並在用戶登錄後自動合併 “暫存購物車” 和“用戶購物車”。

暫存購物車存儲在客戶端瀏覽器或 App,可存放到 Cookie 或 LocalStorage。用戶購物車保存在服務端,可以選擇使用:

思考

既然用戶的購物車數據存放在 MySQL 或 Redis 各有優劣。那能否把購物車數據存在 MySQL,並用 Redis 緩存?不就兼顧二者優勢?若可行,如何保證 Redis 中的數據和 MySQL 數據一致性?

用 Redis 給購物車庫做緩存,技術可行。但考慮:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/lBS7IuJoE8cpRVkhh2lj9g