2012-05-22 12 views
19

मैंने एक जावा एप्लिकेशन लिखा है जो कई धागे से SQLite डेटाबेस में घटनाओं को स्पोरैडिक रूप से लॉग करता है। मैंने देखा है कि मैं एक ही समय में छोटी संख्या में घटनाओं को उत्पन्न करके अपेक्षाकृत आसानी से SQLite की "डेटाबेस लॉक" त्रुटियों को ट्रिगर कर सकता हूं। इसने मुझे एक परीक्षण कार्यक्रम लिखने के लिए प्रेरित किया जो सबसे खराब केस व्यवहार की नकल करता है और मुझे आश्चर्य हुआ कि ऐसा लगता है कि SQLite इस उपयोग के मामले में कितना खराब प्रदर्शन करता है। नीचे पोस्ट किया गया कोड बस डेटाबेस पर पांच रिकॉर्ड जोड़ता है, पहले अनुक्रमिक रूप से "नियंत्रण" मान प्राप्त करने के लिए। फिर एक ही पांच रिकॉर्ड समवर्ती रूप से जोड़े जाते हैं।एक बहुप्रचारित जावा एप्लिकेशन में SQLite

[java] Sequential DB access: 
[java] SQL Insert completed: 132 
[java] SQL Insert completed: 133 
[java] SQL Insert completed: 151 
[java] SQL Insert completed: 134 
[java] SQL Insert completed: 125 
[java] Concurrent DB access: 
[java] SQL Insert completed: 116 
[java] SQL Insert completed: 1117 
[java] SQL Insert completed: 2119 
[java] SQL Insert failed: 3001 SQLException: java.sql.SQLException: database locked 
[java] SQL Insert completed: 3136 

5 रिकॉर्ड क्रमिक रूप से लगभग 750 मिलीसेकेंड लेता सम्मिलित करना, मैं समवर्ती आवेषण समय का लगभग एक ही राशि लेने के लिए उम्मीद करेंगे:

import java.sql.*; 

public class Main { 
    public static void main(String[] args) throws Exception { 
     Class.forName("org.sqlite.JDBC"); 
     Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db"); 

     Statement stat = conn.createStatement(); 
     stat.executeUpdate("drop table if exists people"); 
     stat.executeUpdate("create table people (name, occupation)"); 
     conn.close(); 

     SqlTask tasks[] = { 
     new SqlTask("Gandhi", "politics"), 
     new SqlTask("Turing", "computers"), 
     new SqlTask("Picaso", "artist"), 
     new SqlTask("shakespeare", "writer"), 
     new SqlTask("tesla", "inventor"), 
     }; 

     System.out.println("Sequential DB access:"); 

     Thread threads[] = new Thread[tasks.length]; 
     for(int i = 0; i < tasks.length; i++) 
     threads[i] = new Thread(tasks[i]); 

     for(int i = 0; i < tasks.length; i++) { 
     threads[i].start(); 
     threads[i].join(); 
     } 

     System.out.println("Concurrent DB access:"); 

     for(int i = 0; i < tasks.length; i++) 
     threads[i] = new Thread(tasks[i]); 

     for(int i = 0; i < tasks.length; i++) 
     threads[i].start(); 

     for(int i = 0; i < tasks.length; i++) 
     threads[i].join(); 
    } 


    private static class SqlTask implements Runnable { 
     String name, occupation; 

     public SqlTask(String name, String occupation) { 
     this.name = name; 
     this.occupation = occupation; 
     } 

     public void run() { 
     Connection conn = null; 
     PreparedStatement prep = null; 
     long startTime = System.currentTimeMillis(); 

     try { 
      try { 
       conn = DriverManager.getConnection("jdbc:sqlite:test.db"); 
       prep = conn.prepareStatement("insert into people values (?, ?)"); 

       prep.setString(1, name); 
       prep.setString(2, occupation); 
       prep.executeUpdate(); 

       long duration = System.currentTimeMillis() - startTime; 
       System.out.println(" SQL Insert completed: " + duration); 
      } 
      finally { 
       if (prep != null) prep.close(); 
       if (conn != null) conn.close(); 
      } 
     } 
     catch(SQLException e) { 
      long duration = System.currentTimeMillis() - startTime; 
      System.out.print(" SQL Insert failed: " + duration); 
      System.out.println(" SQLException: " + e); 
     } 
     } 
    } 
} 

यहाँ जब मैं इस जावा कोड चलाने उत्पादन होता है। लेकिन आप देख सकते हैं कि एक 3 सेकंड टाइमआउट दिया गया है, वे भी खत्म नहीं करते हैं। मैंने एसक्यूएल के मूल लाइब्रेरी कॉल का उपयोग करके सी में एक समान परीक्षण कार्यक्रम भी लिखा था और समवर्ती आवेषणों के साथ-साथ एक साथ एक साथ आवेषण समाप्त हो गया था। तो समस्या मेरी जावा पुस्तकालय के साथ है।

