ওআরএম (অবজেক্ট-রিলেশনাল ম্যাপিং) এ "এন + 1 সমস্যা নির্বাচন করে" কী?


1595

"এন + 1 সমস্যা সমাধান করে" সাধারণত অবজেক্ট-রিলেশনাল ম্যাপিং (ওআরএম) আলোচনায় একটি সমস্যা হিসাবে বর্ণনা করা হয় এবং আমি বুঝতে পারি যে বস্তুর মধ্যে সহজ বলে মনে হচ্ছে এমন কিছুর জন্য অনেকগুলি ডাটাবেস অনুসন্ধান করার সাথে এর কিছু করার আছে understand দুনিয়া।

কারও কি সমস্যার আরও বিস্তারিত ব্যাখ্যা আছে?


2
এন +1 সমস্যা বোঝার জন্য দুর্দান্ত ব্যাখ্যা সহ এটি একটি দুর্দান্ত লিঙ্ক । : এটা এই সমস্যা পাল্টা সমাধান জুড়ে architects.dzone.com/articles/how-identify-and-resilve-n1
ACeS ঘটনাও ঘটে।


এই সমস্যার সমাধান খুঁজছেন প্রত্যেকের জন্য, আমি এটি বর্ণনা করে একটি পোস্ট পেয়েছি। stackoverflow.com/questions/32453989/...
damndemon

2
উত্তরগুলি বিবেচনা করে, এগুলি 1+ N সমস্যা হিসাবে চিহ্নিত করা উচিত নয়? যেহেতু এটি একটি পরিভাষা বলে মনে হচ্ছে, আমি বিশেষত ওপি জিজ্ঞাসা করছি না।
ব্যবহারকারী 1418717

উত্তর:


1013

ধরা যাক আপনার কাছে Carঅবজেক্টের সংকলন (ডাটাবেস সারি) রয়েছে এবং প্রত্যেকের Carকাছে Wheelবস্তুর সংগ্রহ রয়েছে (সারিগুলিও)। অন্য কথায়, CarWheelএকটি 1-থেকে-বহু সম্পর্ক।

এখন, আসুন আমরা আপনাকে সমস্ত গাড়ির মাধ্যমে পুনরাবৃত্তি করতে হবে এবং প্রতিটিটির জন্য, চাকার একটি তালিকা মুদ্রণ করতে হবে। নিরীহ ও / আর বাস্তবায়ন নিম্নলিখিতগুলি করবে:

SELECT * FROM Cars;

এবং তারপরে প্রতিটি জন্য Car:

SELECT * FROM Wheel WHERE CarId = ?

অন্য কথায়, আপনার গাড়িগুলির জন্য একটি নির্বাচন করুন এবং তারপরে এন অতিরিক্ত নির্বাচিত হন, যেখানে মোট মোট গাড়ি সংখ্যা এন।

বিকল্পভাবে, কেউ সমস্ত চাকা পেতে পারে এবং স্মৃতিতে লুকিংগুলি সম্পাদন করতে পারে:

SELECT * FROM Wheel

এটি N + 1 থেকে 2 এ ডাটাবেসে রাউন্ড-ট্রিপের সংখ্যা হ্রাস করে Most

তথ্যসূত্র: হাইবারনেট সহ জাভা অধ্যবসায় , অধ্যায় 13।


139
"এটি খারাপ" সম্পর্কে স্পষ্ট করতে - আপনি SELECT * from Wheel;N + 1 এর পরিবর্তে 1 টি নির্বাচন ( ) দিয়ে সমস্ত চাকা পেতে পারেন । একটি বৃহত এন সঙ্গে, পারফরম্যান্স হিট খুব তাৎপর্যপূর্ণ হতে পারে।
tucuxi

211
@ টুকুসি আমি অবাক হয়েছি আপনি ভুল হওয়ার জন্য অনেকগুলি উত্স পেয়েছিলেন। সূচী সম্পর্কে একটি ডাটাবেস খুব ভাল, একটি নির্দিষ্ট কারিডের জন্য ক্যোয়ারী করা খুব দ্রুত ফিরে আসবে। তবে যদি আপনি সমস্ত চাকা একবারে পেয়ে থাকেন তবে আপনাকে আপনার অ্যাপ্লিকেশনটিতে ক্যারিডের সন্ধান করতে হবে, যা সূচিযুক্ত নয়, এটি ধীর। আপনার ডাটাবেসে পৌঁছানোর ক্ষেত্রে বড় অলসতার সমস্যা না থাকলে n + 1 আসলেই দ্রুত - এবং হ্যাঁ, আমি এটিকে রিয়েল ওয়ার্ল্ডের বিভিন্ন ধরণের কোডের সাথে বেঞ্চমার্ক করেছি।
অ্যারিল

73
@ariel 'সঠিক' উপায়টি হ'ল CarId (1 টি নির্বাচিত) দ্বারা আদেশযুক্ত সমস্ত চাকা পাওয়া, এবং যদি CarId এর চেয়ে আরও বিশদ প্রয়োজন হয় তবে সমস্ত গাড়ির জন্য দ্বিতীয় জিজ্ঞাসা করুন (মোট 2 টি ক্যোয়ারী)। জিনিসপত্র মুদ্রণ করা এখন সর্বোত্তম, এবং কোনও সূচী বা গৌণ স্টোরেজ প্রয়োজন হয়নি (আপনি ফলাফলগুলি নিয়ে পুনরাবৃত্তি করতে পারেন, সেগুলি ডাউনলোড করার দরকার নেই)। আপনি ভুল জিনিসটি বেঞ্চমার্ক করেছেন। আপনি যদি এখনও নিজের মানদণ্ডের বিষয়ে আত্মবিশ্বাসী হন, আপনি কি নিজের পরীক্ষা এবং ফলাফলগুলি ব্যাখ্যা করে একটি দীর্ঘ মন্তব্য (বা একটি সম্পূর্ণ উত্তর) পোস্ট করতে আপত্তি করবেন?
tucuxi

92
"হাইবারনেট (আমি অন্যান্য ওআরএম ফ্রেমওয়ার্কগুলির সাথে পরিচিত নই) আপনাকে এটি পরিচালনা করার বিভিন্ন উপায় দেয়" " এবং এই উপায় হয়?
টিমা

58
@ অ্যারিল আলাদা মেশিনে ডাটাবেস এবং অ্যাপ্লিকেশন সার্ভারের সাহায্যে আপনার মানদণ্ডগুলি চালানোর চেষ্টা করুন। আমার অভিজ্ঞতা হিসাবে, ডাটাবেসের রাউন্ড ট্রিপগুলি নিজের প্রশ্নের চেয়ে ওভারহেডে বেশি ব্যয় করে। হ্যাঁ, অনুসন্ধানগুলি সত্যই দ্রুত, তবে এটি হ'ল রাউন্ড ট্রিপস w আমি "WHERE ID = Const " কে "WHERE ID IN ( Const , Const , ...)" এ রূপান্তর করেছি এবং এর থেকে প্রসারিত অর্ডারের অর্ডার বৃদ্ধি পেয়েছে।
হ্যান্স

110
SELECT 
table1.*
, table2.*
INNER JOIN table2 ON table2.SomeFkId = table1.SomeId

এটি আপনাকে ফলাফল সেট করে যেখানে টেবিল 2-এ থাকা শিশু সারিগুলি টেবিল 2-এ প্রতিটি সন্তানের সারণির জন্য টেবিল 1 ফলাফলগুলি ফিরে আসার দ্বারা নকল সৃষ্টি করে। ও / আর ম্যাপারদের একটি অনন্য কী ক্ষেত্রের উপর ভিত্তি করে টেবিল 1 উদাহরণগুলি পৃথক করতে হবে, তারপরে শিশুদের দৃষ্টান্তগুলি জনপ্রিয় করতে সমস্ত টেবিল 2 কলাম ব্যবহার করুন।

