2009-08-03 19 views
10

Tôi đang thực hiện một số lượng lớn INSERTS cho cơ sở dữ liệu SQLite. Tôi chỉ sử dụng một sợi. Tôi hàng loạt các bài viết để cải thiện hiệu suất và có một chút bảo mật trong trường hợp của một vụ tai nạn. Về cơ bản tôi nhớ cache một loạt dữ liệu trong bộ nhớ và sau đó khi tôi thấy thích hợp, tôi lặp lại tất cả dữ liệu đó và thực hiện INSERTS. Các mã này được hiển thị dưới đây:Tệp cơ sở dữ liệu bị khóa không thể giải thích được trong khi thực hiện SQLite cam kết

public void Commit() 
    { 
     using (SQLiteConnection conn = new SQLiteConnection(this.connString)) 
     { 
      conn.Open(); 
      using (SQLiteTransaction trans = conn.BeginTransaction()) 
      { 
       using (SQLiteCommand command = conn.CreateCommand()) 
       { 
        command.CommandText = "INSERT OR IGNORE INTO [MY_TABLE] (col1, col2) VALUES (?,?)"; 

        command.Parameters.Add(this.col1Param); 
        command.Parameters.Add(this.col2Param); 

        foreach (Data o in this.dataTemp) 
        { 
         this.col1Param.Value = o.Col1Prop; 
         this. col2Param.Value = o.Col2Prop; 

         command.ExecuteNonQuery(); 
        } 
       } 
       this.TryHandleCommit(trans); 
      } 
      conn.Close(); 
     } 
    } 

bây giờ tôi sử dụng các mánh lới quảng cáo sau đây để có được những điều để cuối cùng làm việc:

private void TryHandleCommit(SQLiteTransaction trans) 
    { 
     try 
     { 
      trans.Commit(); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Trying again..."); 
      this.TryHandleCommit(trans); 
     } 
    } 

tôi tạo DB của tôi như vậy:

public DataBase(String path) 
    { 
     //build connection string 
     SQLiteConnectionStringBuilder connString = new SQLiteConnectionStringBuilder(); 
     connString.DataSource = path; 
     connString.Version = 3; 
     connString.DefaultTimeout = 5; 
     connString.JournalMode = SQLiteJournalModeEnum.Persist; 
     connString.UseUTF16Encoding = true; 

     using (connection = new SQLiteConnection(connString.ToString())) 
     { 
      //check for existence of db 
      FileInfo f = new FileInfo(path); 

      if (!f.Exists) //build new blank db 
      { 
       SQLiteConnection.CreateFile(path); 
       connection.Open(); 

       using (SQLiteTransaction trans = connection.BeginTransaction()) 
       { 
        using (SQLiteCommand command = connection.CreateCommand()) 
        { 
         command.CommandText = DataBase.CREATE_MATCHES; 
         command.ExecuteNonQuery(); 

         command.CommandText = DataBase.CREATE_STRING_DATA; 
         command.ExecuteNonQuery(); 
         //TODO add logging 
        } 
        trans.Commit(); 
       } 
       connection.Close(); 
      } 
     }    
    } 

Sau đó tôi xuất chuỗi kết nối và sử dụng nó để có được các kết nối mới trong các phần khác nhau của chương trình.

Có vẻ như khoảng thời gian dường như ngẫu nhiên, mặc dù ở mức quá lớn để bỏ qua hoặc giải quyết khác vấn đề này, tôi nhận được SQLiteException unhandled: Tệp cơ sở dữ liệu bị khóa. Điều này xảy ra khi tôi cố gắng thực hiện giao dịch. Không có lỗi nào xảy ra trước đó. Điều này không luôn luôn xảy ra. Đôi khi toàn bộ điều chạy mà không có một xô.

  • Không có lần đọc nào được thực hiện trên các tệp này trước khi kết thúc cam kết.
  • Tôi có bản nhị phân SQLite mới nhất.
  • Tôi đang biên soạn cho .NET 2.0.
  • Tôi đang sử dụng VS 2008.
  • db là tệp cục bộ.
  • Tất cả hoạt động này được đóng gói trong một chuỗi/quy trình.
  • Tính năng bảo vệ chống vi-rút bị tắt (mặc dù tôi cho rằng điều đó chỉ liên quan nếu bạn đang kết nối qua mạng?).
  • Theo bài Scotsman của tôi đã thực hiện những thay đổi sau:
  • Chế độ Tạp chí thiết lập để Persist
  • file DB lưu trong C: \ Documents + Settings \ ApplicationData qua System.Windows.Forms.Application.AppData cửa sổ gọi
  • Không có ngoại lệ nội
  • Chứng kiến ​​trên hai máy riêng biệt (mặc dù phần cứng và phần mềm rất giống nhau)
  • Đã chạy Trình theo dõi quy trình - không có quy trình không liên quan nào được gắn vào các tệp DB - vấn đề chắc chắn trong mã của tôi ...

