2010-05-03 13 views
13

मुझे अपने वेब-एप को वास्तव में विशाल डेटासेट के साथ काम करने की ज़रूरत है। फिलहाल मुझे आउटऑफमेमरी एक्सेप्शन या आउटपुट मिलता है जो 1-2 मिनट उत्पन्न होता है।जेपीए (या कम से कम हाइबरनेट के साथ) के साथ बड़े डेटासेट को कैसे संभालें?

आइए इसे सरल रखें और मान लीजिए कि हमारे पास डीबी में 0 टेबल हैं: Worker और WorkLog पहले 1000 में पंक्तियों और दूसरे में 10 000 000 पंक्तियों के साथ। लेटर टेबल में 'वर्कर्स आईडी' और 'घंटे वर्कड' फ़ील्ड सहित कई फ़ील्ड हैं। हमें क्या चाहिए:

  1. प्रत्येक उपयोगकर्ता द्वारा कुल घंटे की गणना की गणना;

  2. प्रत्येक उपयोगकर्ता के लिए कार्य अवधि की सूची।

सबसे स्पष्ट दृष्टिकोण (IMO) सादा एसक्यूएल में प्रत्येक कार्य के लिए है:

1)

select Worker.name, sum(hoursWorked) from Worker, WorkLog 
    where Worker.id = WorkLog.workerId 
    group by Worker.name; 

//results of this query should be transformed to Multimap<Worker, Long> 

2)

select Worker.name, WorkLog.start, WorkLog.hoursWorked from Worker, WorkLog 
    where Worker.id = WorkLog.workerId; 

//results of this query should be transformed to Multimap<Worker, Period> 
//if it was JDBC then it would be vitally 
//to set resultSet.setFetchSize (someSmallNumber), ~100 

तो, मैं दो प्रश्न हैं :

  1. जेपीए (या कम से कम हाइबरनेट के साथ) के प्रत्येक दृष्टिकोण को कैसे कार्यान्वित करें;
  2. आप इस समस्या को कैसे संभालेंगे (जेपीए या हाइबरनेट के साथ)?
+1

क्या आप एक रिपोर्ट बनाने की कोशिश कर रहे हैं, या आप वस्तुओं का एक गुच्छा लोड करने की कोशिश कर रहे हैं? यदि आप सिर्फ एक रिपोर्ट बनाने की कोशिश कर रहे हैं, तो इसे एसक्यूएल में करें जैसा आपने कहा था और इसके साथ किया जाना चाहिए। – Zak

+0

@Zak: मेरे पास jpa + spring + jsf में एक वेब अनुप्रयोग है जो काम करता है। लेकिन इसका प्रदर्शन बेहतर होना चाहिए। और, और भी महत्वपूर्ण बात यह है कि इस समय इसे संभालने से कहीं अधिक बड़े डेटासेट को संभालने में सक्षम होना चाहिए। 1) पहली क्वेरी के साथ एक समस्या है जिसे मैं नहीं जानता कि इसे 'hql' या' jpa क्वेरी भाषा 'में कैसे लिखना है। मैं सादे वर्ग का उपयोग नहीं करना चाहता, imho यह एक अंतिम उपाय है। 2) दूसरी क्वेरी के साथ समस्या यह है कि मुझे नहीं पता कि 'जेपीए' में fetch आकार कैसे सेट करें और मुझे यह भी नहीं पता कि इस स्थिति को 'जेपीए' के ​​साथ कैसे संभाला जाए: परिणामस्वरूप कोई लूप नहीं है, मैं नहीं करता हूं ' टी 'अगली' fetch लोड करने के बारे में पता नहीं है। – Roman

उत्तर

13

लगता है हम डीबी 2 टेबल है: कार्यकर्ता और WorkLog पहले एक में लगभग 1000 पंक्तियों और इस तरह उच्च मात्रा के लिए एक दूसरे के

में 10 000 000 पंक्तियों के साथ, मेरे सिफारिश हाइबरनेट से The StatelessSession interface उपयोग करने के लिए होगा:

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

StatelessSession session = sessionFactory.openStatelessSession(); 
Transaction tx = session.beginTransaction(); 

ScrollableResults customers = session.getNamedQuery("GetCustomers") 
    .scroll(ScrollMode.FORWARD_ONLY); 