SELECT table1.*

SELECT table2.* WHERE SomeFkId = #

N + 1 হল যেখানে প্রথম ক্যোয়ারী প্রাথমিক অবজেক্টকে পপুলেট করে এবং দ্বিতীয় ক্যোয়ারী প্রত্যাবর্তিত প্রতিটি অনন্য প্রাথমিক অবজেক্টের জন্য সমস্ত শিশু অবজেক্টকে পপুলেট করে।

বিবেচনা:

class House
{
    int Id { get; set; }
    string Address { get; set; }
    Person[] Inhabitants { get; set; }
}

class Person
{
    string Name { get; set; }
    int HouseId { get; set; }
}

এবং একই কাঠামো সহ টেবিলগুলি। "22 ভ্যালি সেন্ট" ঠিকানার জন্য একটি একক প্রশ্ন ফিরে আসতে পারে:

Id Address      Name HouseId
1  22 Valley St Dave 1
1  22 Valley St John 1
1  22 Valley St Mike 1

ও / আরএমের আইডি = 1, ঠিকানা = "22 ভ্যালি সেন্ট" সহ বাড়ির একটি উদাহরণ পূরণ করা উচিত এবং তারপরে কেবল একটি প্রশ্নের সাথে ডেভ, জন এবং মাইকের জন্য দৃষ্টান্তের সাথে বাসিন্দাদের অ্যারেটি তৈরি করুন।

উপরে ব্যবহৃত একই ঠিকানার জন্য একটি এন + 1 ক্যোয়ারীর ফলাফল হবে:

Id Address
1  22 Valley St

মত একটি পৃথক ক্যোয়ারী সহ

SELECT * FROM Person WHERE HouseId = 1

এবং এর মতো একটি পৃথক ডেটা সেট তৈরি হয়

Name    HouseId
Dave    1
John    1
Mike    1

এবং চূড়ান্ত ফলাফল একক ক্যোয়ারির সাথে উপরের মত একই।

একক নির্বাচনের সুবিধা হ'ল আপনি সমস্ত ডেটা সামনে রেখে যা আপনি শেষ পর্যন্ত যা চান তা হতে পারে। এন + 1 এর সুবিধাগুলি হ'ল ক্যোয়ারী জটিলতা হ্রাস পেয়েছে এবং আপনি অলস লোডিং ব্যবহার করতে পারেন যেখানে সন্তানের ফলাফলের সেটগুলি কেবল প্রথম অনুরোধের পরে লোড করা হয়।


4
এন + 1 এর অন্যান্য সুবিধাটি এটি দ্রুত হয় কারণ ডাটাবেসগুলি সরাসরি কোনও সূচক থেকে ফলাফলগুলি ফিরিয়ে দিতে পারে। যোগদানের কাজটি করার পরে এবং বাছাইয়ের জন্য একটি টেম্প টেবিলের প্রয়োজন, যা ধীর। আপনার ডাটাবেসে যদি অনেক বেশি বিলম্ব হয় তবেই n + 1 এড়ানোর একমাত্র কারণ।
অ্যারিল

17
যোগদান এবং বাছাই করা বেশ দ্রুত হতে পারে (কারণ আপনি সূচকযুক্ত এবং সম্ভবত-বাছাই করা ক্ষেত্রগুলিতে যোগদান করবেন)। আপনার 'এন + 1' কত বড়? আপনি কি গুরুত্ব সহকারে বিশ্বাস করেন যে এন + 1 সমস্যাটি কেবলমাত্র উচ্চ-ল্যাটেন্সি ডাটাবেস সংযোগগুলিতে প্রযোজ্য?
tucuxi

9
@ অরিয়েল - আপনার মাপদণ্ড সঠিক হতে পারে যদিও, আপনার পরামর্শ যে এন +1 "দ্রুততম" ভুল is কীভাবে সম্ভব? এই এন.ইউইকিপিডিয়া.org / উইকি / অ্যানিকডোটাল_সিভিডেন্স এবং এই প্রশ্নের অন্যান্য উত্তরে আমার মন্তব্য দেখুন ।
হিটনিল্যান্ড

7
@ অ্যারিল - আমি মনে করি আমি এটি ঠিকঠাক বুঝতে পেরেছি :)। আমি কেবল এটি উল্লেখ করার চেষ্টা করছি যে আপনার ফলাফলটি কেবলমাত্র শর্তের একটি সেটে প্রযোজ্য। আমি খুব সহজেই বিপরীত দেখিয়ে এমন একটি পাল্টা উদাহরণ তৈরি করতে পারি। যে জানার জন্য?
হিটনিল্যান্ড

13
পুনরাবৃত্তি করার জন্য, সিলেক্ট এন + 1 সমস্যাটি এর মূল বিষয়: আমার পুনরুদ্ধার করার জন্য 600 রেকর্ড রয়েছে। তাদের সকলকে একটি ক্যোয়ারীতে পাওয়া 600 বা ক্যারিয়ারে একবারে 1 পেতে কী দ্রুত? আপনি মাইআইএসএমে না থাকলে এবং / অথবা আপনার কাছে দুর্বল স্বাভাবিক / দুর্বল সূচকযুক্ত স্কিমা না থাকে (যার ক্ষেত্রে ওআরএম সমস্যা হয় না), সঠিকভাবে সুরক্ষিত ডিবি স্বতন্ত্র সারিগুলিতে ফিরে আসার সময় 2 এমএসে 600 টি সারি ফিরিয়ে দেবে প্রায় 1 এমএস প্রতি। সুতরাং আমরা প্রায়শই দেখতে পাই যে এন + 1 কয়েক মিলি
কুকুর

64

পণ্যের সাথে একের সাথে একাধিক সম্পর্কের সরবরাহকারী। এক সরবরাহকারীর অনেক পণ্য রয়েছে (সরবরাহ)।

***** Table: Supplier *****
+-----+-------------------+
| ID  |       NAME        |
+-----+-------------------+
|  1  |  Supplier Name 1  |
|  2  |  Supplier Name 2  |
|  3  |  Supplier Name 3  |
|  4  |  Supplier Name 4  |
+-----+-------------------+

***** Table: Product *****
+-----+-----------+--------------------+-------+------------+
| ID  |   NAME    |     DESCRIPTION    | PRICE | SUPPLIERID |
+-----+-----------+--------------------+-------+------------+
|1    | Product 1 | Name for Product 1 |  2.0  |     1      |
|2    | Product 2 | Name for Product 2 | 22.0  |     1      |
|3    | Product 3 | Name for Product 3 | 30.0  |     2      |
|4    | Product 4 | Name for Product 4 |  7.0  |     3      |
+-----+-----------+--------------------+-------+------------+

উপাদানগুলোও:

  • সরবরাহকারীর জন্য অলস মোডটি "সত্য" এ সেট করা হয়েছে (ডিফল্ট)

  • পণ্যের অনুসন্ধানে ব্যবহৃত আনার মোড নির্বাচন করুন

  • আনার মোড (ডিফল্ট): সরবরাহকারী তথ্য অ্যাক্সেস করা হয়

  • ক্যাচিং প্রথমবারের মতো কোনও ভূমিকা পালন করে না

  • সরবরাহকারী অ্যাক্সেস করা হয়

আনার মোডটি নির্বাচিত আনুন (ডিফল্ট)

// It takes Select fetch mode as a default
Query query = session.createQuery( "from Product p");
List list = query.list();
// Supplier is being accessed
displayProductsListWithSupplierName(results);

