c - SQLite के INSERT-प्रति-दूसरे प्रदर्शन में सुधार करें?




performance optimization (6)

थोक आवेषण पर

इस पोस्ट से और स्टैक ओवरफ़्लो प्रश्न से प्रेरित हुआ जिसने मुझे यहां ले जाया - क्या SQLite डेटाबेस में एक समय में कई पंक्तियां डालना संभव है? - मैंने अपना पहला Git भंडार पोस्ट किया है:

https://github.com/rdpoor/CreateOrUpdate

जो थोक MySQL , SQLite या PostgreSQL डेटाबेस में ActiveRecords की एक सरणी लोड करता है। इसमें मौजूदा रिकॉर्ड्स को अनदेखा करने, उन्हें ओवरराइट करने या त्रुटि बढ़ाने का विकल्प शामिल है। मेरे प्राथमिक बेंचमार्क अनुक्रमिक लिखने की तुलना में 10x गति सुधार दिखाते हैं - वाईएमएमवी।

मैं इसे उत्पादन कोड में उपयोग कर रहा हूं जहां मुझे अक्सर बड़े डेटासेट आयात करने की आवश्यकता होती है, और मैं इसके साथ बहुत खुश हूं।

SQLite अनुकूलित करना मुश्किल है। सी आवेदन के थोक-सम्मिलन प्रदर्शन प्रति सेकंड 85 आवेषण से प्रति सेकंड 96,000 आवेषण से भिन्न हो सकते हैं!

पृष्ठभूमि: हम डेस्कटॉप एप्लिकेशन के हिस्से के रूप में SQLite का उपयोग कर रहे हैं। हमारे पास XML फ़ाइलों में संग्रहीत कॉन्फ़िगरेशन डेटा की बड़ी मात्रा है जो अनुप्रयोग प्रारंभ होने पर आगे की प्रक्रिया के लिए एक SQLite डेटाबेस में पार्स और लोड किया जाता है। SQLite इस स्थिति के लिए आदर्श है क्योंकि यह तेज़ है, इसके लिए कोई विशेष कॉन्फ़िगरेशन की आवश्यकता नहीं है, और डेटाबेस को डिस्क पर एक फ़ाइल के रूप में संग्रहीत किया जाता है।

तर्क: शुरुआत में मैं जो प्रदर्शन देख रहा था उससे निराश था। यह पता चला है कि SQLite का प्रदर्शन महत्वपूर्ण रूप से भिन्न हो सकता है (थोक-आवेषण और चयन दोनों के लिए) डेटाबेस डेटाबेस कॉन्फ़िगर कैसे किया जाता है और आप API का उपयोग कैसे कर रहे हैं। यह पता लगाने के लिए एक मामूली मामला नहीं था कि सभी विकल्प और तकनीक क्या थीं, इसलिए मैंने सोचा कि विकी ओवरफ्लो पाठकों के साथ परिणामों को साझा करने के लिए इस समुदाय विकी प्रविष्टि को बनाने के लिए समझदारी है ताकि दूसरों को एक ही जांच की परेशानी बचा सके।

प्रयोग: सामान्य ज्ञान में प्रदर्शन युक्तियों के बारे में बात करने की बजाय (यानी "लेनदेन का उपयोग करें!" ), मैंने सोचा कि कुछ सी कोड लिखना और वास्तव में विभिन्न विकल्पों के प्रभाव को मापना सर्वोत्तम है। हम कुछ सरल डेटा से शुरू करने जा रहे हैं:

  • टोरंटो शहर के लिए पूर्ण पारगमन अनुसूची का एक 28 एमबी टैब-सीमांकित टेक्स्ट फ़ाइल (लगभग 865,000 रिकॉर्ड)
  • मेरी टेस्ट मशीन एक 3.60 गीगाहर्ट्ज़ पी 4 विंडोज एक्सपी चल रही है।
  • कोड को विजुअल सी ++ 2005 के साथ "पूर्ण अनुकूलन" (/ ऑक्स) के साथ "रिलीज" के रूप में संकलित किया गया है और फास्ट कोड (/ ओटी) का पक्ष लिया गया है।
  • मैं SQLite "Amalgamation" का उपयोग कर रहा हूं, सीधे मेरे परीक्षण अनुप्रयोग में संकलित। मेरे पास होने वाला SQLite संस्करण थोड़ा पुराना है (3.6.7), लेकिन मुझे संदेह है कि ये परिणाम नवीनतम रिलीज के लिए तुलनीय होंगे (यदि आप अन्यथा सोचते हैं तो कृपया एक टिप्पणी छोड़ दें)।