while (customers.next()) { 
    Customer customer = (Customer) customers.get(0); 
    customer.updateStuff(...); 
    session.update(customer); 
} 

tx.commit(); 
session.close(); 

इस कोड उदाहरण में, Customer उदाहरणों क्वेरी द्वारा रिटर्न तुरंत अलग कर रहे हैं। वे कभी भी किसी भी दृढ़ता संदर्भ से जुड़े नहीं हैं।

insert(), update() और delete() संचालन StatelessSession इंटरफ़ेस द्वारा परिभाषित प्रत्यक्ष डेटाबेस पंक्ति-स्तर संचालन माना जाता है। परिणामस्वरूप क्रमशः SQL INSERT, UPDATE या DELETE का तत्काल निष्पादन परिणामस्वरूप होता है। वे save(), saveOrUpdate() और delete() संचालन Session इंटरफ़ेस द्वारा परिभाषित करने के लिए विभिन्न अर्थ विज्ञान है।

+0

@ पास्कल थिवेंट: उत्तर के लिए धन्यवाद! वॉल्यूम्स के बारे में: मुझे असली वॉल्यूम नहीं पता, मैंने अभी अधिकतम निर्दिष्ट किया है (मेरी राय में जो डोमेन के कुछ ज्ञान पर आधारित है)। हो सकता है कि वास्तविक मात्रा 10-100 गुना कम हो और आईएमएचओ इन खंडों के लिए समाधान भी ठीक रहेगा। – Roman

+0

क्या आप जानते हैं कि "स्टेटलेस सत्र डेटा अलियासिंग प्रभावों के प्रति संवेदनशील हैं" के द्वारा उनका क्या मतलब है? धन्यवाद। –

+0

यह किसी भी तरह से तेज़ नहीं है।वास्तव में, यह 'EntityManager' के सामान्य उपयोग की तुलना में ** अत्यंत ** धीमी और कम प्रदर्शन करने वाला है। – Blauhirn

1

कच्चे एसक्यूएल को अंतिम उपाय नहीं माना जाना चाहिए। यदि आप जेपीए स्तर पर चीजें "मानक" रखना चाहते हैं, लेकिन डेटाबेस स्तर पर नहीं, तो इसे अभी भी एक विकल्प माना जाना चाहिए। जेपीए के पास देशी प्रश्नों के लिए भी समर्थन है जहां यह अभी भी आपके लिए मानक संस्थाओं के लिए मैपिंग करेगा।

हालांकि, अगर आप एक बड़े परिणाम सेट है कि डेटाबेस में क्रियान्वित नहीं हो पाता है, तो आप वास्तव में सिर्फ सादा JDBC जेपीए (मानक) के रूप में उपयोग डेटा के बड़े सेट की स्ट्रीमिंग का समर्थन नहीं करता है चाहिए।

यह बंदरगाह के लिए अलग-अलग आवेदन सर्वर पर आपके आवेदन अगर आप जेपीए कार्यान्वयन विशिष्ट निर्माणों का उपयोग के बाद से जेपीए इंजन अनुप्रयोग सर्वर में अंतर्निहित है और आप एक नियंत्रण है जिस पर जेपीए प्रदाता किया जा रहा है नहीं हो सकता है कठिन होगा।

+0

यह। मैंने पाया कि मैन्युअल रूप से कनेक्शन क्वेरी निष्पादित करना ('session.doWork' या ऐसे) वास्तव में सबसे तेज़ है आप – Blauhirn

+0

प्राप्त कर सकते हैं मानक EntityManager में 'doWork' ऑपरेशन नहीं है। –

+0

हां, यही कारण है कि मैंने 'सत्र' लिखा था जिसे आप 'entityManager.unwrap (सत्र.क्लास) के माध्यम से प्राप्त कर सकते हैं;'। आईडीक अगर यह खराब प्रोग्रामिंग शैली हालांकि है। मुझे लगता है कि कोई भी 'sessionFactory' बीन – Blauhirn

0

मैं इस तरह कुछ उपयोग कर रहा हूं और यह बहुत तेज़ काम करता है। मुझे देशी एसक्यूएल का उपयोग करने से भी नफरत है क्योंकि हमारे आवेदन को किसी भी डेटाबेस पर काम करना चाहिए।

फ़ॉलिंग एक बहुत ही अनुकूलित एसक्यूएल में resutls और नक्शे के रिकॉर्ड की सूची लौटाता है।

