2012-07-09 16 views
23

मैं MySQL डेटाबेस डिज़ाइन कर रहा हूं जिसे विभिन्न InnoDB तालिकाओं में प्रति सेकंड लगभग 600 पंक्ति आवेषणों को संभालने की आवश्यकता है। मेरा वर्तमान कार्यान्वयन गैर-समर्थित तैयार बयान का उपयोग करता है। हालांकि, MySQL डेटाबेस बाधाओं को लिखना और समय के साथ मेरी कतार का आकार बढ़ता है।जावा में MySQL सम्मिलित कथन का प्रदर्शन: बैच मोड कई मानों के साथ एकल कथन बनाम कथन बनाम

कार्यान्वयन जावा में लिखा गया है, मुझे हाथ से संस्करण नहीं पता है। यह MySQL के Java connector का उपयोग करता है। मुझे कल JDBC पर स्विच करने की आवश्यकता है। मुझे लगता है कि ये दो अलग कनेक्टर पैकेज हैं।

मैं इस मुद्दे पर निम्नलिखित धागे पढ़ा है:

और mysql साइट से:

मेरे प्रश्न हैं:

  • है किसी को भी सलाह या एक से अधिक मान के साथ एक एकल INSERT कथन का उपयोग बनाम बैच मोड में तैयार बयान के साथ आवेषण का उपयोग कर प्रदर्शन मतभेदों पर अनुभव है।

  • MySQL जावा कनेक्टर बनाम JDBC के बीच प्रदर्शन अंतर क्या हैं। क्या मुझे एक या दूसरे का उपयोग करना चाहिए?

  • तालिकाएं संग्रह उद्देश्यों के लिए हैं, और ~ 9 0% ~ 10% पढ़ने के लिए लिख सकते हैं (शायद कम भी)। मैं InnoDB का उपयोग कर रहा हूँ। क्या यह मायिसम पर सही विकल्प है?

आपकी मदद के लिए अग्रिम धन्यवाद।

+0

अच्छी तरह से, आप एकल ऑपरेशन में यह ऑपरेशन करेंगे। दूसरे मामले में आप प्रति पंक्ति सम्मिलन लेनदेन होगा। –

+0

शायद इस सवाल के लिए dba.stackexchange एक बेहतर जगह होगी। आपके द्वारा पहले से किए गए शोध और प्रयासों के लिए –

+0

+1 हालांकि यह आपकी पहली पोस्ट है। –

उत्तर

27

JDBC बस मानक इंटरफेस की पेशकश ताकि आप वास्तव में एक विशिष्ट JDBC कार्यान्वयन करने के लिए बाध्य नहीं कर रहे हैं डेटाबेस का उपयोग की एक जावा SE मानक है। MySQL जावा कनेक्टर (कनेक्टर/जे) केवल MySQL डेटाबेस के लिए जेडीबीसी इंटरफेस का कार्यान्वयन है। अनुभव से बाहर, मैं एक ऐसी परियोजना में शामिल हूं जो MySQL का उपयोग करके बड़ी मात्रा में डेटा का उपयोग करता है, और हम मुख्य रूप से डेटा के लिए माईसाम पसंद करते हैं जो उत्पन्न किया जा सकता है: यह लेनदेन खोने के उच्च प्रदर्शन को प्राप्त करने की अनुमति देता है, लेकिन आम तौर पर बोलते हुए, माईसाम तेज़ है, लेकिन InnoDB अधिक विश्वसनीय है।

मुझे एक साल पहले भी आईएनएसईआरटी कथन के प्रदर्शन के लिए आश्चर्य हुआ, और मेरे कोड शेल्फ में निम्नलिखित पुराने परीक्षण कोड को मिला (क्षमा करें, यह थोड़ा जटिल है और आपके प्रश्न के दायरे से थोड़ा सा है)। नीचे दिए गए कोड परीक्षण डाटा डालने के 4 तरीके के उदाहरण हैं:

  • एकलINSERT रों;
  • INSERT एस;
  • मैनुअल थोकINSERT (इसका उपयोग कभी नहीं करें - यह खतरनाक है);
  • और अंत में तैयार थोकINSERT)।