select ... various field names ... from PRODUCT
select ... various field names ... from SUPPLIER where SUPPLIER.id=?
select ... various field names ... from SUPPLIER where SUPPLIER.id=?
select ... various field names ... from SUPPLIER where SUPPLIER.id=?

ফলাফল:

  • পণ্যের জন্য 1 নির্বাচন বিবৃতি
  • এন সরবরাহকারী জন্য বিবৃতি নির্বাচন করুন

এটি এন + 1 নির্বাচন সমস্যা!


3
এটি কি সরবরাহকারীর জন্য 1 নির্বাচিত হওয়ার কথা রয়েছে, তারপরে N পণ্যটির জন্য নির্বাচন করে?
bencampbell_14

@ বেনক্যাম্পবেল_ হ্যাঁ, প্রথমদিকে আমিও একই অনুভব করেছি। তবে তার উদাহরণের সাথে এটি অনেক সরবরাহকারীদের কাছে এক পণ্য।
মোহাম্মদ ফয়জান খান

38

আমি অন্যান্য উত্তরগুলিতে সরাসরি মন্তব্য করতে পারি না, কারণ আমার যথেষ্ট খ্যাতি নেই। তবে এটি লক্ষণীয় যে সমস্যাটি মূলত কেবলমাত্র উত্থাপিত হয় কারণ historতিহাসিকভাবে, হ্যান্ডলিংয়ের সাথে যোগ দেওয়ার ক্ষেত্রে অনেকগুলি ডিবিএমই বেশ দরিদ্র ছিল (মাইএসকিউএল একটি বিশেষ উল্লেখযোগ্য উদাহরণ)। সুতরাং এন +1 প্রায়শই একটি যোগদানের চেয়ে উল্লেখযোগ্যভাবে দ্রুত হয়। এবং তারপরে এন + 1 তে উন্নতি করার উপায় রয়েছে তবে তার পরেও যোগদানের দরকার নেই, যা মূল সমস্যাটির সাথে সম্পর্কিত।

যাইহোক, মাইএসকিউএল যোগদানের সময় এটি আগের থেকে অনেক বেশি ভাল। আমি যখন প্রথম মাইএসকিউএল শিখেছি তখন আমি অনেকগুলি যোগদান করে। তারপরে আমি আবিষ্কার করেছি যে তারা কত ধীর গতিতে রয়েছে এবং পরিবর্তে কোডটিতে এন + 1 এ স্যুইচ করেছেন। তবে, সম্প্রতি, আমি আবার যোগদানের দিকে ফিরে চলেছি, কারণ মাইএসকিউএল এখন প্রথমবারের মতো এটি ব্যবহার করা শুরু করার চেয়ে সেগুলি পরিচালনা করার ক্ষেত্রে অনেক ভাল।

পারফরম্যান্সের দিক দিয়ে আজকাল, টেবিলগুলির যথাযথ সূচকযুক্ত সেটগুলিতে একটি সাধারণ যোগদান খুব কমই সমস্যা। এবং যদি এটি কোনও পারফরম্যান্স হিট দেয়, তবে সূচক ইঙ্গিতগুলির ব্যবহার প্রায়শই তাদের সমাধান করে।

এটি এখানে মাইএসকিউএল ডেভেলপমেন্ট টিমের একটি দ্বারা আলোচনা করা হয়েছে:

http://jorgenloland.blogspot.co.uk/2013/02/dbt-3-q3-6-x-performance-in-mysql-5610.html

সুতরাং সংক্ষিপ্তসারটি হ'ল: যদি আপনি অতীতে মাইএসকিউএল এর সাথে বিমূ .় পারফরম্যান্সের কারণে যোগদান করা এড়িয়ে চলেছেন তবে সর্বশেষতম সংস্করণগুলিতে আবার চেষ্টা করুন। আপনি সম্ভবত আনন্দদায়ক অবাক হবেন।


7
মাইএসকিউএলের প্রারম্ভিক সংস্করণগুলিকে একটি রিলেশনাল ডিবিএমএসের কল করা এক প্রসারিত বিষয় ... যদি এই সমস্যাগুলির মুখোমুখি লোকেরা একটি সত্যিকারের ডাটাবেস ব্যবহার করে থাকে তবে তারা এই ধরণের সমস্যার মুখোমুখি হত না। ;-)
ক্রেগ 23

2
মজার বিষয় হল, মাইএসকিউএলে আইএনএনওডিবি ইঞ্জিনের প্রবর্তন এবং পরবর্তী অপ্টিমাইজেশনের মাধ্যমে এই ধরণের অনেকগুলি সমস্যা সমাধান করা হয়েছিল, তবে আপনি এখনও মাইসাম প্রচারের চেষ্টা করছেন এমন লোকদের মধ্যে ছুটে যাবেন কারণ তারা মনে করেন এটি দ্রুততর।
ক্রেগ 23

5
এফওয়াইআই, JOINআরডিবিএমএসে ব্যবহৃত 3 টি সাধারণ অ্যালগরিদমের মধ্যে একটিকে নেস্টেড লুপ বলা হয়। এটি মূলত হুডের নীচে একটি এন + 1 নির্বাচন। পার্থক্য কেবলমাত্র ডিবি এটি পরিসংখ্যান এবং সূচকগুলি ভিত্তিক ব্যবহারের জন্য বুদ্ধিমান পছন্দ করেছে, ক্লায়েন্ট কোডটিকে এই পথটি স্পষ্টভাবে বাধ্য করতে বাধ্য না করে।
ব্র্যান্ডন

2
@ ব্র্যান্ডন হ্যাঁ! অনেকগুলি জোইন ইঙ্গিতগুলি এবং আইএনডেক্স ইঙ্গিতগুলির মতো, সমস্ত ক্ষেত্রে একটি নির্দিষ্ট মৃত্যুদন্ড কার্যকর করার জন্য ডাটাবেসটি খুব কমই পরাজিত হবে। ডেটাবেস প্রায় সবসময় খুব ভাল, ডেটা পেতে সর্বোত্তম পদ্ধতির চয়ন করতে খুব ভাল। সম্ভবত ডিবিএসের প্রথম দিনগুলিতে আপনার প্রশ্নটি 'বাক্যাংশ' দিয়ে ডিবি বদ্ধ করার জন্য একটি অদ্ভুত উপায়ে প্রয়োজন ছিল, কিন্তু বিশ্বমানের ইঞ্জিনিয়ারিংয়ের দশক পরে, আপনি এখন আপনার ডাটাবেসকে একটি সম্পর্কিত প্রশ্ন জিজ্ঞাসা করে এবং এটি দিয়ে দিয়ে সর্বোত্তম পারফরম্যান্স পেতে পারেন আপনার জন্য কীভাবে ডেটা আনতে এবং একত্রিত করবেন তা বাছাই করুন।
কুকুরগুলি

3
সূচক এবং পরিসংখ্যান ব্যবহার করে কেবল ডাটাবেসই নয়, সমস্ত ক্রিয়াকলাপ স্থানীয় আই / ওও হয়, যার বেশিরভাগই ডিস্কের চেয়ে উচ্চ দক্ষ ক্যাশেটির বিরুদ্ধে পরিচালিত হয়। ডাটাবেস প্রোগ্রামাররা এই ধরণের জিনিসগুলির অনুকূলকরণের জন্য অত্যন্ত মনোযোগ দেয়।
ক্রেগ

27

এই সমস্যার কারণে আমরা জ্যাঙ্গোর ওআরএম থেকে সরে এসেছি। মূলত, যদি আপনি চেষ্টা করেন এবং করেন