Có ai có ý tưởng gì đang xảy ra ở đây không?

Tôi biết tôi đã bỏ toàn bộ mã lộn xộn, nhưng tôi đã cố gắng tìm ra cách này quá lâu. Lời cảm ơn của tôi đến bất cứ ai làm cho nó đến cuối câu hỏi này!

brian

CẬP NHẬT:

Cảm ơn những lời đề nghị cho đến nay! Tôi đã triển khai nhiều thay đổi được đề xuất.Tôi cảm thấy rằng chúng tôi đang tiến gần hơn đến câu trả lời ... tuy nhiên ...

Quy tắc kỹ thuật ở trên hoạt động tuy nhiên nó không xác định! Nó không được đảm bảo để làm bất cứ điều gì ngoài việc quay trong trung lập mãi mãi. Trong thực tế nó dường như làm việc ở đâu đó giữa lần lặp thứ nhất và thứ 10. Nếu tôi bó cam kết của tôi tại một khoảng thời gian hợp lý thiệt hại sẽ được giảm nhẹ nhưng tôi thực sự không muốn để lại những thứ trong trạng thái này ...

Thêm đề xuất chào mừng!

+0

Mặc dù bạn không muốn có các cam kết nhỏ hơn - vấn đề vẫn còn xảy ra khi bạn di chuyển bắt đầu tran và cam kết tran vào vòng lặp hàng loạt bên trong? – pjp

Trả lời

5

Chạy Sysinternals Process Monitor và lọc tên tệp trong khi chạy chương trình của bạn để loại trừ nếu bất kỳ quy trình nào khác thực hiện bất kỳ điều gì và xem chương trình của bạn đang làm gì một cách không chính xác. Long shot, nhưng có thể cho một đầu mối.

+1

vậy ... có phải vấn đề chỉ là một tiến trình khác đang khóa tập tin? – Rory

2

Tệp cơ sở dữ liệu của bạn có trên cùng một máy với ứng dụng hoặc được lưu trữ trên máy chủ không?

Bạn nên tạo kết nối mới trong mỗi chuỗi. Tôi sẽ đơn giản hóa việc tạo ra một kết nối, sử dụng ở khắp mọi nơi: connection = new SQLiteConnection (connString.ToString());

và sử dụng tệp cơ sở dữ liệu trên cùng một máy như ứng dụng và kiểm tra lại.

Tại sao hai cách tạo kết nối khác nhau?

+0

Tôi hiện đã triển khai công cụ này. Không có may mắn nhưng một khởi đầu tốt im chắc chắn! cám ơn! –

3

Những điều cần xem cho:

  • không sử dụng kết nối trên nhiều chủ đề/quy trình.

  • Tôi đã thấy điều đó xảy ra khi trình quét vi rút phát hiện các thay đổi đối với tệp và cố gắng quét nó. Nó sẽ khóa tập tin trong một khoảng thời gian ngắn và gây ra sự tàn phá.

+1

Chỉ cần thử điều đó - tôi đoán nó không phải là vấn đề nhưng cảm ơn anyway! –

2

Những người này gặp sự cố tương tự (hầu hết, có vẻ như tệp nhật ký bị khóa, có thể tương tác TortoiseSVN ... kiểm tra các bài viết được tham chiếu).

Họ đã đưa ra một bộ đề xuất (đúng thư mục, thay đổi loại nhật ký từ xóa thành tồn tại, v.v.). http://sqlite.phxsoftware.com/forums/p/689/5445.aspx#5445


Tùy chọn chế độ tạp chí được thảo luận tại đây: http://www.sqlite.org/pragma.html. Bạn có thể thử TRUNCATE.

Có theo dõi ngăn xếp trong trường hợp ngoại lệ vào SQL Lite không?

Bạn cho biết bạn "thực hiện các cam kết của mình trong một khoảng thời gian hợp lý". Khoảng thời gian là gì?

+0

