এসকিউএলাইটের প্রতি সেকেন্ডের INSERT- উন্নত করুন


2974

এসকিউএলাইট অপ্টিমাইজ করা জটিল। একটি সি অ্যাপ্লিকেশনের বাল্ক-সন্নিবেশ সম্পাদনা প্রতি সেকেন্ডে 85 টি সন্নিবেশ থেকে প্রতি সেকেন্ডে 96,000 এরও বেশি পরিবর্তিত হতে পারে!

পটভূমি: আমরা ডেস্কটপ অ্যাপ্লিকেশনটির অংশ হিসাবে এসকিউএলাইট ব্যবহার করছি। আমাদের কাছে এক্সএমএল ফাইলগুলিতে প্রচুর পরিমাণে কনফিগারেশন ডেটা সঞ্চিত রয়েছে যা অ্যাপ্লিকেশনটি আরম্ভ করার সময় আরও প্রক্রিয়াকরণের জন্য একটি এসকিউএল ডাটাবেসে পার্স করা এবং লোড করা হয়। এসকিউএলাইট এই পরিস্থিতির জন্য আদর্শ কারণ এটি দ্রুত, এটির জন্য বিশেষায়িত কনফিগারেশন প্রয়োজন নেই এবং ডাটাবেসটি একক ফাইল হিসাবে ডিস্কে সঞ্চিত থাকে।

যুক্তি: প্রাথমিকভাবে আমি যে পারফরম্যান্সটি দেখছিলাম তাতে হতাশ হয়েছি। এটি দেখা গেছে যে ডাটাবেসটি কীভাবে কনফিগার করা হয়েছে এবং আপনি কীভাবে API ব্যবহার করছেন তার উপর নির্ভর করে এসকিউএলাইটের পারফরম্যান্স উল্লেখযোগ্যভাবে পরিবর্তিত হতে পারে (বাল্ক-সন্নিবেশকারী এবং নির্বাচিত উভয়ের জন্য)। বিকল্পগুলি এবং কৌশলগুলির মধ্যে কী কী তা নির্ধারণ করা কোনও তুচ্ছ বিষয় ছিল না, তাই আমি অন্যদের একই তদন্তের ঝামেলা বাঁচাতে স্ট্যাক ওভারফ্লো পাঠকদের সাথে ফলাফলগুলি ভাগ করে নেওয়ার জন্য এই সম্প্রদায়টির উইকি এন্ট্রি তৈরি করা বুদ্ধিমানের বলে মনে করি।

পরীক্ষা: সাধারণ অর্থে পারফরম্যান্স টিপসের কথা বলার চেয়ে (যেমন "একটি লেনদেন ব্যবহার করুন!" ), আমি কিছু সি কোড লিখতে এবং বিভিন্ন বিকল্পের প্রভাবটি পরিমাপ করা ভাল বলে মনে করি । আমরা কিছু সাধারণ ডেটা দিয়ে শুরু করতে যাচ্ছি:

  • টরন্টো শহরের পুরো ট্রানজিট শিডিয়ুলের একটি 28 এমবি ট্যাব-সীমাবদ্ধ পাঠ্য ফাইল (আনুমানিক 865,000 রেকর্ডস)
  • আমার পরীক্ষা মেশিনটি একটি 3.60 গিগাহার্টজ পি 4 চলমান উইন্ডোজ এক্সপি।
  • কোডটি ভিজ্যুয়াল সি ++ 2005 এর সাথে "সম্পূর্ণ অপ্টিমাইজেশন" (/ অক্স) এবং ফেভারিট ফাস্ট কোড (/ ওটি) সহ "রিলিজ" হিসাবে সংকলিত হয়েছে ।
  • আমি সরাসরি আমার পরীক্ষার অ্যাপ্লিকেশনটিতে সংকলিত এসকিউএলাইট "সংহতি" ব্যবহার করছি। আমার কাছে এসকিউএলাইট সংস্করণটি কিছুটা পুরানো (৩.6..7), তবে আমি সন্দেহ করি যে এই ফলাফলগুলি সর্বশেষ প্রকাশের সাথে তুলনাযোগ্য হবে (যদি আপনি অন্যথায় মনে করেন তবে একটি মন্তব্য দিন)।

আসুন কিছু কোড লিখি!

কোড: একটি সাধারণ সি প্রোগ্রাম যা পাঠ্য ফাইলটি লাইন বাই লাইন পড়ে, স্ট্রিংকে মানগুলিতে বিভক্ত করে এবং তারপরে একটি এসকিউএল ডাটাবেসে ডেটা সন্নিবেশ করে। কোডটির এই "বেসলাইন" সংস্করণে ডাটাবেস তৈরি করা হয়েছে, তবে আমরা আসলে ডেটা won'tোকাতে চাই না:

