測試平台系列(63) 軟刪除之殤
- 2021 年 9 月 18 日
- 筆記
- pity接口自動化測試平台, Python
大家好~我是
米洛
!這是一個完整的
接口測試平台系列教程
,希望能和大家一起學習,從0到1打造一個開源平台。歡迎關注我的公眾號
測試開發坑貨
,獲取最新文章教程!
回顧
上一節我們基本上搞定了數據構造器
的增刪改等操作,這一篇我們來講講軟刪除
相關的內容。
什麼是軟刪除
先聲明一下,我沒有
查閱關於具體軟刪除
的資料,大家有興趣的可以自己去搜索下,我說的都是自己的理解。
刪除數據,一般來說我們是從數據庫
或其他存儲介質裏面刪除數據,舉個🌰栗子:
我們需要把數據表
裏面的用戶A刪除掉,那我們最常見
的方式就是:
delete from user where name='A';
這個我理解的話,就是物理刪除
,刪除了以後,我們的數據表裏面不再有這條數據的信息
,如果還需要這個用戶,則需要重新insert一遍。
那軟刪除
又是什麼呢?軟刪除也可以叫做邏輯刪除
,比如我們給User表定義一個字段: deleted_at(代表這條數據刪除的時間),如果這個字段不為空,說明用戶未被刪除
, 反之則說明用戶已經被刪除了
。因為只是邏輯上的”刪除”,並不是真正刪掉了這條數據,所以又叫做邏輯刪除。
軟刪除的優點
軟刪除對比物理刪除的話,好處在哪呢?我認為有以下幾個方面:
- 數據未被
真實刪除
,數據更可靠 - 可以通過把刪除的時間戳賦予給deleted_at,從而知道刪除的時間,無形之中記錄了用戶的刪除操作
- 對於用戶而言和物理刪除沒什麼區別
- 擁有天然的回收站功能
軟刪除的缺點
上面提到了軟刪除
的好處,這裡就來說說軟刪除讓人又愛又恨
的地方。
-
查詢數據更加複雜
查詢條件需要帶入deleted_at is null,否則會查詢出
被刪除
的數據。除了這個以外,還有一個比較棘手的問題,且聽我慢慢道來。
索引之殤
索引這塊是一個比較棘手
的問題,特別是當我們有唯一索引的時候,我們先來看一種場景。
-
用戶表
我們的用戶表裏面有email字段,但email大家都懂的,是
不可以重複
的,所以我們需要給它加上唯一索引
。
場景一
來看第一種場景,我們為用戶表創建了一條數據:
email -> [email protected]
deleted_at -> null
我們這時候要刪除這條數據
,但因為軟刪除的原因我們會寫這樣的sql:
update user set deleted_at = now() where email='[email protected]';
你以為這就沒事兒了嗎?
接下來的事情就讓人腦崩
了,由於唯一索引email_uidx
的存在,我沒法再新插入一條[email protected]的數據了。
執行插入語句
的時候,會報duplicate index的錯誤,相信大家也不少見。
那這個問題怎麼解決呢?我們來看看場景二
。
試着解決一下
為了解決場景一的唯一索引
問題,我們想到了聯合索引
,啥子叫聯合唯一索引呢?就是多個字段同時相同的時候,數據才算重複,也就是觸發duplicate index
報錯。
於是我們創建一個聯合唯一索引:
ALTER TABLE user ADD UNIQUE INDEX(email, deleted_at);
這時候,我們再來看場景一:
- 創建用戶
[email protected]
deleted_at=null
接着,我們刪除之,這時候它變成了:
[email protected]
deleted_at=2021-09-12 20:13:00
然後我們繼續添加[email protected]
的用戶,發現可以添加了
。
你以為這就完事
了?那我們再看看場景二:
場景二
這個比較簡單,我們insert2條相同的數據:
insert into user (email, deleted_at) values ('[email protected]', null);
insert into user (email, deleted_at) values ('[email protected]', 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()
大家如果有更合適的方案,也歡迎一起探討
感激不盡!