for p in person:
    print p.car.colour

ORM আনন্দের সাথে সমস্ত লোককে ফিরিয়ে দেবে (সাধারণত কোনও ব্যক্তি বস্তুর উদাহরণ হিসাবে) তবে তার জন্য প্রতিটি ব্যক্তির জন্য গাড়ির টেবিলটি জিজ্ঞাসা করতে হবে।

এটির একটি সহজ এবং খুব কার্যকর পদ্ধতিকে আমি " ফ্যানফোল্ডিং " বলি , যা সম্পর্কিত সম্পর্কিত ডাটাবেস থেকে কোয়েরির ফলাফলগুলি মূল টেবিলগুলিতে মানচিত্র তৈরি করা উচিত যা থেকে কোয়েরিটি রচনা করা উচিত avo

পদক্ষেপ 1: প্রশস্ত নির্বাচন

  select * from people_car_colour; # this is a view or sql function

এটি কিছু ফিরে আসবে

  p.id | p.name | p.telno | car.id | car.type | car.colour
  -----+--------+---------+--------+----------+-----------
  2    | jones  | 2145    | 77     | ford     | red
  2    | jones  | 2145    | 1012   | toyota   | blue
  16   | ashby  | 124     | 99     | bmw      | yellow

পদক্ষেপ 2: আপত্তি

তৃতীয় আইটেমের পরে বিভক্ত হওয়ার যুক্তি যুক্ত করে জেনেরিক অবজেক্ট ক্রিয়েটারকে ফলাফল চুষে নিন। এর অর্থ হ'ল "জোনস" অবজেক্টটি একাধিকবার তৈরি হবে না।

পদক্ষেপ 3: রেন্ডার

for p in people:
    print p.car.colour # no more car queries

পাইথনের ফ্যানফোল্ডিং বাস্তবায়নের জন্য এই ওয়েব পৃষ্ঠাটি দেখুন ।


10
আমি আপনার পোস্টে হোঁচট খেয়ে খুব আনন্দিত, কারণ আমি ভেবেছিলাম আমি পাগল হয়ে যাচ্ছি। আমি যখন এন +1 সমস্যা সম্পর্কে জানতে পেরেছিলাম তখন আমার তাত্ক্ষণিকভাবে চিন্তাভাবনা হয়েছিল - ঠিক আছে, আপনি কেবল এমন একটি দৃষ্টিভঙ্গি তৈরি করেন না যাতে আপনার প্রয়োজনীয় সমস্ত তথ্য রয়েছে এবং সেই দৃষ্টিভঙ্গি থেকে টানুন? আপনি আমার অবস্থান যাচাই করেছেন। ধন্যবাদ জনাব.
একটি বিকাশকারী

14
এই সমস্যার কারণে আমরা জ্যাঙ্গোর ওআরএম থেকে সরে এসেছি। তাই না? জ্যাঙ্গো রয়েছে select_related, যা এটি সমাধান করার জন্য বোঝানো হয়েছে - আসলে, এর দস্তাবেজগুলি আপনার p.car.colourউদাহরণের অনুরূপ একটি উদাহরণ দিয়ে শুরু হয় ।
অ্যাড্রিয়ান 17

8
এটি একটি পুরানো সংবাদ, আমাদের আছে select_related()এবং prefetch_related()এখন জ্যাঙ্গোতে।
মারিউজ জামরো

1
কুল। তবে select_related()এবং বন্ধু কোনও যোগদানের স্পষ্টত উপকারী এক্সট্রাপোলেশনগুলির কোনও কাজ করে বলে মনে হচ্ছে না LEFT OUTER JOIN। সমস্যাটি কোনও ইন্টারফেসের সমস্যা নয়, তবে আমার দৃষ্টিতে অবজেক্টস এবং রিলেশনাল ডেটা ম্যাপেবল। এমন এক আজব ধারণাটি করার একটি সমস্যা।
rorycl

26

যেহেতু এটি একটি খুব সাধারণ প্রশ্ন, আমি এই নিবন্ধটি লিখেছি , যার ভিত্তিতে এই উত্তরটি ভিত্তিক।

এন +1 ক্যোয়ারী সমস্যাটি কী

N + 1 ক্যোয়ারী সমস্যাটি ঘটে যখন ডেটা অ্যাক্সেস ফ্রেমওয়ার্কটি প্রাথমিক এসকিউএল কোয়েরি কার্যকর করার সময় পুনরুদ্ধার করা যেতে পারে একই ডেটা আনার জন্য এন অতিরিক্ত এসকিউএল স্টেটমেন্টগুলি কার্যকর করে।

N এর মান যত বেশি হবে, তত বেশি প্রশ্নগুলি কার্যকর করা হবে, কার্যকারণের প্রভাব তত বেশি। এবং, ধীর চলমান ক্যোয়ারীগুলি সন্ধান করতে আপনাকে সাহায্য করতে পারে এমন ধীর ক্যোয়ারী লগের বিপরীতে , এন + 1 সমস্যাটি স্পট হবে না কারণ প্রতিটি পৃথক অতিরিক্ত ক্যোয়ারী ধীর ক্যোয়ারি লগটি না চালানোর জন্য যথেষ্ট দ্রুত চলে।

সমস্যাটি হ'ল বিপুল সংখ্যক অতিরিক্ত প্রশ্নের সন্ধান করছে যা সামগ্রিকভাবে প্রতিক্রিয়া সময়কে ধীর করতে যথেষ্ট সময় নেয়।

আসুন বিবেচনা করা যাক আমাদের নিম্নলিখিত পোস্ট এবং পোস্ট_কমেন্টস ডাটাবেস টেবিলগুলি রয়েছে যা একের মধ্যে একাধিক টেবিল সম্পর্ক তৈরি করে :

<কোড> পোস্ট </ কোড> এবং <কোড> পোস্ট_কমেন্টস </ কোড> সারণী

আমরা নিম্নলিখিত 4 টি postসারি তৈরি করতে যাচ্ছি :

INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence - Part 1', 1)

INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence - Part 2', 2)

INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence - Part 3', 3)

INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence - Part 4', 4)

এবং, আমরা 4 টি post_commentশিশু রেকর্ডও তৈরি করব :

INSERT INTO post_comment (post_id, review, id)
VALUES (1, 'Excellent book to understand Java Persistence', 1)

INSERT INTO post_comment (post_id, review, id)
VALUES (2, 'Must-read for Java developers', 2)

INSERT INTO post_comment (post_id, review, id)
VALUES (3, 'Five Stars', 3)

INSERT INTO post_comment (post_id, review, id)
VALUES (4, 'A great reference book', 4)

প্লেইন এসকিউএল নিয়ে এন +1 ক্যোয়ারী সমস্যা

আপনি যদি post_commentsএই এসকিউএল কোয়েরি ব্যবহার করে নির্বাচন করেন :

List<Tuple> comments = entityManager.createNativeQuery("""
    SELECT
        pc.id AS id,
        pc.review AS review,
        pc.post_id AS postId
    FROM post_comment pc
    """, Tuple.class)
.getResultList();

এবং, পরে, আপনি post titleপ্রতিটিটির জন্য সম্পর্কিত আনার সিদ্ধান্ত নিন post_comment:

for (Tuple comment : comments) {
    String review = (String) comment.get("review");
    Long postId = ((Number) comment.get("postId")).longValue();

    String postTitle = (String) entityManager.createNativeQuery("""
        SELECT
            p.title
        FROM post p
        WHERE p.id = :postId
        """)
    .setParameter("postId", postId)
    .getSingleResult();

    LOGGER.info(
        "The Post '{}' got this review '{}'",
        postTitle,
        review
    );
}