rất thú vị tôi thực sự sử dụng rùa - tôi sẽ đăng lại và cho bạn biết nếu nó giúp ... –

+0

sử dụng liên tục có thể đã giảm tần suất của vấn đề này, mặc dù không loại bỏ nó. cũng UserAppData là một cuộc gọi tốt. cảm ơn! –

+0

Vậy điều đó khiến bạn ở đâu? Trong khi tôi chắc chắn nó không phải là một vấn đề là nó có thể là bạn đang làm cam kết quá gần nhau (timewise) với nhau? – BlueShepherd

2

Tôi sẽ luôn sử dụng kết nối, giao dịch và lệnh trong mệnh đề using. Trong danh sách mã đầu tiên của bạn bạn đã làm, nhưng thứ ba của bạn (tạo ra các bảng) bạn đã không. Tôi đề nghị bạn làm điều đó quá, bởi vì (những người hiểu biết?) Có lẽ các lệnh tạo bảng bằng cách nào đó tiếp tục khóa tập tin. Cú sút dài ... nhưng đáng để bắn?

+0

thật không may là đã không làm các trick, nhưng nó có lẽ là cách tốt hơn để làm điều đó dù sao tôi đã cập nhật mã của tôi cho phù hợp ... thx! –

2

Bạn có Google Desktop Search (hoặc trình lập chỉ mục tệp khác) đang chạy không? Như đã đề cập, Sysinternals Process Monitor có thể giúp bạn theo dõi nó.

Ngoài ra, tên tệp của cơ sở dữ liệu là gì?Từ PerformanceTuningWindows:

rất, rất cẩn thận những gì bạn đặt tên cho cơ sở dữ liệu của bạn, đặc biệt là phần mở rộng

Ví dụ, nếu bạn cung cấp cho tất cả các cơ sở dữ liệu của bạn .sdb mở rộng (Cơ sở dữ liệu SQLite, tên đẹp hey Tôi nghĩ vì vậy khi tôi chọn nó ...) bạn phát hiện ra rằng phần mở rộng SDB đã được liên kết với các gói PACKFES.

Bây giờ, đây là phần dễ thương, APPFIX là một thực thi/gói Windows XP nhận, và nó sẽ, (tôi nhấn mạnh) ADD cơ sở dữ liệu chức năng System Restore

Điều này có nghĩa, ở lại với tôi ở đây , mỗi khi bạn viết bất cứ điều gì cho cơ sở dữ liệu, hệ thống Windows XP cho rằng một tệp thi hành đẫm máu đã thay đổi và sao chép cơ sở dữ liệu ENTIRE 800 meg của bạn vào thư mục khôi phục hệ thống ....

Tôi khuyên bạn nên sử dụng DB hoặc DAT.

9

Có vẻ như bạn đã không liên kết lệnh với giao dịch bạn đã tạo. Thay vì:

using (SQLiteCommand command = conn.CreateCommand()) 

Bạn nên sử dụng:

using (SQLiteCommand command = new SQLiteCommand("<INSERT statement here>", conn, trans)) 

Hoặc bạn có thể thiết lập thuộc tính Transaction của mình sau khi xây dựng của nó.

Trong khi chúng tôi đang ở đó - xử lý của bạn thất bại là không chính xác:

phương pháp ExecuteNonQuery của lệnh cũng có thể thất bại và bạn không thực sự được bảo vệ. Bạn nên thay đổi mã thành một cái gì đó như:

public void Commit() 
    { 
     using (SQLiteConnection conn = new SQLiteConnection(this.connString)) 
     { 
      conn.Open(); 
      SQLiteTransaction trans = conn.BeginTransaction(); 
      try 
      { 
       using (SQLiteCommand command = conn.CreateCommand()) 
       { 
        command.Transaction = trans; // Now the command is linked to the transaction and don't try to create a new one (which is probably why your database gets locked) 
        command.CommandText = "INSERT OR IGNORE INTO [MY_TABLE] (col1, col2) VALUES (?,?)"; 

        command.Parameters.Add(this.col1Param); 
        command.Parameters.Add(this.col2Param); 

        foreach (Data o in this.dataTemp) 
        { 
         this.col1Param.Value = o.Col1Prop; 
         this. col2Param.Value = o.Col2Prop; 

         command.ExecuteNonQuery(); 
        } 
       } 

       trans.Commit(); 
      } 
      catch (SQLiteException ex) 
      { 
       // You need to rollback in case something wrong happened in command.ExecuteNonQuery() ... 
       trans.Rollback(); 
       throw; 
      } 
     } 
    } 