आइए कुछ कोड लिखें!

कोड: एक साधारण सी प्रोग्राम जो पाठ फ़ाइल लाइन-दर-रेखा को पढ़ता है, स्ट्रिंग को मानों में विभाजित करता है और फिर डेटा को SQLite डेटाबेस में सम्मिलित करेगा। कोड के इस "बेसलाइन" संस्करण में, डेटाबेस बनाया गया है, लेकिन हम वास्तव में डेटा सम्मिलित नहीं करेंगे:

/*************************************************************
    Baseline code to experiment with SQLite performance.

    Input data is a 28 MB TAB-delimited text file of the
    complete Toronto Transit System schedule/route info
    from http://www.toronto.ca/open/datasets/ttc-routes/

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

#define INPUTDATA "C:\\TTC_schedule_scheduleitem_10-27-2009.txt"
#define DATABASE "c:\\TTC_schedule_scheduleitem_10-27-2009.sqlite"
#define TABLE "CREATE TABLE IF NOT EXISTS TTC (id INTEGER PRIMARY KEY, Route_ID TEXT, Branch_Code TEXT, Version INTEGER, Stop INTEGER, Vehicle_Index INTEGER, Day Integer, Time TEXT)"
#define BUFFER_SIZE 256

int main(int argc, char **argv) {

    sqlite3 * db;
    sqlite3_stmt * stmt;
    char * sErrMsg = 0;
    char * tail = 0;
    int nRetCode;
    int n = 0;

    clock_t cStartClock;

    FILE * pFile;
    char sInputBuf [BUFFER_SIZE] = "\0";

    char * sRT = 0;  /* Route */
    char * sBR = 0;  /* Branch */
    char * sVR = 0;  /* Version */
    char * sST = 0;  /* Stop Number */
    char * sVI = 0;  /* Vehicle */
    char * sDT = 0;  /* Date */
    char * sTM = 0;  /* Time */

    char sSQL [BUFFER_SIZE] = "\0";

    /*********************************************/
    /* Open the Database and create the Schema */
    sqlite3_open(DATABASE, &db);
    sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);

    /*********************************************/
    /* Open input file and import into Database*/
    cStartClock = clock();

    pFile = fopen (INPUTDATA,"r");
    while (!feof(pFile)) {

        fgets (sInputBuf, BUFFER_SIZE, pFile);

        sRT = strtok (sInputBuf, "\t");     /* Get Route */
        sBR = strtok (NULL, "\t");            /* Get Branch */
        sVR = strtok (NULL, "\t");            /* Get Version */
        sST = strtok (NULL, "\t");            /* Get Stop Number */
        sVI = strtok (NULL, "\t");            /* Get Vehicle */
        sDT = strtok (NULL, "\t");            /* Get Date */
        sTM = strtok (NULL, "\t");            /* Get Time */

        /* ACTUAL INSERT WILL GO HERE */

        n++;
    }
    fclose (pFile);

    printf("Imported %d records in %4.2f seconds\n", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);

    sqlite3_close(db);
    return 0;
}

नियंत्रण"

कोड को चलाने के रूप में वास्तव में कोई डेटाबेस संचालन नहीं करता है, लेकिन यह हमें एक विचार देगा कि कच्चे सी फ़ाइल I / O और स्ट्रिंग प्रोसेसिंग ऑपरेशन कितनी तेजी से हैं।

0.94 सेकंड में 864 9 13 रिकॉर्ड आयात किए गए

महान! हम प्रति सेकंड 920,000 आवेषण कर सकते हैं, बशर्ते हम वास्तव में कोई सम्मिलित न करें :-)

"सबसे खराब केस-परिदृश्य"

हम फ़ाइल से पढ़ने वाले मानों का उपयोग करके SQL स्ट्रिंग जेनरेट करने जा रहे हैं और sqlite3_exec का उपयोग कर SQL ऑपरेशन का आह्वान करते हैं:

sprintf(sSQL, "INSERT INTO TTC VALUES (NULL, '%s', '%s', '%s', '%s', '%s', '%s', '%s')", sRT, sBR, sVR, sST, sVI, sDT, sTM);
sqlite3_exec(db, sSQL, NULL, NULL, &sErrMsg);