String hql = "select distinct " + 
      "t.uuid as uuid, t.title as title, t.code as code, t.date as date, t.dueDate as dueDate, " + 
      "t.startDate as startDate, t.endDate as endDate, t.constraintDate as constraintDate, t.closureDate as closureDate, t.creationDate as creationDate, " + 
      "sc.category as category, sp.priority as priority, sd.difficulty as difficulty, t.progress as progress, st.type as type, " + 
      "ss.status as status, ss.color as rowColor, (p.rKey || ' ' || p.name) as project, ps.status as projectstatus, (r.code || ' ' || r.title) as requirement, " + 
      "t.estimate as estimate, w.title as workgroup, o.name || ' ' || o.surname as owner, " + 
      "ROUND(sum(COALESCE(a.duration, 0)) * 100/case when ((COALESCE(t.estimate, 0) * COALESCE(t.progress, 0)) = 0) then 1 else (COALESCE(t.estimate, 0) * COALESCE(t.progress, 0)) end, 2) as factor " + 
      "from " + Task.class.getName() + " t " + 
      "left join t.category sc " + 
      "left join t.priority sp " + 
      "left join t.difficulty sd " + 
      "left join t.taskType st " + 
      "left join t.status ss " + 
      "left join t.project p " + 
      "left join t.owner o " + 
      "left join t.workgroup w " + 
      "left join p.status ps " + 
      "left join t.requirement r " + 
      "left join p.status sps " + 
      "left join t.iterationTasks it " + 
      "left join t.taskActivities a " + 
      "left join it.iteration i " + 
      "where sps.active = true and " + 
      "ss.done = false and " + 
      "(i.uuid <> :iterationUuid or it.uuid is null) " + filterHql + 
      "group by t.uuid, t.title, t.code, t.date, t.dueDate, " + 
      "t.startDate, t.endDate, t.constraintDate, t.closureDate, t.creationDate, " + 
      "sc.category, sp.priority, sd.difficulty, t.progress, st.type, " + 
      "ss.status, ss.color, p.rKey, p.name, ps.status, r.code, r.title, " + 
      "t.estimate, w.title, o.name, o.surname " + sortHql; 

    if (logger.isDebugEnabled()) { 
     logger.debug("Executing hql: " + hql); 
    } 

    Query query = hibernateTemplate.getSessionFactory().getCurrentSession().getSession(EntityMode.MAP).createQuery(hql); 
    for(String key: filterValues.keySet()) { 
     Object valueSet = filterValues.get(key); 

     if (logger.isDebugEnabled()) { 
      logger.debug("Setting query parameter for " + key); 
     } 

     if (valueSet instanceof java.util.Collection<?>) { 
      query.setParameterList(key, (Collection)filterValues.get(key)); 
     } else { 
      query.setParameter(key, filterValues.get(key)); 
     } 
    }  
    query.setString("iterationUuid", iteration.getUuid()); 
    query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); 

    if (logger.isDebugEnabled()) { 
     logger.debug("Query building complete."); 
     logger.debug("SQL: " + query.getQueryString()); 
    } 

    return query.list(); 
+0

अनुकूलित? क्या आप इसे समझा सकते हैं? – nalply

0

मैं मानता हूँ कि डेटाबेस सर्वर पर गणना कर विशेष मामले आप का उल्लेख में आपका सबसे अच्छा विकल्प है।

select w, sum(wl.hoursWorked) 
from Worker w, WorkLog wl 
where w.id = wl.workerId 
group by w 

1) या, यदि संघ मैप किया गया है: HQL और JPAQL उन प्रश्नों के दोनों संभाल कर सकते हैं

select w, sum(wl.hoursWorked) 
from Worker w join w.workLogs wl 
group by w 

दोनों या जो लौट आप सूची जहां वस्तु [] रों श्रमिक और लंबे हैं। या फिर आप यह भी कहा कि रैप करने के लिए ऊपर उदाहरण के लिए, "गतिशील इन्स्टेन्शियशन" क्वेरी इस्तेमाल कर सकते हैं:

select new WorkerTotal(select w, sum(wl.hoursWorked)) 
from Worker w join w.workLogs wl 
group by w 

या (जरूरत के आधार पर) शायद यहां तक ​​कि बस:

select new WorkerTotal(select w.id, w.name, sum(wl.hoursWorked)) 
from Worker w join w.workLogs wl 
group by w.id, w.name 