আপনি এন + 1 ক্যোয়ারী সমস্যাটি ট্রিগার করতে চলেছেন কারণ একটি এসকিউএল ক্যোয়ারীর পরিবর্তে, আপনি 5 (1 + 4) কার্যকর করেছেন:

SELECT
    pc.id AS id,
    pc.review AS review,
    pc.post_id AS postId
FROM post_comment pc

SELECT p.title FROM post p WHERE p.id = 1
-- The Post 'High-Performance Java Persistence - Part 1' got this review
-- 'Excellent book to understand Java Persistence'

SELECT p.title FROM post p WHERE p.id = 2
-- The Post 'High-Performance Java Persistence - Part 2' got this review
-- 'Must-read for Java developers'

SELECT p.title FROM post p WHERE p.id = 3
-- The Post 'High-Performance Java Persistence - Part 3' got this review
-- 'Five Stars'

SELECT p.title FROM post p WHERE p.id = 4
-- The Post 'High-Performance Java Persistence - Part 4' got this review
-- 'A great reference book'

N + 1 ক্যোয়ারী ইস্যু ঠিক করা খুব সহজ। আসল এসকিউএল কোয়েরিতে আপনার প্রয়োজনীয় সমস্ত ডেটা উত্তোলন করার জন্য আপনাকে যা করতে হবে তা হ'ল:

List<Tuple> comments = entityManager.createNativeQuery("""
    SELECT
        pc.id AS id,
        pc.review AS review,
        p.title AS postTitle
    FROM post_comment pc
    JOIN post p ON pc.post_id = p.id
    """, Tuple.class)
.getResultList();

for (Tuple comment : comments) {
    String review = (String) comment.get("review");
    String postTitle = (String) comment.get("postTitle");

    LOGGER.info(
        "The Post '{}' got this review '{}'",
        postTitle,
        review
    );
}

এবার আমরা ব্যবহার করতে আরও আগ্রহী সমস্ত ডেটা আনতে কেবলমাত্র একটি এসকিউএল কোয়েরি কার্যকর করা হয়েছে।

জেপিএ এবং হাইবারনেট নিয়ে এন +1 ক্যোয়ারী সমস্যা

জেপিএ এবং হাইবারনেট ব্যবহার করার সময়, আপনি এন + 1 ক্যোয়ারী ইস্যুটি ট্রিগার করতে পারেন এমন বেশ কয়েকটি উপায় রয়েছে, সুতরাং আপনি কীভাবে এই পরিস্থিতিগুলি এড়াতে পারবেন তা জানা খুব গুরুত্বপূর্ণ।

পরবর্তী উদাহরণের জন্য, বিবেচনা আমরা ম্যাপিং হয় postএবং post_commentsনিম্নলিখিত সত্ত্বা থেকে টেবিল:

<কোড> পোস্ট </ কোড> এবং <কোড> পোস্টকমেন্ট </ কোড> সত্ত্বা

জেপিএ ম্যাপিংগুলি এর মতো দেখাচ্ছে:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    //Getters and setters omitted for brevity
}

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    private Long id;

    @ManyToOne
    private Post post;

    private String review;

    //Getters and setters omitted for brevity
}

FetchType.EAGER

ব্যবহার FetchType.EAGERহয় পরোক্ষভাবে বা স্পষ্টভাবে আপনার JPA সমিতির জন্য একটি খারাপ ধারণা কারণ আপনার পথ আরো তথ্য যা আপনার প্রয়োজন আনতে যাচ্ছি। আরও, FetchType.EAGERকৌশলটি এন + 1 ক্যোয়ারী ইস্যুতেও প্রবণ।

দুর্ভাগ্যবশত, @ManyToOneএবং @OneToOneসমিতির ব্যবহার FetchType.EAGER, ডিফল্ট অনুসারে তাই যদি আপনার ম্যাপিং ভালো চেহারা:

@ManyToOne
private Post post;

আপনি FetchType.EAGERকৌশলটি ব্যবহার করছেন এবং প্রতিবারের মতো JOIN FETCHকোনও PostCommentসত্তা কোনও জেপিকিউএল বা মানদণ্ডের এপিআই কোয়েরি লোড করার সময় আপনি ব্যবহার করতে ভুলে যাচ্ছেন:

List<PostComment> comments = entityManager
.createQuery("""
    select pc
    from PostComment pc
    """, PostComment.class)
.getResultList();

আপনি এন + 1 ক্যোয়ারী সমস্যাটি ট্রিগার করতে চলেছেন:

SELECT 
    pc.id AS id1_1_, 
    pc.post_id AS post_id3_1_, 
    pc.review AS review2_1_ 
FROM 
    post_comment pc

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 2
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 3
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 4

লক্ষ্য করুন অতিরিক্ত নির্বাচন বিবৃতি কারণ মৃত্যুদন্ড কার্যকর করা হয় যে postসমিতি ফেরার পূর্বে সংগৃহীত হতে হয়েছে Listএর PostCommentসত্ত্বা।

ডিফল্ট আনয়ন পরিকল্পনার বিপরীতে, যা আপনি findপদ্ধতিটির কল করার সময় ব্যবহার করছেন EnrityManager, একটি জেপিকিউএল বা ক্রিটারিয়া এপিআই ক্যোয়ারী একটি সুস্পষ্ট পরিকল্পনা সংজ্ঞায়িত করে যে হাইবারনেট স্বয়ংক্রিয়ভাবে একটি জিন ফিচকে ইনজেকশন দিয়ে পরিবর্তন করতে পারে না। সুতরাং, আপনার এটি ম্যানুয়ালি করা দরকার।

আপনার যদি postঅ্যাসোসিয়েশনটি মোটেই প্রয়োজন না হয়, ব্যবহার করার সময় আপনি ভাগ্য থেকে দূরে থাকবেন FetchType.EAGERকারণ এটি আনা এড়ানোর কোনও উপায় নেই। এজন্য FetchType.LAZYডিফল্টরূপে ব্যবহার করা ভাল ।

তবে, আপনি যদি postঅ্যাসোসিয়েশনটি ব্যবহার করতে চান , তবে আপনি JOIN FETCHN + 1 ক্যোয়ারী সমস্যাটি থেকে উত্তোলন করতে পারেন :

List<PostComment> comments = entityManager.createQuery("""
    select pc
    from PostComment pc
    join fetch pc.post p
    """, PostComment.class)
.getResultList();

for(PostComment comment : comments) {
    LOGGER.info(
        "The Post '{}' got this review '{}'", 
        comment.getPost().getTitle(), 
        comment.getReview()
    );
}

এবার হাইবারনেট একটি একক এসকিউএল বিবৃতি কার্যকর করবে:

SELECT 
    pc.id as id1_1_0_, 
    pc.post_id as post_id3_1_0_, 
    pc.review as review2_1_0_, 
    p.id as id1_0_1_, 
    p.title as title2_0_1_ 
FROM 
    post_comment pc 
INNER JOIN 
    post p ON pc.post_id = p.id

-- The Post 'High-Performance Java Persistence - Part 1' got this review 
-- 'Excellent book to understand Java Persistence'

-- The Post 'High-Performance Java Persistence - Part 2' got this review 
-- 'Must-read for Java developers'

-- The Post 'High-Performance Java Persistence - Part 3' got this review 
-- 'Five Stars'

-- The Post 'High-Performance Java Persistence - Part 4' got this review 
-- 'A great reference book'

কেন আপনার FetchType.EAGERআনার কৌশলটি এড়ানো উচিত সে সম্পর্কে আরও তথ্যের জন্য , এই নিবন্ধটিও পরীক্ষা করে দেখুন ।