यह धीमा होने वाला है क्योंकि एसक्यूएल को प्रत्येक सम्मिलन के लिए वीडीबीई कोड में संकलित किया जाएगा और प्रत्येक सम्मिलन अपने लेन-देन में होगा। कितना धीमा

9933.61 सेकंड में 864 9 13 रिकॉर्ड आयात किए गए

ओह! 2 घंटे और 45 मिनट! यह प्रति सेकंड केवल 85 आवेषण है।

एक लेनदेन का उपयोग करना

डिफ़ॉल्ट रूप से, SQLite एक अद्वितीय लेनदेन के भीतर प्रत्येक INSERT / UPDATE कथन का मूल्यांकन करेगा। यदि बड़ी संख्या में आवेषण प्रदर्शन करते हैं, तो यह सलाह दी जाती है कि आप अपने ऑपरेशन को लेनदेन में लपेटें:

sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);

pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {

    ...

}
fclose (pFile);

sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);

38.03 सेकंड में 864 9 13 रिकॉर्ड आयात किए गए

वह बेहतर है। बस एक ही लेनदेन में हमारे सभी आवेषणों को लपेटने से हमारे प्रदर्शन में प्रति सेकंड 23,000 आवेषण में सुधार हुआ

एक तैयार वक्तव्य का उपयोग करना

एक लेनदेन का उपयोग करना एक बड़ा सुधार था, लेकिन प्रत्येक सम्मिलन के लिए एसक्यूएल कथन को पुन: संकलित करना समझ में नहीं आता है अगर हम एक ही एसक्यूएल ओवर-एंड-ओवर का उपयोग करते हैं। चलिए एक बार हमारे SQL कथन को संकलित करने के लिए sqlite3_prepare_v2 का उपयोग करें और फिर sqlite3_bind_text का उपयोग करके उस पैरामीटर को हमारे पैरामीटर को बाध्य करें:

/* Open input file and import into the database */
cStartClock = clock();

sprintf(sSQL, "INSERT INTO TTC VALUES (NULL, @RT, @BR, @VR, @ST, @VI, @DT, @TM)");
sqlite3_prepare_v2(db,  sSQL, BUFFER_SIZE, &stmt, &tail);

sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);

pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {

    fgets (sInputBuf, BUFFER_SIZE, pFile);

    sRT = strtok (sInputBuf, "\t");   /* Get Route */
    sBR = strtok (NULL, "\t");        /* Get Branch */
    sVR = strtok (NULL, "\t");        /* Get Version */
    sST = strtok (NULL, "\t");        /* Get Stop Number */
    sVI = strtok (NULL, "\t");        /* Get Vehicle */
    sDT = strtok (NULL, "\t");        /* Get Date */
    sTM = strtok (NULL, "\t");        /* Get Time */

    sqlite3_bind_text(stmt, 1, sRT, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, sBR, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 3, sVR, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 4, sST, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 5, sVI, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 6, sDT, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 7, sTM, -1, SQLITE_TRANSIENT);

    sqlite3_step(stmt);

    sqlite3_clear_bindings(stmt);
    sqlite3_reset(stmt);

    n++;
}
fclose (pFile);

sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);

printf("Imported %d records in %4.2f seconds\n", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);

sqlite3_finalize(stmt);
sqlite3_close(db);

return 0;

16.27 सेकंड में 864 9 13 रिकॉर्ड आयात किए गए

अच्छा! थोड़ा और कोड है ( sqlite3_clear_bindings और sqlite3_reset को कॉल करना न भूलें), लेकिन हमने प्रति सेकंड 53,000 इन्सर्ट पर हमारे प्रदर्शन को दोगुना कर दिया है

PRAGMA तुल्यकालिक = बंद

डिफ़ॉल्ट रूप से, SQLite ओएस-स्तरीय लेखन आदेश जारी करने के बाद रोक देगा। यह गारंटी देता है कि डेटा डिस्क पर लिखा गया है। synchronous = OFF सेट करके, हम एसक्यूएलआईटी को ओएस को डेटा को आसानी से लिखने के लिए निर्देश दे रहे हैं और फिर जारी रख सकते हैं। प्लेटर को डेटा लिखे जाने से पहले कंप्यूटर को एक विनाशकारी दुर्घटना (या पावर विफलता) पीड़ित होने पर डेटाबेस फ़ाइल दूषित हो सकती है:

/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg);

12.41 सेकेंड में 864 9 13 रिकॉर्ड आयात किए गए

