大家好~我是
米洛
!
這是一個完整的介面測試平臺系列教程
,希望能和大家一起學習,從0到1打造一個開源平臺。
歡迎關注我的公眾號測試開發坑貨
,獲取最新文章教程!
回顧
上一節我們基本上搞定了資料構造器
的增刪改等操作,這一篇我們來講講軟刪除
相關的內容。
什麼是軟刪除
先宣告一下,我沒有
查閱關於具體軟刪除
的資料,大家有興趣的可以自己去搜尋下,我說的都是自己的理解。
刪除資料,一般來說我們是從資料庫
或其他儲存介質裡面刪除資料,舉個?栗子:
我們需要把資料表
裡面的使用者A刪除掉,那我們最常見
的方式就是:
delete from user where name='A';
這個我理解的話,就是物理刪除
,刪除了以後,我們的資料表裡面不再有這條資料的資訊
,如果還需要這個使用者,則需要重新insert一遍。
那軟刪除
又是什麼呢?軟刪除也可以叫做邏輯刪除
,比如我們給User表定義一個欄位: deleted_at(代表這條資料刪除的時間),如果這個欄位不為空,說明使用者未被刪除
, 反之則說明使用者已經被刪除了
。因為只是邏輯上的"刪除",並不是真正刪掉了這條資料,所以又叫做邏輯刪除。
軟刪除的優點
軟刪除對比物理刪除的話,好處在哪呢?我認為有以下幾個方面:
- 資料未被
真實刪除
,資料更可靠 - 可以通過把刪除的時間戳賦予給deleted_at,從而知道刪除的時間,無形之中記錄了使用者的刪除操作
- 對於使用者而言和物理刪除沒什麼區別
- 擁有天然的回收站功能
軟刪除的缺點
上面提到了軟刪除
的好處,這裡就來說說軟刪除讓人又愛又恨
的地方。
-
查詢資料更加複雜
查詢條件需要帶入deleted_at is null,否則會查詢出
被刪除
的資料。除了這個以外,還有一個比較棘手的問題,且聽我慢慢道來。
索引之殤
索引這塊是一個比較棘手
的問題,特別是當我們有唯一索引的時候,我們先來看一種場景。
-
使用者表
我們的使用者表裡面有email欄位,但email大家都懂的,是
不可以重複
的,所以我們需要給它加上唯一索引
。
場景一
來看第一種場景,我們為使用者表建立了一條資料:
email -> 123456@qq.com
deleted_at -> null
我們這時候要刪除這條資料
,但因為軟刪除的原因我們會寫這樣的sql:
update user set deleted_at = now() where email='12345@qq.com';
你以為這就沒事兒了嗎?
接下來的事情就讓人腦崩
了,由於唯一索引email_uidx
的存在,我沒法再新插入一條email=123456@qq.com的資料了。
執行插入語句
的時候,會報duplicate index的錯誤,相信大家也不少見。
那這個問題怎麼解決呢?我們來看看場景二
。
試著解決一下
為了解決場景一的唯一索引
問題,我們想到了聯合索引
,啥子叫聯合唯一索引呢?就是多個欄位同時相同的時候,資料才算重複,也就是觸發duplicate index
報錯。
於是我們建立一個聯合唯一索引:
ALTER TABLE user ADD UNIQUE INDEX(email, deleted_at);
這時候,我們再來看場景一:
- 建立使用者
email=12345@qq.com
deleted_at=null
接著,我們刪除之,這時候它變成了:
email=12345@qq.com
deleted_at=2021-09-12 20:13:00
然後我們繼續新增12345@qq.com
的使用者,發現可以新增了
。
你以為這就完事
了?那我們再看看場景二:
場景二
這個比較簡單,我們insert2條相同的資料:
insert into user (email, deleted_at) values ('12345@qq.com', null);
insert into user (email, deleted_at) values ('12345@qq.com', null);
好久沒寫sql,也不知道寫的對不對,湊合看,大概是這麼個意思。
這時候奇蹟出現了
,可以發現2條資料都插入成功了,也就是說,之前的email的唯一性
得不到保證了。
這是為什麼呢?
原來,當聯合索引裡面有欄位為null的時候,聯合索引會自動失效
。
那這個真的是非常難受
,可謂是修復了一個bug又導致了另一個bug。
解決方案一
其實我們可以把deleted_at
設定為和主鍵一樣的自增id,每當被刪除的時候就+1,恢復的時候也+1,預設為0,這樣也不會因為預設為NULL而引發唯一索引失效。
解決方案二
我們可以把deleted_at
設定為時間戳,預設為0(代表未刪除),一般來說,手動操作刪除操作
時間戳肯定有細微變化,這樣索引將不會生效,也就是不會影響到之前的資料。
但也有一個缺點: 如果一個資料被刪除多次,資料庫將存在許多
相同的被刪除的資料,當然一般人也不會做辣麼無聊的事情。
class Model(Base):
deleted_at = Column(BIGINT, nullable=False, default=0)
可以看到deleted_at如此定義,當有刪除操作的時候,我們設定deleted_at = time.time()即可。
import time
model.deleted_at = time.time()
大家如果有更合適的方案,也歡迎一起探討
感激不盡!