10個月前,我開始用neo4j做cmdb. 初體驗下去neo4j很美好. 但是一年中發(fā)現(xiàn)一些問題, 僅僅是個人的體驗.經(jīng)供參考
查詢語言
如果接觸過Neo4j,都會為Cypher的簡單和易用感覺到驚嘆, 向其他數(shù)據(jù)庫一樣,Neo4j也提供了Explain
和Profile
工具.這樣讓你容易理解查詢是如何運作的.如果你看過neo4j的查詢計劃優(yōu)化的圖,你會發(fā)現(xiàn)一大堆牽扯的線. 最開始簡單的查詢,一切都很美好.當(dāng)業(yè)務(wù)需求越來雜,你用到的查詢越來越復(fù)雜,你就發(fā)現(xiàn),天啊. 為什么我改了這么一小段代碼的順序,查詢慢了這么多. 所以官方也推薦你用多個小查詢替代一個大查詢. 所以你要遷移部分計算任務(wù)到你的代碼層,而不是通過Cypher再數(shù)據(jù)庫中幫你處理.
查詢的執(zhí)行過程
查詢監(jiān)視
你有沒有過這種情況, 本來你想查100條數(shù)據(jù),但是你寫錯了,你寫成查1M條數(shù)據(jù). 正常情況下,一般數(shù)據(jù)庫提供query watcher
會阻止你這個查詢進行下去,因為這太耗內(nèi)存,會讓數(shù)據(jù)庫都崩了. Neo4j里面也有這個東西,但是好像作用不穩(wěn)定.最好的結(jié)果是你等待了很久得到了一個undefined--undefined
. 更差的是數(shù)據(jù)庫崩了,再差一點,你的服務(wù)崩了.
讀優(yōu)先
Neo4j有一個設(shè)定,就是任何對Node的操作,都要先把該node的值讀出來. 在關(guān)系型數(shù)據(jù)庫中,你要刪除一個表,是不需要讀取每一列的數(shù)據(jù)的.而在neo4j里面,你要這樣做. 所以如果你想要根據(jù)label比如Match (n:label) delete n
.那你就要小心了.如果你這個label下有1M的node.那這個任務(wù)是無法完成的,因為你的機器RAM不夠大.會讓JVM GC-FULL. 所以同樣,你必須在代碼層做一些分塊刪除處理. 又增加了你的編碼難度.
鎖
Neo4j的鎖行為和關(guān)系型數(shù)據(jù)庫不大一樣.關(guān)系型數(shù)據(jù)中你做了一個更新操作,那么根據(jù)你是樂觀/悲觀. 或者一些策略,數(shù)據(jù)庫的查詢執(zhí)行器會按照固定的套路進行查詢. 在Neo4j中.只有一個寫鎖.但這個寫鎖是在真的set
語句開始執(zhí)行的時候才加上去的. 比如match (n:label {id:1}) set n.param=2
.這句話中只有match執(zhí)行完了之后才會加上寫鎖. 這在并發(fā)更新的時候就會出現(xiàn)問題. 這在neo4j里面被討論的很多,很多人也提出了方案
連接
neo4j沒有連接池.而且也沒有參數(shù)限制數(shù)據(jù)庫連接數(shù).很多時候這個問題會導(dǎo)致很多不穩(wěn)定的情況發(fā)生.
擴展
因為用的是Neo4j3.3的版本.沒有全文檢索的功能.而需求又必須支持比較強大的查詢. 所以我需要一個搜索引擎. 我用了ES.但是我并不想在代碼層去做neo4j和ES的數(shù)據(jù)同步. 這樣又讓我的代碼復(fù)雜了. 所以我決定用neo4j的插件去做這件事情. neo4j沒有像數(shù)據(jù)庫trigger這樣的東西.但是可以用transacitonEventhanlder
來實現(xiàn). 這就有一些問題.
- 缺乏資料, 可能因為我是python程序員的緣故,我對java和kotlin一知半解.
- 事件并不是真的像他說的那樣運轉(zhuǎn).比如
beforeCommit
.這個事件應(yīng)該發(fā)生在數(shù)據(jù)庫未發(fā)生變化的時候,這個時候你應(yīng)該是可以訪問被刪除的node.但是不是,你訪問不了.雖然該事務(wù)未提交. 幸好graphware
提供了一系列的插件來支持這些事情.
總結(jié)
我沒有說Neo4j不能用于生產(chǎn)了,畢竟在它的官網(wǎng)那么多大公司都用了. 但是它至少還有待完善. 經(jīng)過使用了半年之后.我就決定將數(shù)據(jù)從neo4j遷出. 只在neo4j中保留全部數(shù)據(jù)集的子集.用來做一些必要的圖搜索算法.