FetchType.LAZY

এমনকি আপনি FetchType.LAZYসমস্ত সংঘের জন্য স্পষ্টভাবে ব্যবহার করতে স্যুইচ করলেও আপনি এখনও এন + 1 ইস্যুতে ঝাঁপিয়ে পড়তে পারেন।

এবার postসমিতিটি এইভাবে ম্যাপ করা হয়েছে:

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

এখন, আপনি PostCommentসত্ত্বা আনার সময় :

List<PostComment> comments = entityManager
.createQuery("""
    select pc
    from PostComment pc
    """, PostComment.class)
.getResultList();

হাইবারনেট একটি একক এসকিউএল বিবৃতি কার্যকর করবে:

SELECT 
    pc.id AS id1_1_, 
    pc.post_id AS post_id3_1_, 
    pc.review AS review2_1_ 
FROM 
    post_comment pc

তবে, যদি পরে, আপনি অলস-বোঝা সমিতিটি উল্লেখ করতে চলেছেন post:

for(PostComment comment : comments) {
    LOGGER.info(
        "The Post '{}' got this review '{}'", 
        comment.getPost().getTitle(), 
        comment.getReview()
    );
}

আপনি এন + 1 ক্যোয়ারী ইস্যু পাবেন:

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1
-- The Post 'High-Performance Java Persistence - Part 1' got this review 
-- 'Excellent book to understand Java Persistence'

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 2
-- The Post 'High-Performance Java Persistence - Part 2' got this review 
-- 'Must-read for Java developers'

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 3
-- The Post 'High-Performance Java Persistence - Part 3' got this review 
-- 'Five Stars'

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 4
-- The Post 'High-Performance Java Persistence - Part 4' got this review 
-- 'A great reference book'

কারণ postসমিতি প্রখর রৌদ্রে সংগৃহীত হয়, সেকেন্ডারি SQL বক্তব্য যখন অর্ডার লগ বার্তা নির্মাণ করার জন্য অলস সমিতি অ্যাক্সেস মৃত্যুদন্ড কার্যকর করা হবে না।

আবার, JOIN FETCHফিক্সটি জেপিকিউএল কোয়েরিতে একটি ধারা যোগ করার সাথে জড়িত:

List<PostComment> comments = entityManager.createQuery("""
    select pc
    from PostComment pc
    join fetch pc.post p
    """, PostComment.class)
.getResultList();

for(PostComment comment : comments) {
    LOGGER.info(
        "The Post '{}' got this review '{}'", 
        comment.getPost().getTitle(), 
        comment.getReview()
    );
}

এবং, FetchType.EAGERযেমন উদাহরণের মতো, এই জেপিকিউএল কোয়েরিটি একটি একক এসকিউএল বিবৃতি উত্পন্ন করবে।

আপনি যদি FetchType.LAZYদ্বি- @OneToOneনির্দেশমূলক জেপিএ সম্পর্কের শিশু সংযোগটি ব্যবহার করে থাকেন এবং উল্লেখ না করেন , আপনি এখনও এন + 1 ক্যোয়ারী ইস্যুটি ট্রিগার করতে পারেন।

@OneToOneসমিতিগুলির দ্বারা উত্পন্ন N + 1 কোয়েরি ইস্যুটি কীভাবে আপনি কাটিয়ে উঠতে পারেন সে সম্পর্কে আরও তথ্যের জন্য , এই নিবন্ধটি দেখুন

N + 1 ক্যোয়ারী সমস্যাটি কীভাবে স্বয়ংক্রিয়ভাবে সনাক্ত করা যায়

আপনি যদি নিজের ডেটা অ্যাক্সেস লেয়ারে N + 1 ক্যোয়ারী সমস্যাটি স্বয়ংক্রিয়ভাবে সনাক্ত করতে চান তবে এই নিবন্ধটিdb-util ওপেন-সোর্স প্রকল্পটি ব্যবহার করে আপনি কীভাবে এটি করতে পারেন তা ব্যাখ্যা করে ।

প্রথমত, আপনাকে নিম্নলিখিত মাভেন নির্ভরতা যুক্ত করতে হবে:

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>db-util</artifactId>
    <version>${db-util.version}</version>
</dependency>

এরপরে, আপনাকে SQLStatementCountValidatorঅন্তর্নিহিত এসকিউএল স্টেটমেন্টগুলি উত্পন্ন করার জন্য ইউটিলিটিটি ব্যবহার করতে হবে:

SQLStatementCountValidator.reset();

List<PostComment> comments = entityManager.createQuery("""
    select pc
    from PostComment pc
    """, PostComment.class)
.getResultList();

SQLStatementCountValidator.assertSelectCount(1);

আপনি FetchType.EAGERউপরের পরীক্ষার কেসটি ব্যবহার এবং চালানোর ক্ষেত্রে, আপনি নিম্নলিখিত পরীক্ষার ক্ষেত্রে ব্যর্থতা পাবেন:

SELECT 
    pc.id as id1_1_, 
    pc.post_id as post_id3_1_, 
    pc.review as review2_1_ 
FROM 
    post_comment pc

SELECT p.id as id1_0_0_, p.title as title2_0_0_ FROM post p WHERE p.id = 1

SELECT p.id as id1_0_0_, p.title as title2_0_0_ FROM post p WHERE p.id = 2


-- SQLStatementCountMismatchException: Expected 1 statement(s) but recorded 3 instead!

db-utilওপেন-সোর্স প্রকল্প সম্পর্কে আরও তথ্যের জন্য , এই নিবন্ধটি দেখুন


তবে এখন আপনার পৃষ্ঠা পৃষ্ঠাতে সমস্যা রয়েছে। আপনার যদি 10 টি গাড়ি থাকে তবে প্রতিটি গাড়ি 4 চাকাযুক্ত এবং আপনি পৃষ্ঠাতে 5 টি গাড়ি দিয়ে গাড়ি পৃষ্ঠাতে চান। সুতরাং আপনি মূলত আপনার আছে SELECT cars, wheels FROM cars JOIN wheels LIMIT 0, 5। তবে আপনি যা পাবেন তা 5 চাকা বিশিষ্ট 2 টি গাড়ি (সমস্ত গাড়ি 4 টি চাকাযুক্ত প্রথম গাড়ি এবং দ্বিতীয় গাড়ী মাত্র 1 চাকা সহ), কারণ সীমাবদ্ধতা কেবলমাত্র রুট ক্লজই নয়, পুরো ফলাফলসীমা সীমাবদ্ধ করবে।
CappY

2
আমিও তার জন্য একটি নিবন্ধ আছে ।
ভ্লাদ মিহলসিয়া

নিবন্ধের জন্য আপনাকে ধন্যবাদ। আমি এটা পড়তে হবে। দ্রুত স্ক্রোল দ্বারা - আমি দেখেছি যে সমাধানটি উইন্ডো ফাংশন, তবে সেগুলি মারিয়াডিবিতে মোটামুটি নতুন - তাই সমস্যাটি পুরানো সংস্করণগুলিতে স্থির থাকে। :)
ক্যাপিওয়াই

@ ভ্লাদমিহালসিয়া, আমি আপনার নিবন্ধ থেকে বা পোস্টটি থেকে প্রতি বার লক্ষ করেছি যখন আপনি ম্যান্টি টুওনের কেসটি উল্লেখ করবেন তখন N + 1 সমস্যাটি ব্যাখ্যা করার সময়। তবে প্রকৃতপক্ষে N + 1 ইস্যু সম্পর্কিত ওয়ানটোমনি মামলায় বেশিরভাগ লোক আগ্রহী। আপনি কি দয়া করে ওয়ানটম্যানির কেসটি উল্লেখ এবং ব্যাখ্যা করতে পারেন?
জেজে বীম

