2014-09-01 92 views
1

我試圖通過對它的一些調查來了解Neo4j對象緩存。對象緩存的第一印象來自此鏈接中的幻燈片: http://www.slideshare.net/thobe/an-overview-of-neo4j-internals瞭解Neo4j對象緩存

具體來說,緩存中的節點/關係對象應該與幻燈片9或15/42類似。爲了驗證這一點,我使用現有圖形數據庫內容編寫了一個簡單的服務器腳本我這樣做的方法是使用sun.misc.Unsafe來查看節點/關係對象的起始虛擬地址。獲得虛擬地址的程序是從以下鏈接: How can I get the memory location of a object in java?

public static long addressOf(Object o) throws Exception { 
    Object[] array = new Object[] { o }; 

    long baseOffset = unsafe.arrayBaseOffset(Object[].class); 
    int addressSize = unsafe.addressSize(); 
    long objectAddress; 
    switch (addressSize) { 
    case 4: 
     objectAddress = unsafe.getInt(array, baseOffset); 
     break; 
    case 8: 
     objectAddress = unsafe.getLong(array, baseOffset); 
     break; 
    default: 
     throw new Error("unsupported address size: " + addressSize); 
    } 
    return (objectAddress); 
} 

而在Neo4j的服務器腳本(我的main()類),我按id得到節點地址,並以下列方式打印出來的地址:

void checkAddr(){ 
    nodeAddr(0); 
    nodeAddr(1); 
    nodeAddr(2); 
} 

void nodeAddr(int n){ 
    Node oneNode = graphDb.getNodeById(n); 
    Node[] array1 = {oneNode}; 

    try { 
     long address = UnsafeUtil.addressOf(array1); 
     System.out.println("Addess: " + address); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

首先,我試着用軟緩存提供程序,這是默認情況。地址獲得打印出來用於節點對象0,1和2是:

Addess:4168500044個 Addess:4168502383個 Addess:4168502753

因此,使用第二地址 - 第一地址和第三地址 - 第二個地址,我可以確切知道節點正在佔用多少空間。在這種情況下,第一個節點對象需要2339B,第二個需要370B。

然後,看到禁用對象緩存的影響,我確實有NoCacheProvider設置:

調用setConfig(GraphDatabaseSettings.cache_type,NoCacheProvider.NAME)

的地址獲得打印出來的:

Addess:4168488391 Addess:4168490708個 Addess:4168491056

與第一種情況類似計算的偏移量是:第一個節點對象需要2317B,第二個需要348B。

這裏來我的問題:

  1. 由於我使用的是同一張圖,做只讀查詢,爲什麼同一個節點對象的大小變化?

  2. 當我禁用對象緩存時,爲什麼地址偏移量看起來像存在對象緩存一樣?例如,在節點存儲文件中,單個節點需要9個字節,這在我的實驗中不是這種情況。如果我得到節點對象的方式有問題,我如何以正確的方式獲得虛擬地址?有什麼辦法可以知道mmap節點文件在內存中的位置?

  3. 我怎麼能確切地知道存儲在節點對象中的內容。當我在此鏈接處查看Node.class時: https://github.com/neo4j/neo4j/blob/1.9.8/community/kernel/src/main/java/org/neo4j/graphdb/Node.java 看起來節點對象看起來應該與在演示文稿幻燈片中看起來一樣。而只是一組節點對象使用的函數。進一步是一個節點對象在no-object-cache和with-object-cache場合中同時作爲一個整體進入內存?

回答

2

Node對象不是什麼的Neo4j存儲在「對象緩存」,這樣你就不會通過查看這些實例獲得多少洞察的Neo4j的緩存。 Neo4j爲您提供的Node的實現是一個名爲NodeProxy的類的實例,並且它們儘可能小(兩個字段:內部標識和對數據庫的引用)。這些只是用於在數據庫中的節點周圍執行操作的節點句柄。存儲在「對象高速緩存」中的對象是一個名爲NodeImpl的類的實例(儘管它們的名稱沒有實現接口Node)。 NodeImpl對象的形狀在該演示文稿的第15張幻燈片(幻燈片中的第9頁)中概述。那麼,它大致有這樣的形狀,自從我製作這些幻燈片以來,Neo4j已經發展了。

Neo4j的演變也改變了節點記錄在磁盤上佔用的字節數。 Neo4j 2.0和更高版本的節點記錄比這些幻燈片所呈現的略大。如果您有興趣查看這些記錄的佈局,您應該查看NodeRecord類,然後從NodeStore類開始,「向下」進入其依賴關係以查找內存映射。

除了查看錯誤的對象以查看Neo4j中不同緩存方法之間的差異,您的測量方法存在缺陷。比較對象的地址不會告訴你任何關於這些對象的大小。 JVM並不保證依次分配的兩個對象(在時間上)將駐留在內存中,即使JVM確實使用了這種分配策略,Neo4j可能已經在兩個對象的分配之間分配了多個對象正在比較。然後是垃圾收集器,它可能會在您獲取一個對象的地址和獲取下一個對象的地址之間移動對象。因此,在Java中查看對象的地址對於任何事情來說都是非常有用的。爲了更好地測量Java中對象的大小,請查看Java Object Layout utility,或使用Java代理中的Instrumentation.getObjectSize(...) method

要回答你的問題是說:

  1. 節點對象的尺寸並沒有發生變化,它們的地址不能保證在運行之間是相同的。按照我上面的描述,你不能依靠對象地址來計算對象的大小。

  2. 既然您正在查看NodeProxy對象,無論Neo4j使用什麼緩存策略,它們看起來都是一樣的。爲了查看NodeImpl對象,您必須深入挖掘Neo4j的內部。由於看起來您使用的是Neo4j 1.9,因此您會將GraphDatabaseService實例強制轉換爲GraphDatabaseAPI(實現內部的接口),然後調用該對象的getNodeManager()方法。從NodeManager您可以撥打getNodeIfCached(node.getId())獲取NodeImpl對象。請注意,這個API不會在Neo4j版本之間兼容,並且使用它是那些「保修無效如果密封破損」的情況之一...

  3. 請看NodeImpl的源代碼。至於何時以及如何將數據帶入緩存,Neo4j會試圖對此進行懶惰,只加載您使用的數據。如果您正在獲取節點的關係,那麼這些關係將被加載到緩存中,並且如果您正在獲取屬性,那麼這些屬性將被加載到緩存中。如果你只有關係,屬性將永遠不會被加載,反之亦然。