WorkerTotal सिर्फ एक सादे वर्ग है। इसमें मिलान करने वाले कन्स्ट्रक्टर होना चाहिए।

2)

select w, new Period(wl.start, wl.hoursWorked) 
from Worker w join w.workLogs wl 

यह आप WorkLog तालिका में प्रत्येक पंक्ति के लिए एक परिणाम वापस आ जाएगी ... new Period(...) बिट "गतिशील इन्स्टेन्शियशन" कहा जाता है और वस्तुओं में परिणाम से tuples रैप करने के लिए (प्रयोग किया जाता है आसान खपत)।

हेरफेर और सामान्य उपयोग के लिए, मैं स्टेटलेस सत्र को पास्कल पॉइंट के रूप में अनुशंसा करता हूं।

0

वहाँ कई तकनीकों है कि जहां स्मृति एक सीमा है बना सकते हैं और बड़े डेटा सेट के लिए प्रश्नों में हेरफेर करने के एक दूसरे के साथ संयोजन के रूप में इस्तेमाल किया जा करना पड़ सकता है कर रहे हैं:

  1. उपयोग setFetchSize (कुछ मूल्य है, शायद 100 +) डिफ़ॉल्ट के रूप में (जेडीबीसी के माध्यम से) 10. यह प्रदर्शन के बारे में अधिक है और इसका सबसे बड़ा संबंधित कारक है। प्रदाता (हाइबरनेट, आदि) से उपलब्ध क्वेरीहिंट का उपयोग करके जेपीए में किया जा सकता है। ऐसा नहीं है (जो भी कारण है) एक जेपीए Query.setFetchSize(int) विधि प्रतीत होता है।
  2. 10K + रिकॉर्ड्स के लिए पूरे परिणाम-सेट को मार्शल करने का प्रयास न करें। कई रणनीतियों लागू होती हैं: जीयूआई के लिए, पेजिंग या एक फ्रेमवर्क का उपयोग करें जो पेजिंग करता है। लुसीन या वाणिज्यिक खोज/अनुक्रमण इंजन पर विचार करें (अगर कंपनी के पास पैसा है तो एंडेका)। कहीं भी डेटा भेजने के लिए, इसे स्ट्रीम करें और प्रत्येक एन रिकॉर्ड को बफर फ्लश करें ताकि कितनी मेमोरी का उपयोग किया जा सके। स्ट्रीम को फ़ाइल, नेटवर्क इत्यादि में फहराया जा सकता है। याद रखें कि जेपीए जेडीबीसी का उपयोग करता है और जेडीबीसी सर्वर पर परिणाम-सेट रखता है, केवल एक पंक्ति में सेट-सेट समूह में एन-पंक्तियां लाता है। समूहों में फ्लशिंग डेटा को सुविधाजनक बनाने के लिए इस ब्रेक-डाउन का उपयोग किया जा सकता है।
  3. विचार करें कि केस-केस क्या है। आम तौर पर, एक आवेदन सवालों के जवाब देने की कोशिश कर रहा है। जब उत्तर 10 के + पंक्तियों के माध्यम से खरपतवार होता है, तो डिजाइन की समीक्षा की जानी चाहिए। फिर, Lucene की तरह अनुक्रमण इंजन का उपयोग कर, क्वेरी को परिशोधित करने पर विचार, डेटाबेस के लिए जा रहा बिना रूप BloomFilters का उपयोग कर शामिल जांच कैश haystacks में सुई खोजने के लिए विचार करना आदि
3

ऐसा लगता है आप भी EclipseLink साथ ऐसा कर सकते । चेक करें: http://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination:

Query query = em.createQuery... 
query.setHint(QueryHints.CURSOR, true) 
    .setHint(QueryHints.SCROLLABLE_CURSOR, true) 
ScrollableCursor scrl = (ScrollableCursor)q.getSingleResult(); 
Object o = null; 
while ((o = scrl.next()) != null) { ... } 
+0

setHint विधि shoing अपरिभाषित। –

2

यह blog post भी कर सकते हैं। यह स्टेटलेस सत्र के साथ दृष्टिकोण को सारांशित करता है और कुछ अतिरिक्त संकेत जोड़ता है, उदा। जेएक्स-आरएस के साथ परिणाम कैसे स्ट्रीम करें।

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