SQLite R*Tree 模塊測試

SQLite R*Tree 模塊測試

相關參考:node

MySQL空間索引簡單使用git

MongoDB地理空間數據存儲及檢索github

The SQLite R*Tree Modulesql

Memory-Mapped I/Oapp

In-Memory Databases性能

libspatialindex測試

R* tree - Wikipedia網站

我另外作了GEOS STRtree/Quadtree 空間檢索的性能,測試代碼和數據可見Spatial_Index_Testspa

一、SQLite R*Tree 模塊特性簡介

關於SQLite的空間索引相關介紹能夠查看官方文檔 The SQLite R*Tree Module ,這裏只作簡單的介紹。

一、SQLite R *Tree模塊實現部分在其源代碼內(源碼下載頁面),無需另外合併。可是默認是沒有啓用的,啓用須要定義SQLITE_ENABLE_RTREE=1宏再編譯。

二、SQLite R *Tree模塊採用虛擬表實現,每一個R *Tree索引都是一個虛擬表。對於這個表,其第一列必須是64位有符號整數類型,做爲主鍵。其它的列(2-12列)根據空間維度肯定,每一個維度包含一對(兩列),分別是該維度的最小和最大值。例如:一維R *Tree索引虛擬表包含3列,分別是Int64主鍵| 最小值| 最大值;二維R*Tree索引虛擬表包含5列,分別是Int64主鍵| 第一維最小值| 第一維最大值| 第二維最小值| 第二維最大值;三、四、5維R*Tree索引虛擬表列數狀況的以此論推,SQLite R *Tree實現不支持寬度超過5維的R *樹。

三、對於各個維度的最大最小值列,SQLite中可使用int32或者float32類型進行數據存儲。與其它常規表中的列不一樣,這裏存儲就是二進制類型的值,而不是轉換爲字符串。若是在插入數據的時候,使用了這二者以外的類型,則會進行隱式轉換。

-- 建立整型座標rtree索引虛擬表
    CREATE VIRTUAL TABLE intrtree USING rtree_i32(id,x0,x1,y0,y1,z0,z1);
    -- 建立浮點型座標rtree索引虛擬表
    CREATE VIRTUAL TABLE floatrtree USING rtree(id,x0,x1,y0,y1,z0,z1);

四、SQLite R *Tree中查詢並不限制查詢的維度必定要與所查詢的表中的維度一致,能夠僅查詢其中的某幾個維度(如3維空間僅查詢2個維度)。通常來講,約束(維度)越多,查詢的範圍框越小,速度越快。

五、默認狀況下使用float32存儲座標值,當沒法精確表示傳入值時,下限座標向下舍入,上限座標向上舍入,所以邊界框可能略大於指定,但永遠不會變小。這在查詢某個範圍以外的數據時,可能會有極小的偏差。

六、對於3.24.0以前的版本,SQLite R *Tree索引虛擬表僅能存儲整數主鍵和座標值列,其它的信息須要另存於其它表中(經過主鍵進行關聯)。從3.24.0版本開始,SQLite R *Tree索引虛擬表能夠存儲任意類型數據的輔助列,輔助列必須以+開頭,最多能夠存儲100個輔助列。

CREATE VIRTUAL TABLE demo_index2 USING rtree(
       id,              -- 64位整型主鍵
       minX, maxX,      -- X方向最小最大值
       minY, maxY,      -- Y方向最小最大值
       +objname TEXT,   -- 輔助列 文本類型
       +objtype TEXT,   -- 輔助列 文本類型
       +boundary BLOB   -- 輔助列 二進制數據
    );

七、能夠自定義R-Tree查詢,以便實現非矩形框碰撞。這須要經過sqlite3_rtree_query_callback(新,3.8.5開始提供)或sqlite3_rtree_geometry_callback(舊)註冊查詢SQL語句和匹配檢測回調。相關信息在SQLite網站上有詳細介紹。

八、一個SQLite R *Tree會附帶三個影子表,用於存儲數據,分別是虛擬表名_node(存儲節點) 虛擬表名_parent(存儲父節點) 虛擬表名_rowid(存儲節點的rowid)。

九、可使用SELECT rtreecheck('虛擬表名')來對R-Tree索引進行完整性和正確性檢查。

二、SQLite R*Tree 模塊簡單測試代碼

寫了一個簡單的測試程序來測試一下**R *Tree**樹的速度,結果仍是能夠的。(可用,並非最佳)

個人機器環境是:

Windows 10 1903 x64專業版

AMD 銳龍 2600X

DDR4 2400 8G

編譯器:VS2017 Native x64

使用本地文件的時候,十萬條數據插入時間大概在2秒之內,查詢一個5x5度大小的範圍,時間基本在0.07秒之內;使用內存模式時,插入時間大概在1.8秒之內,查詢一個5x5度大小的範圍,時間基本在0.04秒之內。

注意:編譯SQLite的時候要定義SQLITE_ENABLE_RTREE宏,開啓RTree索引支持。

#include "sqlite/sqlite3.h"
#include<time.h>
#include <stdlib.h>
#include <stdio.h>

// 由於僅僅是進行一下試用測試,因此有些地方就沒有處理,包括close