यह धावक के रूप में TestNG का उपयोग करता है, और इस तरह कुछ कस्टम कोड विरासत का उपयोग करता है:

  • runWithConnection() विधि - सुनिश्चित करता है कि कनेक्शन बंद कर दिया या कनेक्शन पूल में वापस डाल दिया है के बाद कॉलबैक निष्पादित किया जाता है (लेकिन नीचे दिया गया कोड कथन बंद करने की विश्वसनीय रणनीति का उपयोग नहीं करता है - कोड को कम करने के लिए try/finally के बिना भी);
  • IUnsafeIn<T, E extends Throwable> - एकल पैरामीटर को स्वीकार करने के तरीकों के लिए एक कस्टम कॉलबैक इंटरफ़ेस, लेकिन संभावित रूप से प्रकार ई के अपवाद को फेंकना, जैसे: void handle(T argument) throws E;
package test; 

import test.IUnsafeIn; 

import java.sql.Connection; 
import java.sql.PreparedStatement; 
import java.sql.SQLException; 

import static java.lang.String.format; 
import static java.lang.String.valueOf; 
import static java.lang.System.currentTimeMillis; 

import core.SqlBaseTest; 
import org.testng.annotations.AfterSuite; 
import org.testng.annotations.BeforeSuite; 
import org.testng.annotations.BeforeTest; 
import org.testng.annotations.Test; 

public final class InsertVsBatchInsertTest extends SqlBaseTest { 

    private static final int ITERATION_COUNT = 3000; 

    private static final String CREATE_TABLE_QUERY = "CREATE TABLE IF NOT EXISTS ttt1 (c1 INTEGER, c2 FLOAT, c3 VARCHAR(5)) ENGINE = InnoDB"; 
    private static final String DROP_TABLE_QUERY = "DROP TABLE ttt1"; 
    private static final String CLEAR_TABLE_QUERY = "DELETE FROM ttt1"; 

    private static void withinTimer(String name, Runnable runnable) { 
     final long start = currentTimeMillis(); 
     runnable.run(); 
     logStdOutF("%20s: %d ms", name, currentTimeMillis() - start); 
    } 

    @BeforeSuite 
    public void createTable() { 
     runWithConnection(new IUnsafeIn<Connection, SQLException>() { 
      @Override 
      public void handle(Connection connection) throws SQLException { 
       final PreparedStatement statement = connection.prepareStatement(CREATE_TABLE_QUERY); 
       statement.execute(); 
       statement.close(); 
      } 
     }); 
    } 

    @AfterSuite 
    public void dropTable() { 
     runWithConnection(new IUnsafeIn<Connection, SQLException>() { 
      @Override 
      public void handle(Connection connection) throws SQLException { 
       final PreparedStatement statement = connection.prepareStatement(DROP_TABLE_QUERY); 
       statement.execute(); 
       statement.close(); 
      } 
     }); 
    } 

    @BeforeTest 
    public void clearTestTable() { 
     runWithConnection(new IUnsafeIn<Connection, SQLException>() { 
      @Override 
      public void handle(Connection connection) throws SQLException { 
       final PreparedStatement statement = connection.prepareStatement(CLEAR_TABLE_QUERY); 
       statement.execute(); 
       statement.close(); 
      } 
     }); 
    } 

    @Test 
    public void run1SingleInserts() { 
     withinTimer("Single inserts", new Runnable() { 
      @Override 
      public void run() { 
       runWithConnection(new IUnsafeIn<Connection, SQLException>() { 
        @Override 
        public void handle(Connection connection) throws SQLException { 
         for (int i = 0; i < ITERATION_COUNT; i++) { 
          final PreparedStatement statement = connection.prepareStatement("INSERT INTO ttt1 (c1, c2, c3) VALUES (?, ?, ?)"); 
          statement.setInt(1, i); 
          statement.setFloat(2, i); 
          statement.setString(3, valueOf(i)); 
          statement.execute(); 
          statement.close(); 
         } 
        } 
       }); 
      } 
     }); 
    } 

