心態(tài)崩了!這個問題,困擾了我兩個小時
一次因為主鍵設(shè)置引發(fā)的“數(shù)據(jù)之謎”
最近在開發(fā)一個聊天功能時,我遇到了一個奇怪的問題:明明數(shù)據(jù)庫里有9條符合條件的記錄,但查詢出來的結(jié)果卻只有1條。
問題的出現(xiàn)
事情是這樣的:我在開發(fā)一個聊天功能,用戶之間的對話記錄需要存儲在數(shù)據(jù)庫中。為了方便查詢某個用戶的對話記錄,我在ConversationModel表中使用了user_id字段來標(biāo)識用戶。代碼邏輯很簡單:先查詢某個用戶的對話記錄總數(shù),然后再獲取具體的對話內(nèi)容。
count = ConversationModel.query.filter_by(user_id=recipient_uid).count()
print(f"符合條件的記錄總數(shù):{count}")
conversation_messages = ConversationModel.query.filter_by(user_id=recipient_uid).all()
print(f"查詢到的記錄數(shù):{len(conversation_messages)}")
運行代碼后,我發(fā)現(xiàn)count的值是9,但conversation_messages的長度卻是1。這讓我非常困惑:明明數(shù)據(jù)庫里有9條記錄,為什么查詢出來的結(jié)果只有1條呢?
排查過程
一開始,我以為是查詢條件寫錯了,反復(fù)檢查了代碼,確認(rèn)filter_by(user_id=recipient_uid)的條件沒有問題。接著,我懷疑是不是數(shù)據(jù)庫連接出了問題,導(dǎo)致查詢結(jié)果不一致,但檢查后發(fā)現(xiàn)數(shù)據(jù)庫連接也是正常的。
然后,我開始懷疑是不是數(shù)據(jù)庫事務(wù)隔離級別的問題,甚至去查了SQLAlchemy的文檔,看看是不是有什么隱藏的坑。但折騰了半天,依然沒有找到原因。
最后,我決定直接查看數(shù)據(jù)庫表結(jié)構(gòu),這才發(fā)現(xiàn)了問題的關(guān)鍵:我把user_id設(shè)置成了主鍵(Primary Key)。
為什么主鍵會導(dǎo)致這個問題?
在數(shù)據(jù)庫中,主鍵的唯一作用是唯一標(biāo)識一條記錄。也就是說,主鍵的值必須是唯一的,不能重復(fù)。而我錯誤地把user_id設(shè)置成了主鍵,這意味著每個user_id只能對應(yīng)一條記錄。
所以,當(dāng)我執(zhí)行查詢時:
conversation_messages = ConversationModel.query.filter_by(user_id=recipient_uid).all()
由于user_id是主鍵,數(shù)據(jù)庫只會返回唯一一條記錄,即使實際上有多條記錄符合條件。這就是為什么count是9,但查詢結(jié)果卻只有1條的原因。
問題的解決
找到原因后,解決方法就很簡單了:去掉user_id的主鍵約束。因為一個用戶可能會有多條對話記錄,所以user_id不應(yīng)該作為主鍵。正確的做法是使用一個獨立的字段(比如id)作為主鍵,而user_id只作為普通字段。
修改后的表結(jié)構(gòu)如下:
class ConversationModel(db.Model):
id = db.Column(db.String, primary_key=True, default=lambda: str(uuid.uuid4()))
user_id = db.Column(db.String) # 普通字段,不再作為主鍵
message = db.Column(db.String)
# 其他字段...
修改后,重新運行代碼,count和conversation_messages的結(jié)果終于一致了!
反思與總結(jié)
這次經(jīng)歷讓我深刻體會到,數(shù)據(jù)庫設(shè)計中的每一個細(xì)節(jié)都可能對功能產(chǎn)生重大影響。主鍵的設(shè)置看似簡單,但如果用錯了地方,就會引發(fā)意想不到的問題。以下是我總結(jié)的幾點經(jīng)驗:
- 主鍵的唯一性:主鍵的作用是唯一標(biāo)識一條記錄,不能重復(fù)。如果一個字段的值可能重復(fù)(比如user_id),就不適合作為主鍵。
- 數(shù)據(jù)庫設(shè)計要結(jié)合實際業(yè)務(wù):在設(shè)計數(shù)據(jù)庫時,一定要結(jié)合業(yè)務(wù)需求,確保表結(jié)構(gòu)和字段設(shè)置符合實際場景。
- 排查問題要全面:遇到問題時,不要只盯著代碼,還要檢查數(shù)據(jù)庫表結(jié)構(gòu)、數(shù)據(jù)狀態(tài)等可能的影響因素。
這次經(jīng)歷雖然讓我折騰了2個小時,啊啊??!但也讓我學(xué)到了很多。希望我的這段“踩坑”經(jīng)歷能對大家有所幫助。如果你也遇到過類似的問題,歡迎在評論區(qū)分享你的故事!畢竟,程序員的世界里,解決問題的方式千奇百怪,但最終的目標(biāo)都是一樣的:寫出更好的代碼,做出更好的產(chǎn)品!