int main() 
{
    sqlite3* db = NULL;
    int rc = sqlite3_open(":memory:", &db);
    // int rc = sqlite3_open("D:/sqlite_rtree/test.db", &db);
    if (rc != SQLITE_OK) 
    {
        return -1;
    }
    
    char* errmsg;
    // 建立RTree索引虛擬表
    rc = sqlite3_exec(db,
                      "CREATE VIRTUAL TABLE demo_index USING rtree(id,minX, maxX,minY, maxY,+axucol INTEGER NOT NULL)",
                      NULL, NULL, &errmsg);
    if (rc != SQLITE_OK) 
    {
        printf("%4d Error:%sn", __LINE__, errmsg);
        return -2;
    }
    
    // 開始計時
    clock_t start = clock();
    
    // 開啓事物
    if (sqlite3_exec(db, "begin", NULL, NULL, &errmsg) != SQLITE_OK) {
        printf("%4d Error:%sn", __LINE__, errmsg);
        return -2;
    }
    
    // 生成十萬個大小在 邊長在[0.002,0.202]度大小之內的數據(0.2~22.5千米左右)
    srand(time(NULL));  // 初始化隨機數種子
    sqlite3_stmt *pStmt = NULL;
    
    // 預處理SQL語句
    if(sqlite3_prepare_v2(db,
                          "INSERT INTO demo_index VALUES(?,?,?,?,?,?)",
                          -1, &pStmt, NULL) != SQLITE_OK) {
        printf("%4d Error:%sn", __LINE__, errmsg);
        return -3;
    }
    // 逐個插入
    for (int i = 0; i < 100000; ++i) {
        // 生成在經緯度範圍內的x,y
        double x0 = ((double)rand() / (double)RAND_MAX) * 360 - 180;
        double y0 = ((double)rand() / (double)RAND_MAX) * 180 - 90;
        double x1 = x0 + 0.002 + ((double)rand() / (double)RAND_MAX)*0.2;
        double y1 = y0 + 0.002 + ((double)rand() / (double)RAND_MAX)*0.2;
        // 綁定數據
        sqlite3_bind_int64(pStmt, 1, i);
        sqlite3_bind_double(pStmt, 2, x0);
        sqlite3_bind_double(pStmt, 3, x1);
        sqlite3_bind_double(pStmt, 4, y0);
        sqlite3_bind_double(pStmt, 5, y1);
        sqlite3_bind_int(pStmt, 6, rand()%3);
        // 執行
        sqlite3_step(pStmt);
        // 重置
        sqlite3_reset(pStmt);
    }
    sqlite3_finalize(pStmt); //結束語句,釋放語句句柄
    
    // 結束事物
    if (sqlite3_exec(db, "commit", NULL, NULL, &errmsg) != SQLITE_OK){
        printf("%4d Error:%sn", __LINE__, errmsg);
        return -2;
    }
    
    
    // 結束計時
    clock_t end = clock();
    double hs = (double)(end - start) * 1000 / CLOCKS_PER_SEC;
    printf("插入總耗時: %lf msn", hs);
    // 查詢
    // select * from test where NOT(maxX<74.254915 OR minX>79.765758 OR maxY< 24.214285 OR minY>29.725129) AND auxcol==2 ORDER BY id;
    // 預處理SQL語句
    pStmt = NULL;
    if (sqlite3_prepare_v2(db,
            "SELECT id,minX,minY,auxcol FROM demo_index WHERE NOT(maxX<? OR minX>? OR maxY<?  OR minY>?) AND auxcol==1;",
            -1, &pStmt, NULL) != SQLITE_OK) {
        printf("%4d Error:%sn", __LINE__, errmsg);
        return -4;
    }
    
    //-------------------------------------------------------------------------
    
    // 輸入查詢的範圍框數據
    puts("Input x0,x1,y0,y1:");
    double x0, x1, y0, y1;
    scanf("%lf,%lf,%lf,%lf", &x0, &x1, &y0, &y1);
    printf("-----------[%lf,%lf,%lf,%lf]-------------n", x0, x1, y0, y1);
    
    // 開始計時
    start = clock();
    
    // 綁定查詢範圍數據
    sqlite3_bind_double(pStmt, 1, x0);
    sqlite3_bind_double(pStmt, 2, x1);
    sqlite3_bind_double(pStmt, 3, y0);
    sqlite3_bind_double(pStmt, 4, y1);
    while (sqlite3_step(pStmt) == SQLITE_ROW) {
        int id = sqlite3_column_int(pStmt, 0);
        double x = sqlite3_column_double(pStmt, 1);
        double y = sqlite3_column_double(pStmt, 2);
        int auxcol = sqlite3_column_int(pStmt, 3);
        // 能夠把輸出去掉,減小對時間統計的影響
        printf("%dt %lf,%lfn", id, x, y);
    }
    sqlite3_reset(pStmt); // 這裏只查詢一次能夠沒有,若是須要屢次使用這個查詢語句,則必須有,否則查出數據不對
    sqlite3_finalize(pStmt); //結束語句,釋放語句句柄
    
    // 結束計時
    end = clock();
    hs = (double)(end - start) * 1000 / CLOCKS_PER_SEC;
    printf("本次查詢總耗時: %lf msn", hs);
    
    
    sqlite3_close(db);
    system("pause");
    return 0;
}
相關文章
相關標籤/搜索