Một điều khác là bạn không cần lưu vào bộ nhớ cache bất cứ thứ gì trong bộ nhớ. Bạn có thể phụ thuộc vào cơ chế ghi nhật ký SQLite để lưu trữ trạng thái giao dịch không đầy đủ.

+0

Tuyệt vời Tôi sẽ cần một thời gian để tiêu hóa tất cả điều này và thử nó ra nhưng cảm ơn cho đầu vào! –

+0

Ngoài ra, tôi vui vì bạn đề cập đến bộ nhớ đệm. Ban đầu tôi đã làm nó theo cách bạn đề nghị, nhưng di chuyển ra khỏi đó vì tôi lo ngại rằng nó đã góp phần vào mớ hỗn độn thất bại này. Một khi tôi nhận được nó làm việc tôi sẽ quay trở lại và gửi một số mẫu mã thích hợp ... –

+0

tôi cũng đã thử cả hai phương pháp - xây dựng bằng cách đi qua trong giao dịch và thiết lập các đặc tính post-hoc. không làm giảm bớt vấn đề của tôi. cảm ơn cho lời đề nghị tho! –

4

Chúng tôi đã gặp phải sự cố tương tự khi sử dụng Giao dịch lồng nhau với lớp TransactionScope. Chúng tôi nghĩ tất cả các hành động cơ sở dữ liệu đã xảy ra trên cùng một chuỗi ... tuy nhiên chúng tôi đã bị cơ chế Giao dịch phát hiện ... cụ thể hơn là giao dịch Ambient.

Về cơ bản, có một giao dịch cao hơn chuỗi đó, bởi sự kỳ diệu của ado, kết nối tự động gia nhập. Kết quả là, mặc dù chúng tôi nghĩ rằng chúng tôi đang ghi vào cơ sở dữ liệu trên một chủ đề duy nhất, 't thực sự xảy ra cho đến khi giao dịch trên cùng đã được cam kết. Tại điểm 'không xác định' này, cơ sở dữ liệu được viết để làm cho nó bị khóa ngoài tầm kiểm soát của chúng tôi.

Giải pháp là để đảm bảo rằng cơ sở dữ liệu SQLite không trực tiếp tham gia vào các giao dịch môi trường xung quanh bằng cách đảm bảo chúng tôi sử dụng một cái gì đó như:

using(TransactionScope scope = new TransactionScope(TransactionScopeOptions.RequiresNew)) 
{ 
    ... 
    scope.Complete() 
} 
3

tôi bắt đầu phải đối mặt với vấn đề này cùng một ngày hôm nay: Tôi đang học asp. net mvc, xây dựng ứng dụng đầu tiên của tôi hoàn toàn từ đầu. Đôi khi, khi tôi viết thư cho cơ sở dữ liệu, tôi sẽ nhận được cùng một ngoại lệ, nói rằng tệp cơ sở dữ liệu đã bị khóa.

Tôi thấy nó thực sự lạ, vì tôi hoàn toàn chắc chắn rằng chỉ có một kết nối được mở tại thời điểm đó (dựa trên danh sách các trình xử lý tệp đang hoạt động của quá trình thám hiểm).

Tôi cũng đã xây dựng toàn bộ lớp truy cập dữ liệu từ đầu, sử dụng System.Data.SQLite .Net provider và, khi tôi lên kế hoạch, tôi đã đặc biệt quan tâm đến các kết nối và giao dịch để đảm bảo không có kết nối hoặc giao dịch được để lại xung quanh.

Phần khó khăn là thiết lập điểm ngắt trên lệnh ExecuteNonQuery() và chạy ứng dụng trong chế độ gỡ lỗi sẽ làm cho lỗi biến mất! Googling, tôi tìm thấy một cái gì đó thú vị trên trang web này: http://www.softperfect.com/board/read.php?8,5775. Ở đó, ai đó đã trả lời chuỗi đề xuất tác giả đặt đường dẫn cơ sở dữ liệu vào danh sách bỏ qua chống vi-rút.

Tôi đã thêm tệp cơ sở dữ liệu vào danh sách bỏ qua chống vi-rút của mình (Microsoft Security Essentials) và nó đã giải quyết được sự cố của tôi. Không có nhiều lỗi cơ sở dữ liệu bị khóa!

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