/*************************************************************
    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 এবং স্ট্রিং প্রসেসিং অপারেশনগুলি কত দ্রুত fast

0.94 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

গ্রেট! আমরা প্রতি সেকেন্ডে 920,000 সন্নিবেশ করতে পারি, তবে শর্ত থাকে যে আমরা আসলে কোনও প্রবেশদ্বার না করি :-)


"সবচেয়ে খারাপ কেস-সিনারিও"

আমরা ফাইল থেকে পঠিত মানগুলি ব্যবহার করে এসকিউএল স্ট্রিং তৈরি করতে যাচ্ছি এবং এসকিউএল অপারেশনটি sqlite3_exec ব্যবহার করে অনুরোধ করব:

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 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

বাবা! 2 ঘন্টা 45 মিনিট! এটি প্রতি সেকেন্ডে কেবল 85 টি প্রবেশদ্বার।

একটি লেনদেন ব্যবহার করে

ডিফল্টরূপে, এসকিউএলাইট একটি অনন্য লেনদেনের মধ্যে প্রতিটি 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 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

এটা তুলনামূলক ভাল. কেবলমাত্র আমাদের সমস্ত সন্নিবেশকে একটি একক লেনদেনের মধ্যে মোড়ানো আমাদের পারফরম্যান্সকে প্রতি সেকেন্ডে 23,000 সন্নিবেশে উন্নত করে improved

একটি প্রস্তুত বিবৃতি ব্যবহার

লেনদেনটি ব্যবহার করা একটি বিশাল উন্নতি ছিল, তবে প্রতিটি সন্নিবেশের জন্য এসকিউএল স্টেটমেন্টটি পুনরায় সংশ্লেষ করা যদি আমরা একই এসকিউএলকে ওভার-ওভার ব্যবহার করি তা বোঝা যায় না। আসুন 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 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

নিস! এখানে আরও কিছুটা কোড রয়েছে (কল করতে sqlite3_clear_bindingsএবং ভুলেও ভুলবেন না sqlite3_reset) তবে আমরা আমাদের পারফরম্যান্সকে দ্বিগুণ করে প্রতি সেকেন্ডে 53,000 সন্নিবেশ করিয়েছি।

PRAGMA সিঙ্ক্রোনাস = অফ

ডিফল্টরূপে এসকিউএলাইট ওএস-স্তরের রাইট কমান্ড জারির পরে বিরতি দেবে। এটি গ্যারান্টি দেয় যে ডেটা ডিস্কে লিখিত হয়। সেট করে 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 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

উন্নতিগুলি এখন ছোট, তবে আমরা প্রতি সেকেন্ডে 69,600 সন্নিবেশ করিয়েছি।

PRAGMA জার্নাল_মোড = স্মৃতি

মূল্যায়ন করে মেমরিতে রোলব্যাক জার্নালটি সংরক্ষণ করার বিষয়ে বিবেচনা করুন 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 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

আগের অপ্টিমাইজেশনের তুলনায় কিছুটা ধীর গতিতে প্রতি সেকেন্ডে 64৪,০০০ সন্নিবেশ করা হয়।

PRAGMA সিঙ্ক্রোনাস = অফ এবং PRAGMA জার্নাল_মোড = স্মৃতি

আগের দুটি অপটিমাইজেশন একত্রিত করা যাক। এটি কিছুটা ঝুঁকিপূর্ণ (ক্রাশের ক্ষেত্রে) তবে আমরা কেবল ডেটা আমদানি করি (ব্যাংক চালাচ্ছি না):

/* 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 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

ফ্যান্টাস্টিক! আমরা প্রতি সেকেন্ডে ,000২,০০০ সন্নিবেশ করতে সক্ষম

একটি ইন-মেমরি ডেটাবেস ব্যবহার করা

কেবল কিক্সের জন্য, আসুন পূর্ববর্তী সমস্ত অপ্টিমাইজেশানগুলির উপর ভিত্তি করে তৈরি করুন এবং ডাটাবেস ফাইলের নামটি নতুনভাবে সংজ্ঞায়িত করুন যাতে আমরা সম্পূর্ণ র‍্যামে কাজ করছি:

#define DATABASE ":memory:"

10.94 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

আমাদের ডেটাবেসটি র‍্যামে সঞ্চয় করা অত্যন্ত ব্যবহারিক নয়, তবে এটি চিত্তাকর্ষক যে আমরা প্রতি সেকেন্ডে ,000৯,০০০ সন্নিবেশ সম্পাদন করতে পারি

সি কোড রিফ্যাক্টরিং

যদিও বিশেষত এসকিউএলাইট উন্নতি নয়, লুপের অতিরিক্ত char*অ্যাসাইনমেন্ট ক্রিয়াকলাপটি আমি পছন্দ করি না whilestrtok()সরাসরি সেই আউটপুট সরাসরি প্রবেশের জন্য সেই কোডটিকে দ্রুত রিফ্যাক্টর করি এবং সংকলকটি sqlite3_bind_text()আমাদের জন্য জিনিসগুলি দ্রুত করার চেষ্টা করুক:

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.94 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

আমাদের প্যারামিটার বাইন্ডিংয়ে ব্যবহৃত স্ট্রিং প্রসেসিং কোডটিতে কিছুটা রিফ্যাক্টরিং আমাদের প্রতি সেকেন্ডে 96,700 সন্নিবেশ সম্পাদনের অনুমতি দিয়েছে আমি মনে করি এটি নিরাপদ যে এটি যথেষ্ট দ্রুত । যেহেতু আমরা অন্যান্য ভেরিয়েবলগুলি (যেমন পৃষ্ঠার আকার, সূচক তৈরি ইত্যাদি) টুইট করতে শুরু করি এটি আমাদের বেঞ্চমার্ক হবে।


সংক্ষিপ্তসার (এখনও অবধি)

আমি আশা করি আপনি এখনও আমার সাথে আছেন! আমরা এই রাস্তাটি শুরু করার কারণটি হ'ল এসকিউএলাইটের সাথে বাল্ক-sertোকানো পারফরম্যান্সের পরিমাণ এতটাই ভিন্ন and একই সংকলক (এবং সংকলক বিকল্পগুলি) ব্যবহার করে, এসকিউএলাইটের একই সংস্করণ এবং একই ডাটা আমরা আমাদের সেকেন্ডে এবং এসকিউএলাইটের ব্যবহারের প্রতি সেকেন্ডে 85 টি সন্নিবেশ করায় প্রতি সেকেন্ডে 96,000 এরও বেশি সংখ্যার নিকৃষ্ট পরিস্থিতি থেকে যায়!


INDEX তৈরি করুন তারপরে INSERT বনাম INSERT তারপর INDEX তৈরি করুন

আমরা SELECTকর্মক্ষমতা পরিমাপ করা শুরু করার আগে , আমরা জানি যে আমরা সূচকগুলি তৈরি করব। এটি নীচের উত্তরগুলির মধ্যে একটিতে প্রস্তাবিত হয়েছে যে বাল্ক সন্নিবেশগুলি করার সময়, তথ্য সন্নিবেশ করার পরে সূচকটি তৈরি করা দ্রুত হয় (প্রথমে সূচি তৈরির পরে ডেটা সন্নিবেশ করার বিপরীতে)। আসুন চেষ্টা করুন:

সূচক তৈরি করুন তারপরে ডেটা Inোকান

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

18.13 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

ডেটা thenোকান তারপরে সূচি তৈরি করুন

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

13.66 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে

যেমনটি প্রত্যাশা করা হয়েছিল, একটি কলাম ইনডেক্স করা হলে বাল্ক-সন্নিবেশগুলি ধীরে ধীরে, তবে তথ্য সন্নিবেশ করার পরে সূচীটি তৈরি করা থাকলে এটি কোনও পার্থক্য করে। আমাদের নং-সূচক বেসলাইনটি প্রতি সেকেন্ডে 96,000 সন্নিবেশ করানো হয়। প্রথমে সূচক তৈরি করা পরে ডেটা সন্নিবেশ করানো আমাদের প্রতি সেকেন্ডে 47,700 সন্নিবেশ দেয়, যেখানে প্রথমে তথ্য সন্নিবেশ করিয়ে সূচক তৈরি করে প্রতি সেকেন্ডে আমাদের 63,300 সন্নিবেশ দেয়।


আমি আনন্দের সাথে চেষ্টা করার জন্য অন্যান্য পরিস্থিতিতে পরামর্শ চাই ... এবং শীঘ্রই নির্বাচন জিজ্ঞাসার জন্য অনুরূপ ডেটা সংকলন করা হবে।


8
ভাল যুক্তি! আমাদের ক্ষেত্রে আমরা এক্সএমএল এবং সিএসভি পাঠ্য ফাইল থেকে 200 কে রেকর্ডে পাঠানো প্রায় 1.5 মিলিয়ন কী / মান জোড়গুলি নিয়ে কাজ করছি। ডাটাবেসগুলির সাথে তুলনা করে ছোট যা এসও-র মতো সাইটগুলি চালায় - তবে এসকিউএলইট পারফরম্যান্স টিউন করা গুরুত্বপূর্ণ হয়ে ওঠে big
মাইক উইলেকস

51
"আমাদের কাছে এক্সএমএল ফাইলগুলিতে প্রচুর পরিমাণে কনফিগারেশন ডেটা সঞ্চিত রয়েছে যা অ্যাপ্লিকেশন শুরু হওয়ার সাথে সাথে আরও প্রক্রিয়াকরণের জন্য একটি এসকিউএল ডাটাবেসে পার্স করা এবং লোড করা হয়।" XML এ সঞ্চয় করার পরে আরম্ভের সময়ে সবকিছু লোড করার পরিবর্তে আপনি কেন প্রথমে স্ক্লাইট ডাটাবেসে সমস্ত কিছু রাখেন না?
সিএএফএক্সএক্সএক্স

14
আপনি কল না করার চেষ্টা করেছেন sqlite3_clear_bindings(stmt);? আপনি প্রতিবার বাইন্ডিংগুলি সেট করেছেন যার মাধ্যমে পর্যাপ্ত হওয়া উচিত: প্রথমবারের জন্য sqlite3_step () কল করার আগে বা sqlite3_reset () এর ঠিক পরে, অ্যাপ্লিকেশনটি স্ক্রাইলাইট3_বাইন্ড () ইন্টারফেসগুলির মধ্যে একটিকে প্যারামিটারগুলিতে মান সংযুক্ত করতে পারে। Sqlite3_bind () এ প্রতিটি কল একই প্যারামিটারে পূর্বের বাইন্ডিংগুলিকে ওভাররাইড করে (দেখুন: sqlite.org/cintro.html )। আপনাকে অবশ্যই এটি কল করতে হবে বলে এই ফাংশনটির জন্য ডক্সে কিছু নেই ।
আহকক্স

21
আপনি কি বারবার পরিমাপ করেছেন? Local টি স্থানীয় পয়েন্টার এড়ানোর জন্য 4-এর "জয়" অদ্ভুত, এমনকি একটি বিভ্রান্তি অপ্টিমাইজার হিসাবে ধরে নিয়েছে।
পিটারচেন

5
feof()আপনার ইনপুট লুপের সমাপ্তি নিয়ন্ত্রণ করতে ব্যবহার করবেন না । দ্বারা প্রত্যাবর্তিত ফলাফল ব্যবহার করুন fgets()stackoverflow.com/a/15485689/827263
কিথ থম্পসন

উত্তর:


784

বেশ কয়েকটি টিপস:

  1. লেনদেনে সন্নিবেশ / আপডেট রাখুন।
  2. এসকিউএলাইটের পুরানো সংস্করণগুলির জন্য - একটি স্বল্প জালিয়াতি জার্নাল মোড বিবেচনা করুন ( pragma journal_mode)। নেই NORMAL, এবং তারপর আছে OFF, যা উল্লেখযোগ্যভাবে গতি সন্নিবেশ বৃদ্ধি করতে পারেন করছি আপনিও ডাটাবেসের সম্ভবত দূষিত পেয়ে যদি অপারেটিং সিস্টেম ক্র্যাশ সম্পর্কে চিন্তিত নয়। আপনার অ্যাপ্লিকেশনটি ক্র্যাশ হলে ডেটা ঠিক থাকতে হবে। নোট করুন যে নতুন সংস্করণগুলিতে OFF/MEMORYসেটিংস অ্যাপ্লিকেশন স্তরের ক্র্যাশগুলির জন্য নিরাপদ নয়।
  3. পৃষ্ঠার আকারের সাথে খেলেও পার্থক্য হয় ( PRAGMA page_size)। বৃহত্তর পৃষ্ঠাগুলির মাপ থাকা বড় আকারের পৃষ্ঠাগুলি মেমরিতে রাখা হওয়ায় পাঠ্য এবং লেখাগুলি কিছুটা দ্রুততর করতে পারে। মনে রাখবেন যে আপনার ডাটাবেসের জন্য আরও মেমরি ব্যবহৃত হবে।
  4. আপনার যদি সূচকগুলি থাকে তবে CREATE INDEXআপনার সমস্ত সন্নিবেশ করার পরে কল করার বিষয়ে বিবেচনা করুন। এটি সূচক তৈরি করার পরে আপনার সন্নিবেশগুলি করার চেয়ে উল্লেখযোগ্যভাবে দ্রুত।
  5. আপনার এসকিউএলাইটে একযোগে অ্যাক্সেস থাকলে আপনার যথেষ্ট সতর্কতা অবলম্বন করতে হবে, লেখাগুলি সম্পন্ন করার সময় পুরো ডাটাবেসটি লক হয়ে যায় এবং একাধিক পাঠক সম্ভব হলেও লেখাগুলি লক আউট হয়ে যায়। নতুন এসকিউএল সংস্করণে ওয়াল যোগ করার সাথে এটি কিছুটা উন্নত হয়েছে।
  6. স্থান সাশ্রয় করার সুযোগ নিন ... ছোট ডাটাবেসগুলি দ্রুত যায়। উদাহরণস্বরূপ, যদি আপনার কী মান জোড় থাকে, INTEGER PRIMARY KEYসম্ভব হলে কীটি তৈরির চেষ্টা করুন , যা টেবিলের অন্তর্নিহিত অনন্য সারি নম্বর কলামটি প্রতিস্থাপন করবে।
  7. আপনি যদি একাধিক থ্রেড ব্যবহার করে থাকেন তবে আপনি ভাগ করা পৃষ্ঠা ক্যাশে ব্যবহার করার চেষ্টা করতে পারেন যা লোড হওয়া পৃষ্ঠাগুলিকে থ্রেডগুলির মধ্যে ভাগ করার মঞ্জুরি দেয়, যা ব্যয়বহুল আই / ও কলগুলি এড়াতে পারে।
  8. ব্যবহার করবেন না !feof(file)!

আমি এখানে এবং এখানে অনুরূপ প্রশ্ন জিজ্ঞাসা করেছি ।


9
দস্তাবেজগুলি কোনও PRAGMA জার্নাল_মোড জানে না সাধারণ সাধারণ স্ক্লাইট.অর্গ
ওয়ান ওয়ার্ল্ড

4
এটি একটি সময় হয়েছে, আমার পরামর্শগুলি ওয়াল প্রবর্তনের আগে পুরানো সংস্করণগুলির জন্য প্রয়োগ হয়েছিল। দেখে মনে হচ্ছে মোছা হ'ল নতুন সাধারণ সেটিংস, এবং এখন বন্ধ এবং স্মৃতি সেটিংসও রয়েছে। আমি মনে করি অফ / স্মৃতি ডাটাবেস অখণ্ডতার জন্য ব্যয় করে লেখার কার্যকারিতা উন্নত করবে এবং রোলব্যাকগুলি সম্পূর্ণরূপে অক্ষম করে।
স্নাজজার

4
# 7 এর জন্য, কীভাবে সি # system.data.sqlite মোড়ক ব্যবহার করে ভাগ করা পৃষ্ঠাগুলি ক্যাশে সক্ষম করবেন তার একটি উদাহরণ আছে ?
অ্যারন হুডন

4
# 4 পুরানো স্মৃতি ফিরিয়ে এনেছে - আগের বারের মধ্যে কমপক্ষে একটি কেস ছিল যেখানে একটি গ্রুপ যুক্ত হওয়ার আগে একটি সূচক ফেলে দেওয়া হয়েছিল এবং পরে এটি পুনরায় তৈরি করার পরে সন্নিবেশকে উল্লেখযোগ্যভাবে ছড়িয়ে দেওয়া হয়েছিল। কিছু আধুনিকতার জন্য এখনও আধুনিক সিস্টেমে দ্রুত কাজ করতে পারে যেখানে আপনি জানেন যে পিরিয়ডের জন্য আপনার কাছে টেবিলের একমাত্র অ্যাক্সেস রয়েছে।
বিল কে

থাম্বস আপ # 1 এর জন্য: আমার নিজের লেনদেনের ক্ষেত্রে খুব ভাল ভাগ্য ছিল।
এন্নো

145

সেইগুলি প্রবেশের SQLITE_STATICপরিবর্তে ব্যবহার করার চেষ্টা করুন SQLITE_TRANSIENT

SQLITE_TRANSIENT SQLite ফিরিয়ে দেওয়ার আগে স্ট্রিং ডেটা অনুলিপি করে দেবে।

SQLITE_STATICএটিকে বলে যে আপনি যে মেমরি ঠিকানা দিয়েছিলেন সেটি ক্যোয়ারী সম্পন্ন না হওয়া পর্যন্ত বৈধ হবে (যা এই লুপটিতে সর্বদা ক্ষেত্রে থাকে)। এটি আপনাকে লুপ প্রতি কয়েকটি বরাদ্দ, অনুলিপি এবং অপসারণের কাজগুলি সংরক্ষণ করবে। সম্ভবত একটি বৃহত উন্নতি।


108

এড়ানো sqlite3_clear_bindings(stmt)

পরীক্ষার কোডটি প্রতিবার বাইন্ডিং সেট করে যার মাধ্যমে পর্যাপ্ত হওয়া উচিত।

সি এপিআই ইন্ট্রো SQLite ডক্স থেকে বলেছেন:

কলিং এর আগে sqlite3_step () প্রথমবারের মত বা অবিলম্বে পরে sqlite3_reset () , আবেদন ডাকা যাবে sqlite3_bind () ইন্টারফেসগুলি প্যারামিটার মান সংযুক্ত করতে। Sqlite3_bind () এ প্রতিটি কল একই প্যারামিটারে পূর্বের বাইন্ডিংগুলিকে ওভাররাইড করে

ডাইসগুলিতে sqlite3_clear_bindingsবলার জন্য কিছুই নেই যে কেবল বাঁধাই সেট করার পাশাপাশি আপনাকে অবশ্যই এটি কল করতে হবে।

আরও বিশদ: এড়িয়ে চলুন_স্ক্লাইট3_সায়ার_বাইন্ডিংস ()


5
আশ্চর্যজনকভাবে সঠিক: "অনেকের অন্তর্দৃষ্টি বিপরীতে, sqlite3_reset () কোনও প্রস্তুত বিবৃতিতে বাইন্ডিংগুলি পুনরায় সেট করে না all সমস্ত হোস্ট প্যারামিটারগুলি NUL এ পুনরায় সেট করতে এই রুটিনটি ব্যবহার করুন Use" - sqlite.org/c3ref/clear_bindings.html
ফ্রান্সিস

63

বাল্ক সন্নিবেশগুলিতে

এই পোস্টটি দ্বারা এবং স্ট্যাক ওভারফ্লো প্রশ্ন দ্বারা অনুপ্রাণিত হয়েছে যা আমাকে এখানে নিয়ে গেছে - এসকিউএলাইট ডাটাবেসে একসাথে একাধিক সারি সন্নিবেশ করা কি সম্ভব? - আমি আমার প্রথম গিট সংগ্রহস্থল পোস্ট করেছি :

https://github.com/rdpoor/CreateOrUpdate

যা প্রচুর পরিমাণে মাইএসকিউএল , এসকিউএলাইট বা পোস্টগ্রেএসকিউএল ডাটাবেসগুলিতে অ্যাক্টিভেকর্ডগুলির একটি অ্যারে লোড করে। এটি বিদ্যমান রেকর্ডগুলি উপেক্ষা করার জন্য, সেগুলি ওভাররাইট করতে বা একটি ত্রুটি বাড়াতে একটি বিকল্প অন্তর্ভুক্ত করে। আমার প্রাথমিক প্রাথমিক মানদণ্ডগুলি সিক্যুয়াল লেখার তুলনায় 10x গতির উন্নতি দেখায় - ওয়াইএমএমভি।

আমি এটি প্রোডাকশন কোডে ব্যবহার করছি যেখানে আমার প্রায়শই বড় ডেটাসেট আমদানি করা প্রয়োজন এবং আমি এতে খুশি।


4
@ জেস: আপনি যদি লিঙ্কটি অনুসরণ করেন তবে আপনি দেখতে পাবেন যে তিনি ব্যাচ সন্নিবেশ সিনট্যাক্সটি বোঝাতে চেয়েছিলেন।
অ্যালিক্স অ্যাক্সেল

48

আপনি যদি নিজের INSERT / আপডেটের বিবৃতি খণ্ডন করতে পারেন তবে বেশিরভাগ আমদানি সেরা সম্পাদন করে বলে মনে হচ্ছে । 10,000 বা তারও বেশি মূল্য আমার জন্য কেবল কয়েকটি সারি, ওয়াইএমএমভি সহ একটি টেবিলে ভাল কাজ করেছে ...


22
আপনি x = 10,000 টিউন করতে চান যাতে x = ক্যাশে [= ক্যাশে_ সাইজ * পৃষ্ঠা_সাইজ] / আপনার প্রবেশের গড় আকার।
অ্যালিক্স অ্যাক্সেল

43

যদি আপনি কেবল পড়ার বিষয়ে চিন্তা করেন তবে কিছুটা দ্রুত (তবে বাসি ডেটা পড়তে পারে) সংস্করণটি একাধিক থ্রেড (প্রতি থ্রেড সংযোগ) থেকে একাধিক সংযোগ থেকে পড়তে হবে।

টেবিলের মধ্যে প্রথমে আইটেমগুলি সন্ধান করুন:

SELECT COUNT(*) FROM table

তারপরে পৃষ্ঠাগুলিতে পড়ুন (সীমাবদ্ধ / অফসেট):

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 বা আরও বেশি সারি)।

খুব বেশি বা খুব কম থ্রেড এটি করবে না, আপনাকে নিজেরাই বেঞ্চমার্ক এবং প্রোফাইল করা দরকার।

এছাড়াও আমাদের জন্য, শ্যারডেচে কর্মক্ষমতাটি ধীরে ধীরে করেছে, তাই আমি ম্যানুয়ালি ব্যক্তিগতভাবে রেখেছি (কারণ এটি আমাদের জন্য বিশ্বব্যাপী সক্ষম হয়েছিল)


29

আমি ক্যাশে_সাইজ উচ্চ মানের হিসাবে উত্থাপন না করা পর্যন্ত লেনদেন থেকে কোনও লাভ পাই না PRAGMA cache_size=10000;


নোট করুন যে পৃষ্ঠাগুলির সংখ্যাকে ক্যাশের জন্য cache_sizeসেট করে , মোট র্যাম আকার নয়, এর জন্য ধনাত্মক মান ব্যবহার করা using 4kB এর ডিফল্ট পৃষ্ঠার আকারের সাথে, এই সেটিংটি খোলা ফাইল প্রতি 40MB ডেটা ধরে রাখবে (বা ভাগ করে নেওয়া ক্যাশে দিয়ে চলতে থাকলে প্রক্রিয়া অনুযায়ী )।
গ্রো

21

এই টিউটোরিয়ালটি পড়ার পরে, আমি এটি আমার প্রোগ্রামে প্রয়োগ করার চেষ্টা করেছি।

আমার কাছে 4-5 টি ফাইল রয়েছে যাতে ঠিকানা রয়েছে। প্রতিটি ফাইলের প্রায় 30 মিলিয়ন রেকর্ড রয়েছে। আমি আপনাকে পরামর্শ দিচ্ছি একই কনফিগারেশন ব্যবহার করছি কিন্তু আমার প্রতি সেকেন্ডে INSERT গুলি সংখ্যা কম (low 10.000 প্রতি সেকেন্ড রেকর্ড)।

এখানে আপনার পরামর্শ ব্যর্থ হয়। আপনি সমস্ত রেকর্ডের জন্য একটি একক লেনদেন এবং কোনও ত্রুটি / ব্যর্থতা ছাড়াই একটি একক সন্নিবেশ ব্যবহার করেন। ধরা যাক যে আপনি প্রতিটি রেকর্ডকে বিভিন্ন টেবিলে একাধিক সন্নিবেশে বিভক্ত করছেন। রেকর্ডটি ভেঙে গেলে কী হবে?

অন ​​কনফ্লিক্ট কমান্ডটি প্রযোজ্য নয়, কারণ যদি আপনার রেকর্ডে 10 টি উপাদান থাকে এবং আপনার প্রতিটি উপাদানকে একটি আলাদা টেবিলের মধ্যে সন্নিবেশ করা প্রয়োজন, যদি উপাদান 5 একটি চুক্তি ত্রুটি পায় তবে পূর্ববর্তী 4 টি সন্নিবেশও খুব বেশি হওয়া দরকার।

রোলব্যাক আসে তাই এখানে। রোলব্যাকের সাথে একমাত্র সমস্যা হ'ল আপনি আপনার সমস্ত সন্নিবেশ হারিয়ে ফেলুন এবং উপর থেকে শুরু করুন। আপনি কীভাবে এটি সমাধান করতে পারেন?

আমার সমাধানটি ছিল একাধিক লেনদেন ব্যবহার করা । আমি প্রতি ১০,০০০ রেকর্ডে একটি লেনদেন শুরু করি এবং শেষ করি (কেন এটি নম্বরটি জিজ্ঞাসা করবেন না, এটি আমার পরীক্ষা করা সবচেয়ে দ্রুততম ছিল)। আমি ১০,০০০ আকারের একটি অ্যারে তৈরি করেছি এবং সেখানে সফল রেকর্ডগুলি sertোকিয়েছি। ত্রুটি দেখা দিলে আমি রোলব্যাক করি, লেনদেন শুরু করি, আমার অ্যারে থেকে রেকর্ড সন্নিবেশ করান, প্রতিশ্রুতিবদ্ধ এবং তারপরে ভাঙা রেকর্ডের পরে একটি নতুন লেনদেন শুরু করি।

এই সমাধানটি আমাকে খারাপ / সদৃশ রেকর্ডযুক্ত ফাইলগুলি নিয়ে কাজ করার সময় আমার কাছে থাকা সমস্যাগুলি বাইপাস করতে সহায়তা করেছিল (আমার প্রায় 4% খারাপ রেকর্ড ছিল)।

আমি তৈরি করা অ্যালগরিদম আমাকে আমার প্রক্রিয়াটি ২ ঘন্টার কমিয়ে আনতে সহায়তা করে। ফাইলটির 1hr 30m ফাইলের চূড়ান্ত লোডিং প্রক্রিয়া যা এখনও ধীর হলেও এটি শুরুতে 4hrs এর তুলনায় নয়। আমি সন্নিবেশগুলিকে ১০,০০০ / সেকেন্ড থেকে s 14.000 / সেকেন্ড করতে সক্ষম হয়েছি

এটির গতি বাড়ানোর বিষয়ে কারও কাছে যদি অন্য কোনও ধারণা থাকে তবে আমি পরামর্শের জন্য উন্মুক্ত।

আপডেট :

উপরের আমার উত্তরের সাথে সাথে, আপনার মনে রাখা উচিত যে আপনি যে হার্ড ড্রাইভটি ব্যবহার করছেন তার উপর নির্ভর করে প্রতি সেকেন্ডে সন্নিবেশ করানো হয়। আমি এটি বিভিন্ন হার্ড ড্রাইভ সহ 3 টি পৃথক পিসিতে পরীক্ষা করেছি এবং সময়ে প্রচুর পার্থক্য পেয়েছি। পিসি 1 (1 ঘন্টা 30 মিটার), পিসি 2 (6 ঘন্টা) পিসি 3 (14 ঘন্টা), তাই আমি কেন ভাবব তা ভাবতে শুরু করি।

দুই সপ্তাহ গবেষণা এবং একাধিক সংস্থান পরীক্ষা করার পরে: হার্ড ড্রাইভ, রাম, ক্যাশে, আমি জানতে পেরেছি যে আপনার হার্ড ড্রাইভে কিছু সেটিংস আই / ও হারকে প্রভাবিত করতে পারে। আপনার পছন্দসই আউটপুট ড্রাইভে বৈশিষ্ট্যগুলি ক্লিক করে আপনি সাধারণ ট্যাবে দুটি বিকল্প দেখতে পাবেন। Opt1: এই ড্রাইভটি সংকুচিত করুন, Opt2: এই ড্রাইভের ফাইলগুলিকে বিষয়বস্তু সূচকযুক্ত করার অনুমতি দিন।

এই দুটি অপশন অক্ষম করে সমস্ত 3 পিসি এখন সমাপ্ত হতে প্রায় একই সময় নেয় (1 ঘন্টা এবং 20 থেকে 40 মিনিট)। যদি আপনি ধীর সন্নিবেশগুলির মুখোমুখি হন তবে আপনার হার্ড ড্রাইভটি এই বিকল্পগুলির সাথে কনফিগার করা হয়েছে কিনা তা পরীক্ষা করে দেখুন। এটি সমাধান খোঁজার চেষ্টা করে আপনার প্রচুর সময় এবং মাথা ব্যথা সাশ্রয় করবে


আমি নিম্নলিখিত পরামর্শ করব। * স্ট্রিং অনুলিপি এড়াতে SQLITE_STATIC বনাম SQLITE_TRANSIENT ব্যবহার করুন আপনার অবশ্যই নিশ্চিত করতে হবে যে লেনদেনটি কার্যকর হওয়ার আগে স্ট্রিংটি পরিবর্তন করা হবে না * স্টপ_টাইমসের মধ্যে বাল্ক সন্নিবেশ সন্নিবেশ ব্যবহার করুন (নল,?,???,??,???)? ,?), (নুল,?,??,?,??,??,???), (নল,?,?,??,?????????) ,?,?,?,?,?,?,?,??), (নল,?,??,?,?,??,?????) * এমএম্যাপ করে সংখ্যা কমাতে ফাইলটি? syscalls।
রাউজিয়ার

আমি 11.51 সেকেন্ডে 5,582,642 রেকর্ড আমদানি করতে সক্ষম হচ্ছি
রাউজিয়ার

11

আপনার প্রশ্নের উত্তর হ'ল নতুন এসকিউএলাইট 3 কার্য সম্পাদন করেছে, এটি ব্যবহার করুন।

এই উত্তরটি স্ক্য্লাইটের সাথে স্ক্লাইট 3 ব্যবহার করার চেয়ে 25 গুণ কম ধীরে ধীরে কেন SQLlalmymy inোকানো হয়? স্ক্যালএলেচেমি দ্বারা Orm লেখকের 0.5 সেকেন্ডে 100k সন্নিবেশ রয়েছে এবং আমি পাইথন-স্ক্লাইট এবং স্ক্যালএলচেমির সাথে একই রকম ফলাফল দেখেছি। যা আমাকে বিশ্বাস করতে পরিচালিত করে যে এসকিউএল 3 এর সাথে পারফরম্যান্স উন্নত হয়েছে।


-1

ডিবিতে বাল্ক ডেটা forোকানোর জন্য কনটেন্টপ্রোভাইডার ব্যবহার করুন। ডাটাবেসে বাল্ক ডেটা forোকানোর জন্য নীচের পদ্ধতিটি ব্যবহার করা হয়েছে। এটি এসকিউএলাইটের প্রতি সেকেন্ডের ইনসার্ট-উন্নত করা উচিত।

private SQLiteDatabase database;
database = dbHelper.getWritableDatabase();

public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {

database.beginTransaction();

for (ContentValues value : values)
 db.insert("TABLE_NAME", null, value);

database.setTransactionSuccessful();
database.endTransaction();

}

বাল্ক ইনসার্ট পদ্ধতি কল করুন:

App.getAppContext().getContentResolver().bulkInsert(contentUriTable,
            contentValuesArray);

লিঙ্ক: https://www.vogella.com/tutorials/AndroidSQLite/article.html আরও তথ্যের জন্য ContentProvider বিভাগ ব্যবহার করে চেক করুন

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.