    @Test 
    public void run2BatchInsert() { 
     withinTimer("Batch insert", new Runnable() { 
      @Override 
      public void run() { 
       runWithConnection(new IUnsafeIn<Connection, SQLException>() { 
        @Override 
        public void handle(Connection connection) throws SQLException { 
         final PreparedStatement statement = connection.prepareStatement("INSERT INTO ttt1 (c1, c2, c3) VALUES (?, ?, ?)"); 
         for (int i = 0; i < ITERATION_COUNT; i++) { 
          statement.setInt(1, i); 
          statement.setFloat(2, i); 
          statement.setString(3, valueOf(i)); 
          statement.addBatch(); 
         } 
         statement.executeBatch(); 
         statement.close(); 
        } 
       }); 
      } 
     }); 
    } 

    @Test 
    public void run3DirtyBulkInsert() { 
     withinTimer("Dirty bulk insert", new Runnable() { 
      @Override 
      public void run() { 
       runWithConnection(new IUnsafeIn<Connection, SQLException>() { 
        @Override 
        public void handle(Connection connection) throws SQLException { 
         final StringBuilder builder = new StringBuilder("INSERT INTO ttt1 (c1, c2, c3) VALUES "); 
         for (int i = 0; i < ITERATION_COUNT; i++) { 
          if (i != 0) { 
           builder.append(","); 
          } 
          builder.append(format("(%s, %s, '%s')", i, i, i)); 
         } 
         final String query = builder.toString(); 
         final PreparedStatement statement = connection.prepareStatement(query); 
         statement.execute(); 
         statement.close(); 
        } 
       }); 
      } 
     }); 
    } 

    @Test 
    public void run4SafeBulkInsert() { 
     withinTimer("Safe bulk insert", new Runnable() { 
      @Override 
      public void run() { 
       runWithConnection(new IUnsafeIn<Connection, SQLException>() { 
        private String getInsertPlaceholders(int placeholderCount) { 
         final StringBuilder builder = new StringBuilder("("); 
         for (int i = 0; i < placeholderCount; i++) { 
          if (i != 0) { 
           builder.append(","); 
          } 
          builder.append("?"); 
         } 
         return builder.append(")").toString(); 
        } 

        @SuppressWarnings("AssignmentToForLoopParameter") 
        @Override 
        public void handle(Connection connection) throws SQLException { 
         final int columnCount = 3; 
         final StringBuilder builder = new StringBuilder("INSERT INTO ttt1 (c1, c2, c3) VALUES "); 
         final String placeholders = getInsertPlaceholders(columnCount); 
         for (int i = 0; i < ITERATION_COUNT; i++) { 
          if (i != 0) { 
           builder.append(","); 
          } 
          builder.append(placeholders); 
         } 
         final int maxParameterIndex = ITERATION_COUNT * columnCount; 
         final String query = builder.toString(); 
         final PreparedStatement statement = connection.prepareStatement(query); 
         int valueIndex = 0; 
         for (int parameterIndex = 1; parameterIndex <= maxParameterIndex; valueIndex++) { 
          statement.setObject(parameterIndex++, valueIndex); 
          statement.setObject(parameterIndex++, valueIndex); 
          statement.setObject(parameterIndex++, valueIndex); 
         } 
         statement.execute(); 
         statement.close(); 
        } 
       }); 
      } 
     }); 
    } 

} 

तरीकों @Test एनोटेशन के साथ एनोटेट पर एक नज़र डालें: वे वास्तव में INSERT स्टेटमेंट्स को निष्पादित। MySQL 5.5 स्थापित (MySQL कनेक्टर/जम्मू 5.1.12) के साथ स्रोत कोड में मेरी मशीन पर निम्नलिखित परिणाम उत्पादन InnoDB का उपयोग करता है: के CREATE_TABLE_QUERY निरंतर पर एक नज़र डालें तो कृपया

InnoDB 
Single inserts: 74148 ms 
Batch insert: 84370 ms 
Dirty bulk insert: 178 ms 
Safe bulk insert: 118 ms 