18

মনে করুন আপনার কাছে কম্পিউটার এবং কর্মচারী রয়েছে। সংস্থার অনেকগুলি EMPLOYEES রয়েছে (অর্থাত EMPLOYEE এর একটি ক্ষেত্র COMPANY_ID)।

কিছু ও / আর কনফিগারেশনগুলিতে, যখন আপনি কোনও ম্যাপযুক্ত সংস্থা অবজেক্ট পাবেন এবং তার কর্মচারী সামগ্রীগুলি অ্যাক্সেস করতে যান, O / R সরঞ্জামটি প্রতিটি কর্মচারীর জন্য একটি করে নির্বাচন করবে, যদি আপনি কেবল স্ট্রাইক এসকিউএল-এ কাজ করে থাকেন তবে আপনি তা করতে পারেন select * from employees where company_id = XX। সুতরাং এন (# কর্মচারীদের) প্লাস 1 (সংস্থা)

ইজেবি সত্তা বিনের প্রাথমিক সংস্করণগুলি এভাবে কাজ করেছিল। আমি বিশ্বাস করি হাইবারনেটের মতো জিনিসগুলি এটিকে সরিয়ে দিয়েছে, তবে আমি খুব বেশি নিশ্চিত নই। বেশিরভাগ সরঞ্জাম সাধারণত ম্যাপিংয়ের কৌশল হিসাবে তাদের তথ্য অন্তর্ভুক্ত করে।


18

সমস্যাটির একটি ভাল বর্ণনা এখানে

এখন আপনি যে সমস্যাটি বুঝতে পেরেছেন তা সাধারণত আপনার ক্যোয়ারিতে একটি জয়েন্ট আনার মাধ্যমে এড়ানো যায়। এটি মূলত অলস লোড হওয়া বস্তুর আনতে বাধ্য করে যাতে ডেটা n + 1 প্রশ্নের পরিবর্তে একটি ক্যোয়ারিতে পুনরুদ্ধার করা হয়। আশাকরি এটা সাহায্য করবে.


17

: বিষয়ে Ayende চেক পোস্ট নির্বাচন n + 1 সমস্যা NHibernate বিরোধিতা

মূলত, এনএইচবারনেট বা সত্তা ফ্রেমওয়ার্কের মতো কোনও ওআরএম ব্যবহার করার সময়, যদি আপনার একাধিক (মাস্টার-বিশদ) সম্পর্ক থাকে এবং প্রতিটি মাস্টার রেকর্ড অনুযায়ী সমস্ত বিবরণ তালিকাভুক্ত করতে চান, আপনাকে N + 1 কোয়েরি কল করতে হবে ডাটাবেস, "এন" মাস্টার রেকর্ডের সংখ্যা: সমস্ত মাস্টার রেকর্ড পেতে 1 টি ক্যোয়ারী এবং মাস্টার রেকর্ড প্রতি সমস্ত বিবরণ পাওয়ার জন্য মাস্টার রেকর্ড প্রতি এন কোয়েরি।

আরও ডেটাবেস কোয়েরি কলগুলি - আরও বেশি বিলম্বের সময় application অ্যাপ্লিকেশন / ডাটাবেসের কর্মক্ষমতা হ্রাস পেয়েছে।

তবে ওআরএম-এর কাছে এই সমস্যাটি এড়াতে অপশন রয়েছে, মূলত জোয়িনগুলি ব্যবহার করে।


3
যোগদানগুলি একটি ভাল সমাধান নয় (প্রায়শই), কারণ তারা কার্টেসিয়ান পণ্য তৈরি করতে পারে যার অর্থ ফলাফলের সারিগুলির সংখ্যা প্রতিটি সন্তানের টেবিলে ফলাফলের সংখ্যার সাথে গুণিত রুট টেবিলের ফলাফলের সংখ্যা। একাধিক হেরার্কি স্তরের চেয়ে বিশেষত খারাপ। প্রতিটি "100" পোস্ট সহ 20 "ব্লগ" এবং প্রতিটি পোস্টে 10 "মন্তব্য" নির্বাচন করলে 20000 ফলাফল সারি হবে। এনএইচবারনেটের "ব্যাচ-সাইজ" (প্যারেন্ট আইডিতে অনুচ্ছেদে বাচ্চাদের নির্বাচন করুন) বা "সাবলেক্ট" এর মতো কার্যকারিতা রয়েছে।
এরিক হার্ট

13

আমার মতে হাইবারনেট পিটফলে লেখা নিবন্ধ : রিলেশনশিপস অলস হওয়া উচিত কেন আসল এন + 1 ইস্যুটির ঠিক বিপরীত।

আপনার যদি সঠিক ব্যাখ্যার প্রয়োজন হয় তবে দয়া করে হাইবারনেট দেখুন - অধ্যায় 19: পারফরম্যান্স উন্নত করা - কৌশলগুলি আনা

আনুন সিলেক্ট করুন (ডিফল্ট) এন +1 সমস্যা নির্বাচন করে এমন ক্ষেত্রে চরম দুর্বল, সুতরাং আমরা যোগদানের আনতে সক্ষম করতে চাই


2
আমি হাইবারনেট পৃষ্ঠাটি পড়েছি। এটি এন -1 টি যা সমস্যা নির্বাচন করে তা আসলে বলে না । তবে এটি বলে যে আপনি এটি সংশোধন করতে যোগদান করতে পারেন।
ইয়ান বয়ড

3
এক বাছাই বিবৃতিতে একাধিক পিতামাতার জন্য শিশু বস্তু নির্বাচন করার জন্য ব্যাচ-আকারের প্রয়োজন বোধ করা। সাবলেকলেট অন্য বিকল্প হতে পারে। আপনার যদি একাধিক স্তরক্রমের স্তর থাকে এবং কার্টেসিয়ান পণ্য তৈরি হয় তবে সত্যিই খারাপ হতে পারে।
এরিক হার্ট

13

1 টি ক্যোয়ারী ইস্যু করা খুব দ্রুত যা 100 টি ফলাফল প্রদানের চেয়ে 100 ফলাফল দেয় যা প্রতিটি প্রত্যাবর্তনে 1 ফলাফল দেয়।


10

সরবরাহিত লিঙ্কটিতে এন + 1 সমস্যার খুব সাধারণ উদাহরণ রয়েছে। আপনি যদি এটি হাইবারনেটে প্রয়োগ করেন তবে এটি মূলত একই জিনিসটি নিয়ে কথা বলছে। আপনি যখন কোনও অবজেক্টের জন্য অনুসন্ধান করবেন তখন সত্তাটি লোড হয় তবে কোনও সমিতি (অন্যথায় কনফিগার না করা) অলস লোড হবে। সুতরাং প্রতিটি এর জন্য সংযুক্তিগুলি লোড করতে মূল অবজেক্টের জন্য একটি কোয়েরি এবং অন্য কোয়েরি। 100 টি প্রত্যাবর্তিত বস্তুর অর্থ একটি প্রাথমিক ক্যোয়ারী এবং তারপরে প্রতিটির জন্য সমিতি পেতে 100 টি অতিরিক্ত ক্যোয়ারী, এন + 1।

http://pramatr.com/2009/02/05/sql-n-1-selects-explained/


9

এক মিলিয়নেয়ারের এন গাড়ি রয়েছে। আপনি সমস্ত (4) চাকা পেতে চান।

একটি (1) ক্যোয়ারী সমস্ত গাড়ি লোড করে তবে প্রতিটি (এন) গাড়ির জন্য লোডিং চাকার জন্য পৃথক ক্যোয়ারী জমা দেওয়া হয়।

খরচ:

ধরুন সূচিগুলি রামের সাথে খাপ খায়।

1 + এন ক্যোয়ারী পার্সিং এবং প্ল্যানিং + সূচক অনুসন্ধান এবং 1 + এন + (এন * 4) প্লেড লোড লোড করার জন্য প্লেট অ্যাক্সেস।

ধরুন সূচিগুলি রামের সাথে খাপ খায় না।

লোডিং ইনডেক্সের জন্য খারাপের জন্য অতিরিক্ত খরচ 1 + এন প্লেট অ্যাক্সেস করে।

সারসংক্ষেপ

বোতল ঘাড় প্লেট অ্যাক্সেস (সিএডিএড এ প্রতি সেকেন্ডে 70 বার বার এলোমেলো প্রবেশাধিকার) একটি উত্সাহী যোগ নির্বাচন প্লেডের জন্য প্লেট 1 + এন + (এন * 4) বার অ্যাক্সেস করতে পারে। সুতরাং সূচিগুলি যদি রামের সাথে মাপসই হয় - কোনও সমস্যা নেই, এটির দ্রুত যথেষ্ট কারণ শুধুমাত্র রামের ক্রিয়াকলাপ জড়িত।


9

এন +1 নির্বাচন ইস্যুটি একটি ব্যথা এবং ইউনিট পরীক্ষায় এই জাতীয় কেসগুলি সনাক্ত করা বোধগম্য। প্রদত্ত পরীক্ষার পদ্ধতি দ্বারা নির্বাহ করা প্রশ্নের সংখ্যা বা কোডের কেবল একটি স্বেচ্ছাসেবক ব্লক - যাডিবিসি স্নিফার দ্বারা পরীক্ষা করার জন্য আমি একটি ছোট লাইব্রেরি তৈরি করেছি

কেবলমাত্র আপনার পরীক্ষার শ্রেণিতে একটি বিশেষ JUnit নিয়ম যুক্ত করুন এবং আপনার পরীক্ষার পদ্ধতিগুলিতে প্রত্যাশিত সংখ্যক প্রশ্নের সাথে টিক চিহ্ন দিন:

@Rule
public final QueryCounter queryCounter = new QueryCounter();

@Expectation(atMost = 3)
@Test
public void testInvokingDatabase() {
    // your JDBC or JPA code
}

5

অন্যরা যেমন ইস্যুটি আরও মার্জিতভাবে বলেছে তা হ'ল আপনার কাছে হয় ওয়ানটোমনি কলামগুলির কার্টেসিয়ান পণ্য রয়েছে বা আপনি এন + 1 নির্বাচন করছেন। যথাক্রমে সম্ভাব্য বিশাল ফলস্বরূপ বা ডাটাবেসের সাথে চ্যাটি।

আমি অবাক হয়েছি এটির উল্লেখ নেই তবে এই সমস্যাটি সম্পর্কে আমি কীভাবে উপার্জন করেছি ... আমি একটি আধা-অস্থায়ী আইডি টেবিল তৈরি করিআপনার IN ()ক্লজ সীমাবদ্ধতা থাকলে আমি এটিও করি

এটি সমস্ত ক্ষেত্রে (সম্ভবত সংখ্যাগরিষ্ঠও নয়) কাজ করে না তবে এটি বিশেষত ভাল কাজ করে যদি আপনার প্রচুর শিশু অবজেক্ট থাকে যেমন কার্টেসিয়ান পণ্যটি হাতছাড়া হয়ে যায় (অর্থাত্ প্রচুর OneToManyকলামগুলি ফলাফলের ফলাফল হবে) কলামগুলির গুণন) এবং এর মতো আরও অনেক ব্যাচের কাজের মতো কাজ।