Sequential DB access: 
    SQL Insert completed: 126 milliseconds 
    SQL Insert completed: 126 milliseconds 
    SQL Insert completed: 126 milliseconds 
    SQL Insert completed: 125 milliseconds 
    SQL Insert completed: 126 milliseconds 
Concurrent DB access: 
    SQL Insert completed: 117 milliseconds 
    SQL Insert completed: 294 milliseconds 
    SQL Insert completed: 461 milliseconds 
    SQL Insert completed: 662 milliseconds 
    SQL Insert completed: 862 milliseconds 

मैं दो अलग अलग JDBC ड्राइवर (http://www.zentus.com/sqlitejdbc और http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC), और sqlite4java आवरण के साथ इस कोड की कोशिश की:

यहाँ जब मैं सी संस्करण को चलाने के उत्पादन होता है। हर बार परिणाम समान थे। क्या वहां कोई भी जावा के लिए SQLite लाइब्रेरी के बारे में जानता है जिसके पास यह व्यवहार नहीं है?

उत्तर

23

यह core SQLite library के साथ एक समस्या है - किसी जावा रैपर के साथ नहीं। SQLite प्रक्रियाओं के बीच समवर्ती पहुंच सिंक्रनाइज़ेशन के लिए फाइल सिस्टम-आधारित ताले का उपयोग करता है, क्योंकि एक एम्बेडेड डेटाबेस के रूप में इसमें संचालन शेड्यूल करने के लिए समर्पित प्रक्रिया (सर्वर) नहीं होती है। चूंकि आपके कोड में प्रत्येक थ्रेड डेटाबेस से अपना कनेक्शन बनाता है, इसलिए इसे एक अलग प्रक्रिया के रूप में माना जाता है, सिंक्रनाइज़ेशन फ़ाइल-आधारित ताले के माध्यम से होता है, जो कि किसी अन्य सिंक्रनाइज़ेशन विधि से काफी धीमी होती है।

इसके अतिरिक्त, SQLite प्रति-पंक्ति लॉकिंग (अभी तक?) का समर्थन नहीं करता है। अनिवार्य रूप से संपूर्ण डेटाबेस फ़ाइल प्रत्येक ऑपरेशन के लिए locked बन जाती है। यदि आप भाग्यशाली हैं और आपकी फाइल सिस्टम बाइट-रेंज लॉक का समर्थन करता है, तो एकाधिक पाठकों के साथ-साथ आपके डेटाबेस तक पहुंचने के लिए यह संभव हो सकता है, लेकिन आपको उस तरह के व्यवहार को नहीं मानना ​​चाहिए।

मूल SQLite लाइब्रेरी by default allows multiple threads to use the same connection concurrently कोई समस्या नहीं है। मुझे लगता है कि किसी भी जेनबीसी रैपर जावा प्रोग्राम में उस व्यवहार को भी अनुमति देगा, हालांकि मैंने वास्तव में इसे आजमाया नहीं है।

  • शेयर सभी धागे के बीच एक ही JDBC कनेक्शन:

    इसलिए आप दो समाधान है।

  • SQLite डेवलपर्स के बाद से लगता है कि threads are evil, आप एक धागा अपने सभी डेटाबेस कार्यों को संभालने के लिए और अपने खुद का उपयोग कर जावा कोड पर डीबी कार्यों को क्रमानुसार होने से बेहतर होगा लगता है ...

आप this old question of mine पर एक नज़र डालना चाहते हैं - ऐसा लगता है कि SQLite समय के साथ अद्यतन प्रदर्शन में सुधार करने के लिए कई युक्तियां जमा हुई हैं।

+0

इसलिए यदि समस्या की जड़ फाइल सिस्टम ताले है, तो मेरा सी कोड जितना तेज़ चलता है? – jlunavtgrad

+2

hurray! उसी जेडीबीसी कनेक्शन का उपयोग करके पूरी तरह से समस्या हल हो गई। अब मैं अपने सी कोड से बेहतर या बराबर प्रदर्शन देख रहा हूं। मैंने कल उपरोक्त जावा कार्यक्रम के साथ यह कोशिश की थी, लेकिन अब मुझे एहसास हुआ कि मैं प्रत्येक सम्मिलन के लिए एक नए के साथ अपने कनेक्शन को ओवरराइट कर रहा था। – jlunavtgrad

+0

यह भी देखें: http://stackoverflow.com/questions/24513576/opening-database-connection-with-the-sqlite-open-nomutex-flag-in-java – Stephan

1

मैं एकाधिक धागे के लिए एक ही कनेक्शन का उपयोग करता हूं। इसके अलावा मुझे डीबी-राइट विधियों को सिंक्रनाइज़ करना पड़ा, अन्यथा मुझे अभी भी उबाऊ त्रुटि मिली

संबंधित मुद्दे