आप CREATE_TABLE_QUERY को बदलते हैं InnoDB से MyISAM, आपको महत्वपूर्ण प्रदर्शन वृद्धि दिखाई देगी:

MyISAM 
Single inserts: 604 ms 
Batch insert: 447 ms 
Dirty bulk insert: 63 ms 
Safe bulk insert: 26 ms 

आशा है कि इससे मदद मिलती है।

युपीडी:

4 जिस तरह से आप ठीक से इतना बड़ा सच में बड़ी पैकेट का समर्थन करने के होने की max_allowed_packetmysql.ini में ([mysqld] अनुभाग) को कस्टमाइज़ करना होगा के लिए।

+0

बेंचमार्क के लिए धन्यवाद, यह सबसे सरल जवाब था जिसे मैं पूछ सकता था। मैंने आज तैयार किए गए आवेषणों को लागू किया और यह एक आकर्षण की तरह काम किया! – Darren

+0

आपका स्वागत है। :) –

+4

कोई विचार क्यों बैच सम्मिलन InnoDB पर एकल आवेषण से धीमा है? – stracktracer

1

क्या आपके पास किसी भी प्रभावित टेबल पर कोई ट्रिगर है? यदि नहीं, प्रति सेकेंड 600 आवेषण बहुत पसंद नहीं करते हैं।

जेडीबीसी से बैच सम्मिलित कार्यक्षमता उसी लेनदेन में एक ही वक्तव्य जारी करेगी, जबकि बहु-मूल्य एसक्यूएल एक ही कथन में सभी मानों को निचोड़ देगा। बहु-मूल्य विवरण के मामले में, आपको डालने वाले एसक्यूएल को गतिशील रूप से बनाना होगा और यह अधिक कोड, अधिक मेमोरी, एसक्यूएल इंजेक्शन सुरक्षा तंत्र इत्यादि के मामले में ओवरहेड हो सकता है। अपने वर्कलोड के लिए पहले नियमित बैच कार्यक्षमता आज़माएं, एक समस्या नहीं होनी चाहिए।

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

यदि आप निर्माता को सफल सम्मिलन के बारे में सूचित करना चाहते हैं, तो कुछ और नलसाजी की आवश्यकता होती है।

कभी-कभी थ्रेड पर अवरुद्ध करना अधिक सीधे और व्यावहारिक हो सकता है।

if(System.currentTimeMills()-lastInsertTime>TIME_THRESHOLD || queue.size()>SIZE_THRESHOLD) { 
    lastInsertTime=System.currentTimeMills(); 
    // Insert logic 
    } else { 
    // Do nothing OR sleep for some time OR retry after some time. 
    } 
+0

आपकी सलाह के लिए धन्यवाद। मैंने आज कुछ शोध किया और एक प्राथमिक निर्माता-उपभोक्ता संबंध बनाया। मेरा डेटा प्रोसेसर एक थ्रेड में काम करता है, mysql इन्सेट थ्रेड से संबंधित कतार में जानकारी जोड़ता है। ऐसा लगता है कि यह अच्छी तरह से काम करता है। मैं innodb का उपयोग कर रहा था क्योंकि कुछ महत्वपूर्ण विदेशी महत्वपूर्ण संबंध थे जो मैं रखने की कोशिश करने जा रहा था। ऐसा लगता है कि वे वास्तव में चीजों की योजना में आवश्यक नहीं हो सकते हैं, इसलिए मैं कल अपने आईआईएसएएम पर स्विच कर सकता हूं और देख सकता हूं कि चीजें कैसे जाती हैं। – Darren

8

मुझे पता है कि यह धागा बहुत पुराना है, लेकिन मैंने सोचा कि मैं उल्लेख करता हूं कि यदि आप mysql का उपयोग करते समय jdbc url में "rewriteBatchedStatements = true" जोड़ते हैं, तो इसके परिणामस्वरूप बैच किए गए कथन का उपयोग करते समय भारी प्रदर्शन लाभ हो सकता है। बैच डालने का उपयोग करते समय

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