প্রথমে আপনি আপনার প্যারেন্ট অবজেক্ট আইডিকে ব্যাচ হিসাবে একটি আইডির টেবিলের মধ্যে .োকান। এই ব্যাচ_আইডি এমন একটি জিনিস যা আমরা আমাদের অ্যাপ্লিকেশনটিতে উত্পন্ন করি এবং ধরে রাখি।

INSERT INTO temp_ids 
    (product_id, batch_id)
    (SELECT p.product_id, ? 
    FROM product p ORDER BY p.product_id
    LIMIT ? OFFSET ?);

এখন প্রতিটি OneToManyকলামের জন্য আপনি কেবলমাত্র একটি (বা তদ্বিপরীত) দিয়ে শিশু টেবিলের SELECTআইডিং টেবিলটিতে একটি করুন । আপনি কেবল এটি নিশ্চিত করতে চান যে আপনি আইডি কলামের মাধ্যমে অর্ডার করেছেন কারণ এটি ফলাফলগুলি কলামগুলি মার্জ করা সহজ করে তুলবে (অন্যথায় আপনার পুরো ফলাফলের জন্য একটি হ্যাশম্যাপ / সারণীর প্রয়োজন হবে যা খারাপ নাও হতে পারে)।INNER JOINWHERE batch_id=

তারপরে আপনি সাময়িকভাবে আইডির টেবিলটি পরিষ্কার করুন।

এটি বিশেষত খুব ভাল কাজ করে যদি ব্যবহারকারী কিছু ধরণের বাল্ক প্রসেসিংয়ের জন্য 100 বা ততোধিক স্বতন্ত্র আইটেমগুলি নির্বাচন করে। অস্থায়ী সারণীতে 100 টি পৃথক আইডি রাখুন।

এখন আপনি যেই ক্যোয়ারী করছেন সেগুলি হ'ল ওয়ানটোম্যান কলামগুলির সংখ্যা।


1

ম্যাট সলনিট উদাহরণটি ধরুন, কল্পনা করুন যে আপনি গাড়ী এবং চাকার মধ্যে একটি অ্যালসিকে সংজ্ঞা দিয়েছেন এবং আপনার কয়েকটি চাকার ক্ষেত্র প্রয়োজন need এর অর্থ হ'ল প্রথম নির্বাচনের পরে হাইবারনেট "সিলেক্ট * হুইলস থেকে গাড়ি নির্বাচন করতে চলেছে যেখানে প্রতিটি গাড়ির জন্য car_id =: id" করতে হবে।

এটি প্রতিটি এন কারের দ্বারা প্রথম নির্বাচন এবং আরও 1 টি নির্বাচন করে, এজন্য এটিকে এন +1 সমস্যা বলা হয়।

এটি এড়াতে, সমিতিটিকে উত্সাহী করার জন্য উত্সাহিত করুন, যাতে হাইবারনেট একটি যোগদানের সাথে ডেটা বোঝায়।

তবে মনোযোগ দিন, আপনি যদি অনেক সময় সম্পর্কিত চাকাগুলিতে অ্যাক্সেস না করেন তবে এটিকে আলস্য রাখা বা মানদণ্ডের মাধ্যমে আনার ধরণের পরিবর্তন করা ভাল।


1
আবার, যোগদানগুলি কোনও ভাল সমাধান নয়, বিশেষত যখন 2 টিরও বেশি স্তরক্রমের স্তর লোড হতে পারে। পরিবর্তে "সাবলেট" বা "ব্যাচ-আকার" পরীক্ষা করুন; সর্বশেষে "ইন" ধারাটিতে অভিভাবক আইডি দ্বারা বাচ্চাদের লোড করা হবে, যেমন "চাকা ... যেখানে গাড়ি_আইড (1,3,4,6,7,8,11,13)" তে গাড়ি নির্বাচন করুন "থেকে" নির্বাচন করুন ... "।
এরিক হার্ট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.