2009-11-09 33 views
26

Tôi đã nghe nói rằng các câu được chuẩn bị với SQLite sẽ cải thiện hiệu suất. Tôi đã viết một số mã để kiểm tra điều đó, và không thấy bất kỳ sự khác biệt nào về hiệu suất khi sử dụng chúng. Vì vậy, tôi nghĩ có lẽ mã của tôi không chính xác. Xin vui lòng cho tôi biết nếu bạn thấy bất kỳ sai sót trong cách tôi đang làm điều này ...Trong SQLite, các câu lệnh chuẩn bị có thực sự cải thiện hiệu suất không?

[self testPrep:NO dbConn:dbConn]; 
[self testPrep:YES dbConn:dbConn]; 

reuse=0 
recs=2000 
2009-11-09 10:39:18 -0800 
processing... 
2009-11-09 10:39:32 -0800 

reuse=1 
recs=2000 
2009-11-09 10:39:32 -0800 
processing... 
2009-11-09 10:39:46 -0800 

-(void)testPrep:(BOOL)reuse dbConn:(sqlite3*)dbConn{ 
    int recs = 2000; 
    NSString *sql; 
    sqlite3_stmt *stmt; 

    sql = @"DROP TABLE test"; 
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL); 

    sql = @"CREATE TABLE test (id INT,field1 INT, field2 INT,field3 INT,field4 INT,field5 INT,field6 INT,field7 INT,field8 INT,field9 INT,field10 INT)"; 
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL); 

    for(int i=0;i<recs;i++){ 
     sql = @"INSERT INTO test (id,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10) VALUES (%d,1,2,3,4,5,6,7,8,9,10)"; 
     sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL); 
    } 

    sql = @"BEGIN"; 
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL); 

    if (reuse){ 
     sql = @"select * from test where field1=?1 and field2=?2 and field3=?3 and field4=?4 and field5=?5 and field6=?6 and field6=?6 and field8=?8 and field9=?9 and field10=?10 and id=?11"; 
     sqlite3_prepare_v2(dbConn, [sql UTF8String], -1, &stmt, NULL); 
    } 

    NSLog(@"reuse=%d",reuse); 
    NSLog(@"recs=%d",recs); 
    NSDate *before = [NSDate date]; 
    NSLog([before description]); 
    NSLog(@"processing..."); 
    for(int i=0;i<recs;i++){ 
     if (!reuse){ 
      sql = @"select * from test where field1=?1 and field2=?2 and field3=?3 and field4=?4 and field5=?5 and field6=?6 and field6=?6 and field8=?8 and field9=?9 and field10=?10 and id=?11"; 
      sqlite3_prepare_v2(dbConn, [sql UTF8String], -1, &stmt, NULL); 
     } 
     sqlite3_bind_int(stmt, 1, 1); 
     sqlite3_bind_int(stmt, 2, 2); 
     sqlite3_bind_int(stmt, 3, 3); 
     sqlite3_bind_int(stmt, 4, 4); 
     sqlite3_bind_int(stmt, 5, 5); 
     sqlite3_bind_int(stmt, 6, 6); 
     sqlite3_bind_int(stmt, 7, 7); 
     sqlite3_bind_int(stmt, 8, 8); 
     sqlite3_bind_int(stmt, 9, 9); 
     sqlite3_bind_int(stmt, 10, 10); 
     sqlite3_bind_int(stmt, 11, i); 

     while(sqlite3_step(stmt) == SQLITE_ROW) { 
     } 
     sqlite3_reset(stmt); 
    } 

    sql = @"BEGIN"; 
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL); 

    NSDate *after = [NSDate date]; 
    NSLog([after description]); 
} 
+0

có một số chỉ số (về điều đó và các cải thiện tốc độ khác) tại đây: http://stackoverflow.com/questions/1711631/improve-insert-per-second-performance-of-sqlite – cyrilchampier

Trả lời

19

báo cáo chuẩn bị cải thiện hiệu suất bằng cách cache execution plan for a query sau query optimizer đã tìm thấy phương án tốt nhất.

Nếu truy vấn bạn đang sử dụng không có kế hoạch phức tạp (chẳng hạn như các lựa chọn/chèn đơn giản không có tham gia), thì các câu lệnh đã chuẩn bị sẽ không mang lại cho bạn cải tiến lớn vì trình tối ưu hóa sẽ nhanh chóng tìm ra kế hoạch tốt nhất .

Tuy nhiên, nếu bạn chạy cùng một thử nghiệm với truy vấn có vài lần tham gia và sử dụng một số chỉ mục, bạn sẽ thấy sự khác biệt hiệu suất vì trình tối ưu hóa sẽ không chạy mỗi lần truy vấn.

+3

Ngoài ra - có thể có * là * một sự khác biệt hiển thị cho nhiều lần lặp lại hơn. – Tomalak

+1

Có một sự khác biệt lớn ngay cả đối với các truy vấn đơn giản. Tôi đã thực hiện một vài thao tác đơn giản trong mỗi lần cập nhật trạng thái trong một trò chơi, đó là một vấn đề đáng chú ý khi nói lắp và chạy mượt mà. – Arahman

+0

@Omokoii Bạn thực sự nên tránh chạy các hoạt động DB trong chuỗi trò chơi vòng lặp ... – PSIXO

6

Có - nó tạo ra một sự khác biệt lớn cho dù bạn sử dụng sqlite3_exec()sqlite3_prepare_v2()/sqlite3_bind_xxx()/sqlite3_step() để chèn số lượng lớn.

sqlite3_exec() chỉ là một phương pháp tiện lợi. Bên trong nó chỉ gọi cùng một chuỗi sqlite3_prepare_v2() and sqlite3_step(). mã ví dụ bạn đang kêu gọi sqlite3_exec() qua và giao trên một chuỗi chữ:

for(int i=0;i<recs;i++){ 
    sql = @"INSERT INTO test (id,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10) VALUES (%d,1,2,3,4,5,6,7,8,9,10)"; 
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL); 
} 

Tôi không biết các hoạt động bên của bộ phân tích SQLite, nhưng có lẽ các phân tích cú pháp là đủ thông minh để nhận ra rằng bạn đang sử dụng cùng một chuỗi chữ và sau đó bỏ qua phân tích lại/biên dịch lại với mỗi lần lặp lại.

Nếu bạn thử cùng một thử nghiệm với các giá trị thay đổi - bạn sẽ thấy sự khác biệt lớn hơn nhiều về hiệu suất.

+0

Tôi đã rất ngạc nhiên rằng điều này đã tạo ra nhiều sự khác biệt. Nó cạo một vài giây ra một chèn lớn cho tôi, và làm cho tôi hy vọng cho chèn lớn hơn. Tôi sẽ chuyển đổi tất cả các truy vấn để sử dụng bước chuẩn bị liên kết ngay bây giờ. :) – Joe

-3

Sử dụng chuẩn bị + bước thay vì thực hiện các cải tiến hiệu suất rất lớn là có thể. Trong một số trường hợp, hiệu suất đạt được là hơn 100% trong thời gian thực hiện.

+0

nguồn và số liệu? – cyrilchampier

+0

O, và 100% là không thể, đó là vô lý. –

Các vấn đề liên quan