सुधार अब छोटे हैं, लेकिन हम प्रति सेकेंड 69,600 प्रविष्टियां हैं।

PRAGMA journal_mode = स्मृति

PRAGMA journal_mode = MEMORY मूल्यांकन करके स्मृति में रोलबैक जर्नल को संग्रहीत करने पर विचार करें। आपका लेनदेन तेजी से होगा, लेकिन यदि आप लेनदेन के दौरान बिजली खो देते हैं या आपका प्रोग्राम दुर्घटनाग्रस्त हो जाता है तो आपको आंशिक रूप से पूर्ण लेनदेन वाले भ्रष्ट स्थिति में डेटाबेस छोड़ा जा सकता है:

/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA journal_mode = MEMORY", NULL, NULL, &sErrMsg);

13.50 सेकंड में 864 9 13 रिकॉर्ड आयात किए गए

प्रति सेकंड 64,000 आवेषण पर पिछले अनुकूलन की तुलना में थोड़ा धीमा

PRAGMA तुल्यकालिक = OFF और PRAGMA journal_mode = स्मृति

आइए पिछले दो अनुकूलन को गठबंधन करें। यह थोड़ा और जोखिम भरा है (एक दुर्घटना के मामले में), लेकिन हम सिर्फ डेटा आयात कर रहे हैं (बैंक नहीं चला रहे हैं):

/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA journal_mode = MEMORY", NULL, NULL, &sErrMsg);

12.00 सेकेंड में 864 9 13 रिकॉर्ड आयात किए गए

बहुत खुबस! हम प्रति सेकंड 72,000 आवेषण करने में सक्षम हैं

इन-मेमोरी डेटाबेस का उपयोग करना

बस किक्स के लिए, चलिए सभी पिछले अनुकूलन पर निर्माण करते हैं और डेटाबेस फ़ाइल नाम को फिर से परिभाषित करते हैं ताकि हम पूरी तरह से रैम में काम कर रहे हों:

#define DATABASE ":memory:"

10.9 4 सेकेंड में 864 9 13 रिकॉर्ड आयात किए गए

रैम में हमारे डेटाबेस को स्टोर करने के लिए यह सुपर-व्यावहारिक नहीं है, लेकिन यह प्रभावशाली है कि हम प्रति सेकेंड 79,000 आवेषण कर सकते हैं

सी कोड रिफैक्टरिंग

हालांकि विशेष रूप से एक SQLite सुधार नहीं है, मुझे अतिरिक्त लूप में अतिरिक्त char* असाइनमेंट ऑपरेशन पसंद नहीं है। आइए strtok() सीधे sqlite3_bind_text() में आउटपुट पास करने के लिए उस कोड को तुरंत strtok() , और संकलक को हमारे लिए चीजों को गति देने की कोशिश करें:

pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {

    fgets (sInputBuf, BUFFER_SIZE, pFile);

    sqlite3_bind_text(stmt, 1, strtok (sInputBuf, "\t"), -1, SQLITE_TRANSIENT); /* Get Route */
    sqlite3_bind_text(stmt, 2, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Branch */
    sqlite3_bind_text(stmt, 3, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Version */
    sqlite3_bind_text(stmt, 4, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Stop Number */
    sqlite3_bind_text(stmt, 5, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Vehicle */
    sqlite3_bind_text(stmt, 6, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Date */
    sqlite3_bind_text(stmt, 7, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Time */

    sqlite3_step(stmt);        /* Execute the SQL Statement */
    sqlite3_clear_bindings(stmt);    /* Clear bindings */
    sqlite3_reset(stmt);        /* Reset VDBE */

    n++;
}
fclose (pFile);

नोट: हम एक वास्तविक डेटाबेस फ़ाइल का उपयोग करने के लिए वापस आ गए हैं। इन-मेमोरी डेटाबेस तेजी से हैं, लेकिन आवश्यक रूप से व्यावहारिक नहीं हैं

8.9 4 सेकेंड में 864 9 13 रिकॉर्ड आयात किए गए

हमारे पैरामीटर बाइंडिंग में प्रयुक्त स्ट्रिंग प्रोसेसिंग कोड के लिए थोड़ा सा रिफैक्टरिंग हमें प्रति सेकंड 96,700 इन्सर्ट करने की अनुमति देता है मुझे लगता है कि यह कहना सुरक्षित है कि यह बहुत तेज़ है । जैसे-जैसे हम अन्य चर (यानी पृष्ठ का आकार, इंडेक्स सृजन इत्यादि) को ट्विक करना शुरू करते हैं, यह हमारा बेंचमार्क होगा।

सारांश (अब तक)

मुझे उम्मीद है कि तुम अभी भी मेरे साथ हो! इस सड़क को शुरू करने का कारण यह है कि थोक-सम्मिलन प्रदर्शन SQLite के साथ इतनी जंगली रूप से भिन्न होता है, और यह हमेशा स्पष्ट नहीं होता है कि हमारे ऑपरेशन को तेज़ी से बढ़ाने के लिए क्या बदलाव किए जाने की आवश्यकता है। एक ही कंपाइलर (और कंपाइलर विकल्प) का उपयोग करना, SQLite का वही संस्करण और उसी डेटा को हमने अपने कोड और SQLite के हमारे उपयोग को अनुकूलित किया है ताकि प्रति सेकंड 85 आवेषण से प्रति सेकंड 85 आवेषण के सबसे खराब मामले परिदृश्य से जा सके!

INDEX बनाओ फिर INSERT बनाम INSERT फिर INDEX बनाएं

SELECT को मापने से पहले, हम जानते हैं कि हम इंडेक्स बनायेंगे। नीचे दिए गए उत्तरों में से एक में सुझाव दिया गया है कि थोक आवेषण करते समय, डेटा डालने के बाद इंडेक्स बनाने के लिए तेज़ होता है (जैसा कि पहले इंडेक्स बनाने के बाद डेटा डालने का विरोध किया जाता है)। कोशिश करते हैं:

इंडेक्स बनाएं और फिर डेटा डालें

sqlite3_exec(db, "CREATE  INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
...

18.13 सेकंड में 864 9 13 रिकॉर्ड आयात किए गए

डेटा डालें और फिर इंडेक्स बनाएं

...
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "CREATE  INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);

13.66 सेकंड में 864 9 13 रिकॉर्ड आयात किए गए

जैसा कि अपेक्षित है, यदि एक कॉलम अनुक्रमित किया जाता है तो थोक-आवेषण धीमे होते हैं, लेकिन यदि डेटा डालने के बाद इंडेक्स बनाया जाता है तो इससे कोई फर्क पड़ता है। हमारी नो-इंडेक्स बेसलाइन प्रति सेकेंड 96,000 आवेषण है। पहले इंडेक्स बनाना, डेटा डालने से हमें प्रति सेकंड 47,700 इन्सर्ट मिलते हैं, जबकि पहले डेटा डालने से इंडेक्स बनाने से हमें प्रति सेकंड 63,300 आवेषण मिलते हैं।

मैं खुशहाली कोशिश करने के लिए अन्य परिदृश्यों के लिए सुझाव लेता हूं ... और जल्द ही चयन प्रश्नों के लिए समान डेटा संकलित करेगा।


आपके प्रश्न का उत्तर यह है कि नए sqlite3 ने प्रदर्शन में सुधार किया है, इसका उपयोग करें।

यह उत्तर Sqlitechemy sqlite3 का उपयोग करने से 25 गुना धीमी गति से क्यों है? SqlAlchemy Orm लेखक द्वारा 0.5 सेकंड में 100k आवेषण हैं, और मैंने पाइथन-स्क्लाइट और स्क्लक्लेमी के साथ समान परिणाम देखे हैं। जो मुझे विश्वास दिलाता है कि प्रदर्शन sqlite3 के साथ सुधार हुआ है


उन प्रविष्टियों के लिए SQLITE_TRANSIENT बजाय SQLITE_STATIC का उपयोग करने का प्रयास करें।

SQLITE_TRANSIENT SQLite लौटने से पहले स्ट्रिंग डेटा की प्रतिलिपि बनाने का कारण बन जाएगा।

SQLITE_STATIC यह बताता है कि आपके द्वारा दिया गया स्मृति पता तब तक मान्य होगा जब तक क्वेरी निष्पादित नहीं की जाती है (जो इस लूप में हमेशा मामला है)। यह आपको कई लूप ऑपरेशन आवंटित, कॉपी और डिलीकेट करने से बचाएगा। संभवतः एक बड़ा सुधार।


कई सुझाव:

  1. एक लेनदेन में आवेषण / अद्यतन रखो।
  2. SQLite के पुराने संस्करणों के लिए - एक कम परावर्तक जर्नल मोड ( pragma journal_mode ) पर विचार करें। NORMAL , और फिर OFF , जो ओएस दुर्घटनाग्रस्त होने पर डेटाबेस के बारे में बहुत चिंतित नहीं होने पर संभवतः सम्मिलित गति को बढ़ा सकता है। यदि आपका एप्लिकेशन क्रैश हो जाता है तो डेटा ठीक होना चाहिए। ध्यान दें कि नए संस्करणों में, OFF/MEMORY सेटिंग्स एप्लिकेशन स्तर क्रैश के लिए सुरक्षित नहीं हैं।
  3. पेज आकार के साथ खेलना भी एक फर्क पड़ता है ( PRAGMA page_size )। बड़े पेज आकार होने से पढ़ने और लिखने में थोड़ा तेज़ हो जाता है क्योंकि बड़े पेज मेमोरी में होते हैं। ध्यान दें कि आपके डेटाबेस के लिए अधिक मेमोरी का उपयोग किया जाएगा।
  4. यदि आपके पास इंडेक्स हैं, तो अपने सभी आवेषण करने के बाद CREATE INDEX कॉल करने पर विचार करें। यह सूचकांक बनाने और फिर अपने आवेषण करने से काफी तेज है।
  5. यदि आपके पास SQLite तक समवर्ती पहुंच है, तो आपको सावधान रहना होगा, क्योंकि लिखने के दौरान पूरा डेटाबेस लॉक हो जाता है, और हालांकि कई पाठक संभव हैं, लिखना बंद कर दिया जाएगा। यह नए SQLite संस्करणों में एक वाल के अतिरिक्त के साथ कुछ हद तक सुधार हुआ है।
  6. बचत स्थान का लाभ उठाएं ... छोटे डेटाबेस तेजी से जाते हैं। उदाहरण के लिए, यदि आपके पास महत्वपूर्ण मान जोड़े हैं, तो संभव है कि कुंजी को एक संभावित INTEGER PRIMARY KEY कुंजी बनाने का प्रयास करें, जो तालिका में अंतर्निहित अद्वितीय पंक्ति संख्या कॉलम को प्रतिस्थापित करेगा।
  7. यदि आप एकाधिक थ्रेड का उपयोग कर रहे हैं, तो आप साझा पृष्ठ कैश का उपयोग करने का प्रयास कर सकते हैं, जो लोड किए गए पृष्ठों को धागे के बीच साझा करने की अनुमति देगा, जो महंगा I / O कॉल से बच सकते हैं।
  8. उपयोग न करें !feof(file) !

मैंने here और here भी इसी तरह के प्रश्न पूछे here


यदि आप अपने INSERT / UPDATE स्टेटमेंट को रोक सकते हैं तो थोक आयात सबसे अच्छा प्रदर्शन करते हैं। 10,000 या उससे अधिक के मूल्य ने टेबल पर केवल कुछ पंक्तियों के साथ काम किया है, वाईएमएमवी ...


यदि आप केवल पढ़ने के बारे में सोचते हैं, कुछ हद तक तेज़ (लेकिन पुराना डेटा पढ़ सकते हैं) संस्करण एकाधिक थ्रेड (कनेक्शन प्रति-थ्रेड) से कई कनेक्शनों से पढ़ना है।

सबसे पहले टेबल में आइटम ढूंढें:

 SELECT COUNT(*) FROM table

फिर पृष्ठों में पढ़ें (LIMIT / OFFSET)

  SELECT * FROM table ORDER BY _ROWID_ LIMIT <limit> OFFSET <offset>

प्रति-धागे की गणना और गणना की जाती है, जैसे:

int limit = (count + n_threads - 1)/n_threads;

प्रत्येक धागे के लिए:

int offset = thread_index * limit

हमारे छोटे (200 एमबी) डीबी के लिए इसने 50-75% स्पीड-अप बनाया (विंडोज 7 पर 3.8.0.2 64-बिट)। हमारी टेबल भारी गैर-सामान्यीकृत (1000-1500 कॉलम, लगभग 100,000 या अधिक पंक्तियां) हैं।

बहुत सारे या बहुत छोटे धागे ऐसा नहीं करेंगे, आपको खुद को बेंचमार्क और प्रोफाइल करने की आवश्यकता है।

हमारे लिए, शेरडेक ने प्रदर्शन को धीमा कर दिया, इसलिए मैंने मैन्युअल रूप से प्राइवेटाच रखा (क्योंकि यह हमारे लिए वैश्विक रूप से सक्षम था)





optimization