Heh everyone! Can anyone explain to me why SQLite shows such poor query performance when the database is located on an NFS-mounted file system? When running with a single process accessing the database, I'm finding that my insertion rates are similar to local disk, but my query performance is much, much, slower.
To illustrate, I built my test code (source at the end of this post) as: g++ -o sqlite-test sqlite-test.cxx -lsqlite3 -lrt using GCC 3.3.3. My Dell test system had a 3.2Ghz P4, 1Gb of RAM, and a 80 ATA HD (2Mb cache). I'm running SuSE 9.1 on this system. I ran each test three times: /tmp 36749.3, 36662.5, 36239.8 Insertions/Second /tmp 264.195, 268.678, 266.233 Queries/Second ~ 36515.5, 36538.9, 36673.6 Insertions/Second ~ 274.449, 273.875, 273.236 Queries/Second NFS(1) 37128.2, 37479, 37347.9 Insertions/Second NFS(1) 44.2276, 45.408, 52.3626 Queries/Second NFS(2) 36812.2, 37272.3, 36595.2 Insertions/Second NFS(2) 68.3878, 68.3142, 68.0701 Queries/Second /tmp: Local "tmpfs" file system ~: Local "reiserfs" file system NFS(1): Served from Origin 2000 16p system, RAID-5 SCSI disk array, fairly heavy user load NFS(2): Served from lowly PIII Linux system, local ATA disk, no user load Note that insertion performance is basically the same across all configurations, but the query performance is seriously degraded when running on NFS. In practice, on our real application, I'm seeing as much as a 50x speedup when moving the database from NFS onto the local disk. Colleagues working on the same project have all seen similar results. Does anyone have an idea why this is the case? Is there anything I can do to improve the query performance on NFS-mounted databases? Since we are using SQLite for storing our application's "save files", our users are going to want to be able to locate the database in a directory of their choosing. Also note that I'm not complaining about SQLite's query performance in general. The absolute numbers in this test case are artifically low because I'm forcing a full table scan by querying on a non-indexed field. My concern is with the relative performance between local versus NFS file systems. Thanks guys! -- William Hachfeld #include <assert.h> #include <inttypes.h> #include <iostream> #include <sqlite3.h> #include <stdio.h> #include <stdlib.h> #include <string> #include <time.h> #include <unistd.h> uint64_t Now() { struct timespec now; assert(clock_gettime(CLOCK_REALTIME, &now) == 0); return (static_cast<uint64_t>(now.tv_sec) * 1000000000) + static_cast<uint64_t>(now.tv_nsec); } int main(int argc, char* argv[]) { const int N = 10000; // Form and display the name of the database std::string dbname = std::string(get_current_dir_name()) + "/tmp.db"; std::cout << "Database \"" << dbname << "\"" << std::endl; // Open the database sqlite3* handle = NULL; assert(sqlite3_open(dbname.c_str(), &handle) == SQLITE_OK); assert(handle != NULL); // Create and populate a table with 'N' entries assert(sqlite3_exec(handle, "BEGIN TRANSACTION;", NULL, NULL, NULL) == SQLITE_OK); assert(sqlite3_exec(handle, "CREATE TABLE Test (" " key INTEGER PRIMARY KEY," " value INTEGER" ");", NULL, NULL, NULL) == SQLITE_OK); uint64_t t_start = Now(); for(int i = 0; i < N; ++i) { char* statement = sqlite3_mprintf("INSERT INTO Test (value) VALUES (%ld);", i); assert(statement != NULL); assert(sqlite3_exec(handle, statement, NULL, NULL, NULL) == SQLITE_OK); sqlite3_free(statement); } uint64_t t_stop = Now(); assert(sqlite3_exec(handle, "COMMIT TRANSACTION;", NULL, NULL, NULL) == SQLITE_OK); std::cout << (static_cast<double>(N) / (static_cast<double>(t_stop - t_start) / 1000000000.0)) << " Insertions/Second" << std::endl; // Perform 'N' queries on the table t_start = Now(); for(int i = 0; i < N; ++i) { char* statement = sqlite3_mprintf("SELECT * FROM Test WHERE value = %ld;", i); assert(statement != NULL); assert(sqlite3_exec(handle, statement, NULL, NULL, NULL) == SQLITE_OK); sqlite3_free(statement); } t_stop = Now(); std::cout << (static_cast<double>(N) / (static_cast<double>(t_stop - t_start) / 1000000000.0)) << " Queries/Second" << std::endl << std::endl; // Close the database assert(sqlite3_close(handle) == SQLITE_OK); // Remove the database assert(remove(dbname.c_str()) == 0); }