ORM (වස්තු-සම්බන්ධතා සිතියම්කරණය) හි “N + 1 තේරීමේ ගැටලුව” යනු කුමක්ද?


1619

"N + 1 තේරීමේ ගැටළුව" සාමාන්‍යයෙන් වස්තු-සම්බන්ධතා සිතියම්කරණයේ (ORM) සාකච්ඡාවල ගැටලුවක් ලෙස ප්‍රකාශ කර ඇති අතර, වස්තුවෙහි සරල යැයි පෙනෙන දෙයක් සඳහා දත්ත සමුදා විමසීම් රාශියක් කිරීමට එය සම්බන්ධයක් ඇති බව මම තේරුම් ගතිමි. ලෝකය.

ගැටලුව පිළිබඳව වඩාත් සවිස්තරාත්මක පැහැදිලි කිරීමක් කිසිවෙකුට තිබේද?


මෙම ගැටළුව සහ හැකි විසඳුම ගැන කතා කරන ප්‍රයෝජනවත් ලිපි කිහිපයක් තිබේ. පොදු යෙදුම් ගැටළු සහ ඒවා නිවැරදි කරන්නේ කෙසේද: තෝරාගත් N + 1 ගැටලුව , N + 1 ගැටලුව සඳහා (රිදී)
උණ්ඩය

මෙම ගැටලුවට විසඳුමක් සොයන සෑම කෙනෙකුටම, එය විස්තර කරන ලිපියක් මට හමු විය. stackoverflow.com/questions/32453989/...
damndemon

2
පිළිතුරු සලකා බැලීමේදී මෙය 1 + N ගැටලුවක් ලෙස හැඳින්විය යුතු නොවේද? මෙය පාරිභාෂිතයක් ලෙස පෙනෙන පරිදි, මම විශේෂයෙන් OP විමසන්නේ නැත.
user1418717

Answers:


1040

අපි ඔබට Carවස්තු එකතුවක් (දත්ත සමුදා පේළි) ඇති අතර, සෑම එකක්ම වස්තු Carඑකතුවක් Wheel(පේළි ද ඇත). වෙනත් වචන වලින් කිවහොත්, CarWheelයනු 1 සිට බොහෝ සම්බන්ධතාවයකි.

දැන්, අපි ඔබට සියලු මෝටර් රථ හරහා නැවත ගමන් කළ යුතු යැයි කියමු, එක් එක් සඳහා, රෝද ලැයිස්තුවක් මුද්‍රණය කරන්න. බොළඳ O / R ක්‍රියාත්මක කිරීම පහත සඳහන් දේ කරයි:

SELECT * FROM Cars;

ඉන්පසු එක් එක් සඳහා Car:

SELECT * FROM Wheel WHERE CarId = ?

වෙනත් වචන වලින් කිවහොත්, ඔබට කාර් සඳහා එක් තේරීමක් ඇත, පසුව N අතිරේක තේරීම් ඇත, මෙහි N යනු මුළු මෝටර් රථ සංඛ්‍යාවයි.

විකල්පයක් ලෙස, යමෙකුට සියලු රෝද ලබා ගත හැකි අතර මතක සටහන් බැලීමේ හැකියාව ඇත:

SELECT * FROM Wheel

මෙය දත්ත සමුදායට වට + සංචාර ගණන N + 1 සිට 2 දක්වා අඩු කරයි. බොහෝ ORM මෙවලම් මඟින් N + 1 තේරීම වැළැක්වීමට ක්‍රම කිහිපයක් ලබා දේ.

යොමුව: හයිබර්නේට් සමඟ ජාවා නොනැසී පැවතීම , 13 වන පරිච්ඡේදය.


140
"මෙය නරකයි" යන්න පැහැදිලි කිරීම සඳහා - ඔබට SELECT * from Wheel;N + 1 වෙනුවට තෝරාගත් ( ) 1 ක් සහිත සියලුම රෝද ලබා ගත හැකිය . විශාල N සමඟ, කාර්ය සාධන පහර ඉතා වැදගත් විය හැකිය.
tucuxi

215
uctucuxi ඔබ පුදුමයට පත්වීම වැරදියි කියා ඔබට බොහෝ දේ ලැබීම ගැන. දත්ත සමුදායක් දර්ශක පිළිබඳ ඉතා හොඳ ය, නිශ්චිත CarID සඳහා විමසුම කිරීමෙන් ඉතා ඉක්මණින් ආපසු පැමිණේ. නමුත් ඔබට සියලු රෝද එක් වරක් ලැබුනේ නම්, සුචිගත කර නොමැති ඔබේ යෙදුමේ CarID සෙවීමට ඔබට සිදුවේ, මෙය මන්දගාමී වේ. ඔබගේ දත්ත සමුදායට ළඟා වීමට ඔබට ප්‍රධාන ප්‍රමාද ගැටළු නොමැති නම් n + 1 යන්න සැබවින්ම වේගවත් වේ - ඔව්, මම එය සැබෑ ලෝක කේත විශාල ප්‍රමාණයක් සමඟ මිණුම් සලකුණු කළෙමි.
ඒරියල්

75
@ariel 'නිවැරදි' ක්‍රමය වන්නේ සියලුම රෝද ලබා ගැනීමයි , එය ඇණවුම් කර ඇත්තේ CarId (1 තෝරන්න), සහ CarId ට වඩා වැඩි විස්තර අවශ්‍ය නම්, සියලු මෝටර් රථ සඳහා දෙවන විමසුමක් කරන්න (මුළු විමසුම් 2 ක්). දේවල් මුද්‍රණය කිරීම දැන් ප්‍රශස්ත වන අතර දර්ශක හෝ ද්විතීයික ආචයනය අවශ්‍ය නොවීය (ඔබට ප්‍රති results ල නැවත කියවිය හැකිය, ඒවා සියල්ල බාගත කිරීමට අවශ්‍ය නැත). ඔබ වැරදි දේ මිණුම් සලකුණු කළා. ඔබේ මිණුම් සලකුණු ගැන ඔබට තවමත් විශ්වාසයක් තිබේ නම්, ඔබේ අත්හදා බැලීම සහ ප්‍රති results ල පැහැදිලි කරමින් දීර් comment අදහස් දැක්වීමක් (හෝ සම්පූර්ණ පිළිතුරක්) පළ කිරීමට ඔබ කැමතිද?
tucuxi

92
"ශිශිරතාරක (මම අනෙක් ORM රාමු ගැන හුරුපුරුදු නැත) එය හැසිරවීමට ක්‍රම කිහිපයක් ඔබට ලබා දෙයි." මේ ක්‍රම මොනවාද?
ටිමා

58
RIriel වෙනම යන්ත්‍රවල දත්ත සමුදායන් සහ යෙදුම් සේවාදායකයන් සමඟ ඔබේ මිණුම් සලකුණු ක්‍රියාත්මක කිරීමට උත්සාහ කරන්න. මගේ අත්දැකීම් අනුව, දත්ත සමුදාය වෙත රවුම් චාරිකා විමසීමට වඩා ඉහළින් වැය වේ. ඉතින් ඔව්, විමසීම් සැබවින්ම වේගවත් ය, නමුත් එය විනාශකාරී ගමනකි. මම "WHERE Id = const " "WHERE Id IN ( const , const , ...)" බවට පරිවර්තනය කර ඇති අතර විශාලත්වයේ ඇණවුම් එයින් වැඩි වේ.
හාන්ස්

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

වගු අංක 2 හි ළමා පේළි අනුපිටපත් කිරීමට හේතු වන ප්‍රති result ල කට්ටලයක් මඟින් ඔබට ලැබෙනු ඇත. O / R සිතියම්කරුවන් අද්විතීය යතුරු ක්ෂේත්‍රයක් මත පදනම්ව වගු 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

O / RM විසින් ID = 1, Address = "22 Valley St" සමඟ නිවහනක් පුරවා ගත යුතු අතර පසුව ඩේව්, ජෝන් සහ මයික් සඳහා ජනතා සිද්ධීන් සමඟ ජනාවාස සමූහය එක් විමසුමකින් පුරවා ගත යුතුය.

ඉහත භාවිතා කළ එකම ලිපිනය සඳහා N + 1 විමසුමක ප්‍රති result ලය වනු ඇත්තේ:

Id Address
1  22 Valley St

වැනි වෙනම විමසුමක් සමඟ

SELECT * FROM Person WHERE HouseId = 1

එහි ප්‍රති ing ලයක් ලෙස වෙනම දත්ත කට්ටලයක් ලැබේ

Name    HouseId
Dave    1
John    1
Mike    1

අවසාන ප්‍රති result ලය තනි විමසුම සමඟ ඉහත ආකාරයට සමාන වේ.

තනි තේරීමක ඇති වාසි නම්, ඔබ අවසානයේ කැමති සියලු දත්ත ලබා ගැනීමයි. N + 1 හි ඇති වාසි වන්නේ විමසුම් සංකීර්ණතාවය අඩු වන අතර ඔබට ළමා ඉල්ලීම් කට්ටල පටවනු ලබන්නේ කම්මැලි පැටවීමකි.


4
N + 1 හි අනෙක් වාසිය නම් එය වේගවත් වීමයි. මන්දයත් දත්ත සමුදායට ප්‍රති index ල සෘජුවම දර්ශකයකින් ලබා දිය හැකි බැවිනි. බැඳීම හා පසුව වර්ග කිරීම සඳහා තාවකාලික වගුවක් අවශ්‍ය වන අතර එය මන්දගාමී වේ. N + 1 වළක්වා ගැනීමට ඇති එකම හේතුව නම් ඔබේ දත්ත සමුදාය සමඟ බොහෝ ප්‍රමාදයන් කතා කිරීමයි.
ඒරියල්

17
සම්බන්ධ වීම හා වර්ග කිරීම තරමක් වේගවත් විය හැකිය (මන්ද ඔබ සුචිගත කළ හා සමහරවිට වර්ග කළ ක්ෂේත්‍රවලට සම්බන්ධ වනු ඇත). ඔබේ 'n + 1' කොතරම් විශාලද? N + 1 ගැටළුව අදාළ වන්නේ ඉහළ ප්‍රමාද දත්ත සමුදා සම්බන්ධතා සඳහා පමණක් බව ඔබ බැරෑරුම් ලෙස විශ්වාස කරනවාද?
tucuxi

9
@ariel - ඔබේ මිණුම් සලකුණු නිවැරදි වුවත් N + 1 “වේගවත්ම” බවට ඔබේ උපදෙස් වැරදිය. එය කළ හැක්කේ කෙසේද? En.wikipedia.org/wiki/Anecdotal_evidence බලන්න , මෙම ප්‍රශ්නයට අනෙක් පිළිතුරෙහි මගේ අදහස ද බලන්න .
විට්නිලන්ඩ්

7
Ri ඒරියල් - මම හිතන්නේ මම එය හොඳින් තේරුම් ගත්තා :). මම පෙන්වා දීමට උත්සාහ කරන්නේ ඔබේ ප්‍රති result ලය අදාළ වන්නේ එක් කොන්දේසි මාලාවකට පමණක් බවයි. ප්‍රතිවිරුද්ධ දේ පෙන්වන ප්‍රතිවිරුද්ධ උදාහරණයක් මට පහසුවෙන් ගොඩනගා ගත හැකිය. එය අර්ථවත්ද?
විට්නිලන්ඩ්

13
නැවත අවධාරණය කිරීම සඳහා, SELECT N + 1 ගැටළුව එහි හරය වේ: නැවත ලබා ගැනීමට මට වාර්තා 600 ක් ඇත. ඔවුන් 600 දෙනා එක් විමසුමකින් ලබා ගැනීම වේගවත් ද, නැතහොත් විමසුම් 600 කින් වරකට 1 ක් ලබා ගැනීම වේගවත් ද? ඔබ MyISAM හි නොමැති නම් සහ / හෝ ඔබට දුර්වල ලෙස සාමාන්‍යකරණය කළ / දුර්වල ලෙස සුචිගත කරන ලද යෝජනා ක්‍රමයක් තිබේ නම් (ORM ගැටළුව නොවේ), නිසි ලෙස සුසර කරන ලද db මඟින් පේළි 600 එම්එස් 2 කින් ආපසු එවනු ඇත. එක් එම්එස් 1 ක් පමණ. ඉතින් අපි බොහෝ විට දකින්නේ N + 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      |
+-----+-----------+--------------------+-------+------------+

සාධක:

  • සැපයුම්කරු සඳහා කම්මැලි ප්‍රකාරය “සත්‍ය” ලෙස සකසා ඇත (පෙරනිමිය)

  • නිෂ්පාදිතය විමසීමට භාවිතා කරන මාදිලිය තෝරන්න

  • ලබා ගැනීමේ මාදිලිය (සුපුරුදු පරිදි): සැපයුම්කරුගේ තොරතුරු ප්‍රවේශ වේ

  • හැඹිලිය පළමු වරට කාර්යභාරයක් ඉටු නොකරයි

  • සැපයුම්කරුට ප්‍රවේශ වේ

Fetch මාදිලිය තෝරන්න Fetch (පෙරනිමිය)

// 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=?

ප්‍රති ult ලය:

  • නිෂ්පාදන සඳහා 1 ප්‍රකාශය තෝරන්න
  • N සැපයුම්කරු සඳහා ප්‍රකාශ තෝරන්න

මෙය N + 1 තෝරා ගැනීමේ ගැටළුවකි!


4
එය සැපයුම්කරු සඳහා 1 තේරීමක් විය යුතු අතර නිෂ්පාදන සඳහා N තෝරා ගනීද?
bencampbell_14

enbencampbell_ ඔව්, මුලදී මටත් එයම දැනුණා. නමුත් ඔහුගේ උදාහරණය සමඟ එය බොහෝ සැපයුම්කරුවන්ට එක් නිෂ්පාදනයක් වේ.
මොහොමඩ් ෆයිසාන් ඛාන්

39

මට වෙනත් පිළිතුරු ගැන කෙලින්ම අදහස් දැක්විය නොහැක, මන්ද මට ප්‍රමාණවත් කීර්තියක් නොමැති බැවිනි. නමුත් ගැටළුව මූලිකවම පැන නගින්නේ එය සැලකිල්ලට ගැනීම වටී, මන්දයත් බැඳීම් හැසිරවීමේදී d තිහාසිකව බොහෝ ඩීබීඑම් දුර්වල වී ඇති බැවිනි (MySQL විශේෂයෙන් කැපී පෙනෙන උදාහරණයකි). එබැවින් n + 1 බොහෝ විට සම්බන්ධ වීමට වඩා වේගවත් වී ඇත. එවිට n + 1 හි වැඩිදියුණු කළ හැකි ක්‍රම තිබේ, නමුත් තවමත් සම්බන්ධ වීමක් අවශ්‍ය නොවී, මුල් ගැටළුව සම්බන්ධ වන්නේ එයයි.

කෙසේ වෙතත්, MySQL දැන් සම්බන්ධ වීමට පෙර තිබූ තත්වයට වඩා බොහෝ හොඳය. මම මුලින්ම MySQL ඉගෙන ගත් විට, මම භාවිතා කළේ බොහෝ දේ සමඟය. ඒවා කොතරම් මන්දගාමීදැයි මම සොයාගත් අතර ඒ වෙනුවට කේතයේ n + 1 වෙත මාරු වීමි. නමුත්, මෑතකදී, මම නැවත සම්බන්ධ වීමට ගියෙමි, මන්දයත්, MySQL දැන් මම එය භාවිතා කිරීමට පටන් ගත් කාලයට වඩා ඒවා හැසිරවීමට වඩා හොඳය.

මේ දිනවල, නිසි ලෙස සුචිගත කරන ලද වගු සමූහයක් සමඟ සරල සම්බන්ධ වීම කාර්ය සාධනය අනුව කලාතුරකින් ගැටළුවක් වේ. එය කාර්ය සාධන පහරක් ලබා දෙන්නේ නම්, දර්ශක ඉඟි භාවිතා කිරීම බොහෝ විට ඒවා විසඳයි.

මෙය MySQL සංවර්ධන කණ්ඩායමේ එක් අයෙකු විසින් මෙහි සාකච්ඡා කරනු ලැබේ:

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

එබැවින් සාරාංශය නම්: ඔබ ඔවුන් සමඟ MySQL හි දුර්වල ක්‍රියාකාරිත්වය නිසා අතීතයේ දී සම්බන්ධ වීම වළක්වා ඇත්නම්, නවතම අනුවාදයන් මත නැවත උත්සාහ කරන්න. ඔබ බොහෝ විට පුදුමයට පත් වනු ඇත.


7
MySQL හි මුල් සංස්කරණ සම්බන්ධතා ඩීබීඑම්එස් ලෙස හැඳින්වීම තරමක් දිගු වේ ... එම ගැටලුවලට මුහුණ දෙන පුද්ගලයින් සැබෑ දත්ත ගබඩාවක් භාවිතා කළේ නම්, ඔවුන්ට එවැනි ගැටළු වලට මුහුණ දීමට නොහැකි වනු ඇත. ;-)
ක්‍රේග්

2
INNODB එන්ජිම හඳුන්වාදීම හා පසුව ප්‍රශස්තිකරණය කිරීමත් සමඟ MySQL හි මෙම ආකාරයේ ගැටලු බොහොමයක් විසඳා තිබීම සිත්ගන්නා කරුණකි, නමුත් MYISAM ප්‍රවර්ධනය කිරීමට උත්සාහ කරන පුද්ගලයින් වෙත ඔබ තවමත් යොමු වනු ඇත.
ක්‍රේග්

5
JOINRDBMS හි භාවිතා වන පොදු ඇල්ගොරිතම 3 න් එකක් වන FYI, කැදැලි ලූප ලෙස හැඳින්වේ. එය මූලික වශයෙන් කබාය යටතේ තෝරාගත් N + 1 වේ. එකම වෙනස වන්නේ සේවාදායක කේතයට වඩා සංඛ්‍යාත්මකව හා දර්ශක මත පදනම්ව එය භාවිතා කිරීම සඳහා ඩීබී බුද්ධිමත් තේරීමක් කිරීමයි.
බ්‍රැන්ඩන්

2
@ බ්‍රැන්ඩන් ඔව්! JOIN ඉඟි සහ INDEX ඉඟි මෙන්, සෑම අවස්ථාවකම කිසියම් ක්‍රියාත්මක කිරීමේ මාවතක් බල කිරීම දත්ත සමුදාය පරාජය කරන්නේ කලාතුරකිනි. දත්ත ලබා ගැනීම සඳහා ප්‍රශස්ත ප්‍රවේශය තෝරාගැනීමේදී දත්ත සමුදාය සෑම විටම පාහේ ඉතා හොඳයි. සමහර විට ඩී.බී.එස් හි මුල් දිනවල ඔබේ ප්‍රශ්නය ඩී.බී. සමඟ සංයුක්ත කිරීම සඳහා සුවිශේෂී ආකාරයකින් 'වාක්‍ය ඛණ්ඩයක්' කිරීමට අවශ්‍ය විය, නමුත් දශක ගණනාවක ලෝක මට්ටමේ ඉංජිනේරු විද්‍යාවෙන් පසුව, ඔබේ දත්ත ගබඩාවට සම්බන්ධතා ප්‍රශ්නයක් ඇසීමෙන් සහ එයට ඉඩ දීමෙන් ඔබට දැන් හොඳම කාර්ය සාධනය ලබා ගත හැකිය ඔබ වෙනුවෙන් එම දත්ත ලබා ගන්නේ කෙසේද සහ එකලස් කරන්නේ කෙසේද යන්න වර්ග කරන්න.
බල්ලන්

3
දත්ත සමුදාය දර්ශක සහ සංඛ්‍යාලේඛන උපයෝගී කර ගැනීම පමණක් නොව, සියලු මෙහෙයුම් දේශීය I / O ද වන අතර ඒවායින් බොහොමයක් බොහෝ විට ක්‍රියාත්මක වන්නේ තැටියට වඩා ඉහළ කාර්යක්ෂම හැඹිලියට එරෙහිව ය. දත්ත සමුදා ක්‍රමලේඛකයින් මේ ආකාරයේ දේවල් ප්‍රශස්ත කිරීම සඳහා විශාල අවධානයක් යොමු කරයි.
ක්‍රේග්

31

මෙය ඉතා සුලභ ප්‍රශ්නයක් බැවින්, මම මෙම ලිපිය ලිවූ අතර, මෙම පිළිතුර පදනම් වී ඇත.

N + 1 විමසුම් ගැටළුව කුමක්ද?

ප්‍රාථමික SQL විමසුම ක්‍රියාත්මක කිරීමේදී ලබා ගත හැකි එකම දත්ත ලබා ගැනීම සඳහා දත්ත ප්‍රවේශ රාමුව N අතිරේක SQL ප්‍රකාශයන් ක්‍රියාත්මක කළ විට N + 1 විමසුම් ගැටළුව සිදු වේ.

N හි වටිනාකම විශාල වන තරමට විමසුම් ක්‍රියාත්මක වන තරමට කාර්ය සාධන බලපෑම වැඩි වේ. මන්දගාමී විමසුම් සොයා ගැනීමට ඔබට උපකාර කළ හැකි මන්දගාමී විමසුම් ලොගය මෙන් නොව , N + 1 නිකුතුව හඳුනාගත නොහැකි වනුයේ මන්දගාමී විමසුම් ලොගය අවුලුවාලීමට එක් එක් අමතර විමසුම් ප්‍රමාණවත් තරම් වේගයෙන් ධාවනය වන බැවිනි.

ගැටළුව වන්නේ ප්‍රතිචාර කාලය මන්දගාමී කිරීමට ප්‍රමාණවත් කාලයක් ගතවන අතිරේක විමසුම් විශාල සංඛ්‍යාවක් ක්‍රියාත්මක කිරීමයි.

ගේ අපි පිහිටුවීමට වන පහත සඳහන් තනතුර හා post_comments දත්ත සමුදාය වගු ගැන අපි දැන් සලකා බලමු ඒක-බහු වගුව සම්බන්ධතාවය :

<code> post </code> සහ <code> post_comments </code> වගු

අපි පහත postපේළි 4 නිර්මාණය කිරීමට යන්නෙමු :

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)

තවද, අපි post_commentළමා වාර්තා 4 ක් ද නිර්මාණය කරමු :

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)

සරල SQL සමඟ N + 1 විමසුම් ගැටළුව

ඔබ post_commentsමෙම SQL විමසුම භාවිතා කිරීම තෝරා ගන්නේ නම් :

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
    );
}

ඔබ N + 1 විමසුම් ගැටළුව අවුලුවාලීමට යන්නේ එක් SQL විමසුමක් වෙනුවට ඔබ 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 විමසුම් ගැටළුව විසඳීම ඉතා පහසුය. ඔබ කළ යුත්තේ මුල් SQL විමසුමේදී ඔබට අවශ්‍ය සියලුම දත්ත උපුටා ගැනීම පමණි:

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
    );
}

අප තවදුරටත් භාවිතා කිරීමට උනන්දුවක් දක්වන සියලුම දත්ත ලබා ගැනීම සඳහා මෙවර ක්‍රියාත්මක වන්නේ එක් SQL විමසුමක් පමණි.

JPA සහ ශිශිරතාරක සමඟ N + 1 විමසුම් ගැටළුව

JPA සහ Hibernate භාවිතා කරන විට, ඔබට N + 1 විමසුම් ගැටළුව අවුලුවාලිය හැකි ක්‍රම කිහිපයක් තිබේ, එබැවින් ඔබට මෙම තත්වයන් වළක්වා ගත හැකි ආකාරය දැන ගැනීම ඉතා වැදගත් වේ.

ඊළඟ උදාහරණ සඳහා, අපි පහත වගු වෙත වගු postසහ post_commentsවගු සිතියම් ගත කරන බව සලකන්න :

<code> Post </code> සහ <code> PostComment </code> ආයතන

JPA සිතියම්කරණය මේ වගේ ය:

@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උපායමාර්ගය N + 1 විමසුම් ගැටළු වලටද ගොදුරු වේ.

අවාසනාවට, @ManyToOneසහ @OneToOneසංගම් FetchType.EAGERපෙරනිමියෙන් භාවිතා කරයි, එබැවින් ඔබේ සිතියම්කරණය මේ ආකාරයෙන් පෙනේ නම්:

@ManyToOne
private Post post;

ඔබ FetchType.EAGERඋපාය මාර්ගය භාවිතා කරන අතර , JPQL හෝ නිර්ණායක API විමසුමක් සමඟ JOIN FETCHසමහර PostCommentආයතන පූරණය කිරීමේදී භාවිතා කිරීමට අමතක වූ සෑම අවස්ථාවකම :

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

ඔබ N + 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සංගමය නැවත පැමිණෙන පෙර ඉහළම තේ විය යුතුය ListPostCommentආයතනයන්ය.

findක්‍රමවේදය EnrityManagerඇමතීමේදී ඔබ භාවිතා කරන පෙරනිමි ලබා ගැනීමේ සැලැස්ම මෙන් නොව , JPQL හෝ නිර්ණායක API විමසුමක් මඟින් ස්වයංක්‍රීයව JOIN FETCH එන්නත් කිරීමෙන් ශිශිරතාරයට වෙනස් කළ නොහැකි පැහැදිලි සැලැස්මක් අර්ථ දක්වයි. එබැවින්, ඔබ එය අතින් කළ යුතුය.

ඔබට 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()
    );
}

මෙවර හයිබර්නේට් තනි SQL ප්‍රකාශයක් ක්‍රියාත්මක කරයි:

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සියලු සංගම් සඳහා පැහැදිලිවම භාවිතා කිරීමට මාරු වුවද , ඔබට තවමත් N + 1 නිකුතුවට සම්බන්ධ විය හැකිය.

මෙවර postසංගමය මේ ආකාරයට සිතියම් ගත කර ඇත:

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

දැන්, ඔබ PostCommentආයතන ලබා ගන්නා විට:

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

ශිශිරතාරකය තනි SQL ප්‍රකාශයක් ක්‍රියාත්මක කරයි:

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()
    );
}

ඔබට N + 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සංගමයේ ඉහළම තේ lazily වේ, ලඝු-සටහන පණිවිඩය ඉදි කිරීම සඳහා කම්මැලි සංගමය වෙත ප්රවේශ වෙන විට ද්විතීයික SQL ප්රකාශ ක්රියාත්මක කරනු ඇත.

නැවතත්, නිවැරදි JOIN FETCHකිරීම JPQL විමසුමට වගන්තියක් එක් කිරීමෙන් සමන්විත වේ :

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උදාහරණයක් ලෙස, මෙම JPQL විමසුම තනි SQL ප්රකාශ උත්පාදනය කරනු ඇත.

ඔබ භාවිතා කරන්නේ FetchType.LAZYසහ ද්විපාර්ශ්වික @OneToOneJPA සම්බන්ධතාවයක ළමා ඇසුර ගැන සඳහන් නොකලත්, ඔබට තවමත් N + 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උත්පාදනය වන යටින් පවතින SQL ප්‍රකාශයන් තහවුරු කිරීමට ඔබට උපයෝගීතාව භාවිතා කළ යුතුය :

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 ක් සහිත දෙවන මෝටර් රථය), මන්ද LIMIT මඟින් මූල වගන්තිය පමණක් නොව සමස්ත ප්‍රති results ල සීමා කරනු ඇත.
CappY

2
ඒ සඳහා මට ලිපියක් ද තිබේ.
ව්ලැඩ් මිහාල්සියා

ලිපියට ස්තූතියි. මම එය කියවමි. වේගවත් අනුචලනයෙන් - විසඳුම කවුළු ක්‍රියාකාරිත්වය බව මම දුටුවෙමි, නමුත් ඒවා මාරියා ඩීබී හි තරමක් අලුත් ය - එබැවින් පැරණි අනුවාදවල ගැටලුව පවතී. :)
CappY

@ ව්ලැඩ්මිහාල්සියා, එන් + 1 ගැටලුව පැහැදිලි කරන අතරතුර ඔබ බොහෝ ටූඕන් නඩුව ගැන සඳහන් කරන සෑම අවස්ථාවකම මම ඔබේ ලිපියෙන් හෝ ලිපියෙන් පෙන්වා දුන්නෙමි. නමුත් ඇත්ත වශයෙන්ම N + 1 නිකුතුවට අදාළ OneToMany නඩුව කෙරෙහි වැඩි කැමැත්තක් දක්වන පුද්ගලයින්. කරුණාකර OneToMany නඩුව යොමු කර පැහැදිලි කළ හැකිද?
ජේ. ජේ බීම්

27

මෙම ගැටළුව නිසා අපි ජැන්ගෝහි ORM වෙතින් ඉවත් වුණෙමු. මූලික වශයෙන්, ඔබ උත්සාහ කර කරන්නේ නම්

for p in person:
    print p.car.colour

ORM විසින් සියලු පුද්ගලයින් සතුටින් ආපසු එවනු ඇත (සාමාන්‍යයෙන් පුද්ගල වස්තුවක අවස්ථා ලෙස), නමුත් එවිට එක් එක් පුද්ගලයා සඳහා කාර් වගුව විමසීමට අවශ්‍ය වනු ඇත.

මේ සඳහා සරල හා ඉතා effective ලදායී ප්‍රවේශයක් නම් මම " රසික රසිකාවියන් " ලෙස හඳුන්වන අතර එය සම්බන්ධතා දත්ත ගබඩාවක විමසුම් ප්‍රති results ල විමසුම රචනා කරන ලද මුල් වගු වෙත නැවත සිතියම් ගත කළ යුතුය යන විකාර අදහස මග හැරේ.

පියවර 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: පරමාර්ථය

තෙවන අයිතමයෙන් පසුව බෙදීමට තර්කයක් සහිත සාමාන්‍ය වස්තු නිර්මාපකයෙකු වෙත ප්‍රති results ල උරා ගන්න. මෙයින් අදහස් කරන්නේ "ජෝන්ස්" වස්තුව එක් වරකට වඩා සෑදිය නොහැකි බවයි.

පියවර 3: විදැහුම්කරණය

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

පයිතන් සඳහා විදුලි පංකාවක් ක්‍රියාත්මක කිරීම සඳහා මෙම වෙබ් පිටුව බලන්න .


10
මට සතුටුයි මම ඔබේ තනතුරට පැකිලීම ගැන, මන්ද මම පිස්සු වැටෙනු ඇතැයි සිතුවෙමි. මම N + 1 ගැටලුව ගැන දැනගත් විට, මගේ ක්ෂණික සිතුවිල්ල වූයේ, ඔබට අවශ්‍ය සියලු තොරතුරු අඩංගු දර්ශනයක් ඔබ නිර්මාණය නොකරන්නේ මන්ද? ඔබ මගේ ස්ථාවරය වලංගු කළා. ස්තූතියි සර්.
සංවර්ධකයෙක්

16
මෙම ගැටළුව නිසා අපි ජැන්ගෝ හි ORM වෙතින් ඉවත් වුණෙමු. හහ්? select_relatedමෙය විසඳීම සඳහා අදහස් කරන ජැන්ගෝ සතුව ඇත - ඇත්ත වශයෙන්ම, එහි ලියකියවිලි ඔබේ p.car.colourඋදාහරණයට සමාන උදාහරණයකින් ආරම්භ වේ.
ඒඩ්‍රියන් 17

8
මෙය පැරණි ඇන්ස්වර් ය, අපට දැන් select_related()සහ ජැන්ගෝහි ඇත prefetch_related().
මාරියස් ජම්රෝ

1
සිසිල්. එහෙත් select_related(), මිතුරා වැනි සම්බන්ධතාවයක පැහැදිලිවම ප්‍රයෝජනවත් උපුටා ගැනීම් කිසිවක් නොකරන බව පෙනේ LEFT OUTER JOIN. ගැටළුව අතුරුමුහුණත් ගැටළුවක් නොවේ, නමුත් වස්තූන් සහ සම්බන්ධතා දත්ත සිතියම් ගත කළ හැකි ය යන අමුතු අදහස සමඟ කළ යුතු ගැටළුවක් .... මගේ මතය අනුව.
rorycl

19

මෙන්න ගැටලුව පිළිබඳ හොඳ විස්තරයක්

දැන් ඔබ ගැටලුව තේරුම් ගෙන ඇති බැවින් ඔබේ විමසුමට සම්බන්ධ වීමක් කිරීමෙන් එය සාමාන්‍යයෙන් වළක්වා ගත හැකිය. මෙය මූලික වශයෙන් කම්මැලි පටවන ලද වස්තුව ලබා ගැනීමට බල කරයි, එවිට n + 1 විමසුම් වෙනුවට දත්ත එක් විමසුමකින් ලබා ගනී. මෙය උපකාරී වේ යැයි සිතමි.


18

ඔබට COMPANY සහ EMPLOYEE තිබේ යැයි සිතමු. COMPANY හි බොහෝ සේවකයින් සිටී (එනම් සේවකයාට COMPANY_ID ක්ෂේත්‍රයක් ඇත).

සමහර O / R වින්‍යාසයන් තුළ, ඔබ සිතියම්ගත කළ සමාගම් වස්තුවක් ඇති විට සහ එහි සේවක වස්තු වෙත ප්‍රවේශ වීමට ගිය විට, O / R මෙවලම සෑම සේවකයෙකු සඳහාම එක් තේරීමක් කරනු ඇත, ඔබ කෙලින්ම SQL වලින් දේවල් කරන්නේ නම්, ඔබට හැකිය select * from employees where company_id = XX. මේ අනුව N (සේවකයින් #) ප්ලස් 1 (සමාගම)

EJB Entity Beans හි ආරම්භක අනුවාදයන් ක්‍රියාත්මක වූයේ එලෙස ය. හයිබර්නේට් වැනි දේවල් මෙයින් ඉවත් වී ඇති බව මම විශ්වාස කරමි, නමුත් මට එතරම් විශ්වාස නැත. බොහෝ මෙවලම් සාමාන්‍යයෙන් සිතියම් ගත කිරීම සඳහා ඔවුන්ගේ උපාය මාර්ග පිළිබඳ තොරතුරු ඇතුළත් වේ.


17

මාතෘකාව පිළිබඳ අයින්ඩේ පෝස්ට් එක පරීක්ෂා කරන්න: එන්හයිබර්නේට් හි තෝරාගත් එන් + 1 ගැටළුව සමඟ සටන් කිරීම .

මූලික වශයෙන්, NHibernate හෝ EntityFramework වැනි ORM භාවිතා කරන විට, ඔබට එක සිට බොහෝ (ප්‍රධාන-විස්තර) සම්බන්ධතාවයක් තිබේ නම්, සහ එක් එක් ප්‍රධාන වාර්තාවකට සියලු විස්තර ලැයිස්තුගත කිරීමට අවශ්‍ය නම්, ඔබට N + 1 විමසුම් ඇමතුම් ලබා ගත යුතුය. දත්ත සමුදාය, "එන්" යනු ප්‍රධාන වාර්තා ගණන වේ: සියලු ප්‍රධාන වාර්තා ලබා ගැනීම සඳහා විමසුමක් 1 ක් සහ එක් ප්‍රධාන වාර්තාවකට එක් විස්තරයක් ලබා ගැනීම සඳහා එන් විමසීම්.

තවත් දත්ත සමුදා විමසුම් ඇමතුම් → වැඩි ප්‍රමාද කාලයක් application යෙදුම / දත්ත සමුදායේ ක්‍රියාකාරිත්වය අඩුවීම.

කෙසේ වෙතත්, ORM වලට මෙම ගැටළුව මඟහරවා ගැනීමට විකල්ප ඇත, ප්‍රධාන වශයෙන් JOINs භාවිතා කිරීම.


3
බැඳීම හොඳ විසඳුමක් නොවේ (බොහෝ විට), මන්ද ඒවා කාටේෂියානු නිෂ්පාදනයක් විය හැකිය, එයින් අදහස් වන්නේ ප්‍රති result ල පේළි ගණන යනු එක් එක් ළමා වගුවේ ප්‍රති results ල ගණන සමඟ ගුණ කළ මූල වගු ප්‍රති results ල ගණනයි. බහු ධූරාවලි මට්ටම් වලට වඩා විශේෂයෙන් නරක ය. එක් එක් "පෝස්ට්" 100 ක් සහිත "බ්ලොග්" 20 ක් සහ එක් එක් පෝස්ට් එකෙහි "අදහස් 10 ක්" තෝරා ගැනීමෙන් ප්රති result ල පේළි 20000 ක් ලැබෙනු ඇත. NHibernate හි "කණ්ඩායම් ප්‍රමාණය" (දෙමාපිය අයිඩී වල වගන්තියක් ඇති දරුවන් තෝරන්න) හෝ "උප තේරීම" වැනි ක්‍රියාමාර්ග ඇත.
එරික් හාර්ට්

14

එක් එක් ප්‍රති .ල 1 බැගින් වන විමසුම් 100 ක් නිකුත් කිරීමට වඩා ප්‍රති results ල 100 ක් ලබා දෙන 1 විමසුමක් නිකුත් කිරීම වඩා වේගවත් ය.


13

මගේ මතය අනුව ශිශිරතාර්‍ගයේ වැටී ඇති ලිපිය : සබඳතා කම්මැලි විය යුත්තේ මන්ද යන්න සැබෑ N + 1 නිකුතුවට හරියටම ප්‍රතිවිරුද්ධ දෙයකි.

ඔබට නිවැරදි පැහැදිලි කිරීමක් අවශ්‍ය නම් කරුණාකර ශිශිර - 19 වන පරිච්ඡේදය: කාර්ය සාධනය වැඩි දියුණු කිරීම - උපාය මාර්ග ලබා ගැනීම

තේරීම ලබා ගැනීම (සුපුරුදු පරිදි) N + 1 තේරීම් ගැටළු වලට අතිශයින්ම ගොදුරු වේ, එබැවින් අපට සම්බන්ධ වීම ලබා ගැනීම සක්‍රීය කිරීමට අවශ්‍ය විය හැකිය


2
මම ශිශිර පිටුව කියෙව්වා. එය කුමක් කියන්නේ නැහැ එන් + 1 තේරීම ප්රශ්නයක් ඇත්තටම වේ . නමුත් එය නිවැරදි කිරීමට ඔබට බැඳීම් භාවිතා කළ හැකි බව පවසයි.
ඉයන් බොයිඩ්

3
තෝරාගත් ප්‍රකාශනයක දී බහු දෙමව්පියන් සඳහා ළමා වස්තු තෝරා ගැනීම සඳහා කණ්ඩායම් ප්‍රමාණය අවශ්‍ය වේ. උප තේරීම තවත් විකල්පයක් විය හැකිය. ඔබට බහු ධූරාවලි මට්ටම් තිබේ නම් සහ කාටිසියානු නිෂ්පාදනයක් නිර්මාණය කර ඇත්නම් බැඳීම නරක විය හැකිය.
එරික් හාර්ට්

10

සපයන ලද සබැඳියට n + 1 ගැටලුවට ඉතා සරල උදාහරණයක් ඇත. ඔබ එය ශිශිරත්වයට යොදනවා නම් එය මූලිකවම කතා කරන්නේ එකම දෙයයි. ඔබ වස්තුවක් විමසන විට, ආයතනය පටවනු ලැබේ, නමුත් ඕනෑම සංගමයක් (වෙනත් ආකාරයකින් වින්‍යාසගත නොකළහොත්) කම්මැලි වනු ඇත. එබැවින් මූල වස්තූන් සඳහා එක් විමසුමක් සහ මේ සෑම එකක් සඳහාම සංගම් පැටවීම සඳහා තවත් විමසුමක්. ආපසු ලබා දුන් වස්තූන් 100 ක් යනු එක් ආරම්භක විමසුමක් වන අතර පසුව එක් එක් සඳහා සම්බන්ධතාවය ලබා ගැනීම සඳහා අතිරේක විමසුම් 100 ක්, n + 1.

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


9

එක් කෝටිපතියෙකුට එන් කාර් තිබේ. ඔබට සියලු (4) රෝද ලබා ගැනීමට අවශ්‍යයි.

එක් (1) විමසුමක් මඟින් සියලුම මෝටර් රථ පටවනු ලැබේ, නමුත් එක් එක් (එන්) මෝටර් රථ සඳහා රෝද පැටවීම සඳහා වෙනම විමසුමක් ඉදිරිපත් කරනු ලැබේ.

පිරිවැය:

දර්ශක බැටළුවාට ගැලපේ යැයි උපකල්පනය කරන්න.

1 + N විමසුම් විග්‍රහ කිරීම සහ සැලසුම් කිරීම + දර්ශක සෙවීම සහ 1 + N + (N * 4) ගෙවීම් පැටවීම සඳහා තහඩු ප්‍රවේශය.

දර්ශක බැටළුවාට නොගැලපේ යැයි උපකල්පනය කරන්න.

නරකම අවස්ථාවක අමතර පිරිවැය 1 + N තහඩුව පැටවීමේ දර්ශකය සඳහා ප්‍රවේශ වේ.

සාරාංශය

බෝතල් බෙල්ල යනු තහඩු ප්‍රවේශයයි (hdd හි තත්පරයට 70 වතාවක් අහඹු ලෙස ප්‍රවේශ වීම) උනන්දුවක් දක්වන එක්වීමක් තෝරාගැනීමේදී ගෙවීම් සඳහා 1 + N + (N * 4) තහඩුවට ප්‍රවේශ වේ. එබැවින් දර්ශක බැටළුවාට ගැලපෙන්නේ නම් - ගැටළුවක් නැත, එය වේගවත් වන්නේ බැටළු මෙහෙයුම් පමණක් සම්බන්ධ වන බැවිනි.


9

N + 1 තෝරාගත් ගැටළුව වේදනාවක් වන අතර, ඒකක පරීක්ෂණ වලදී එවැනි අවස්ථා හඳුනා ගැනීම අර්ථවත් කරයි. දී ඇති පරීක්ෂණ ක්‍රමයක් මගින් ක්‍රියාත්මක කරන ලද විමසුම් ගණන හෝ අත්තනෝමතික කේත වාරණයක් සත්‍යාපනය කිරීම සඳහා මම කුඩා පුස්තකාලයක් සකස් කර ඇත්තෙමි - ජේඩීබීසී ස්නයිෆර්

ඔබේ පරීක්ෂණ පන්තියට විශේෂ JUnit රීතියක් එක් කර ඔබේ පරීක්ෂණ ක්‍රම පිළිබඳ අපේක්ෂිත විමසුම් ගණන සමඟ විවරණය කරන්න:

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

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

5

අනෙක් අය වඩාත් අලංකාර ලෙස ප්‍රකාශ කර ඇති ගැටළුව නම්, ඔබට එක්කෝ ටෝමානි තීරුවල කාටිසියානු නිෂ්පාදනයක් තිබීම හෝ ඔබ කරන්නේ එන් + 1 තේරීම් ය. එක්කෝ පිළිවෙලින් දැවැන්ත ප්‍රති results ල හෝ දත්ත සමුදාය සමඟ කතාබස් කරන්න.

මට පුදුමයි මේ ගැන සඳහන් නොවුනත් මම මේ ප්‍රශ්නය වටහාගෙන ඇති ආකාරය ... මම අර්ධ තාවකාලික අයිඩී වගුවක් සාදමි . ඔබට IN ()වගන්ති සීමාව ඇති විට මම මෙයද කරමි .

මෙය සෑම අවස්ථාවකම ක්‍රියා නොකරයි (බොහෝ විට බහුතරයක්වත් නොවේ) නමුත් ඔබට කාටේෂියානු නිෂ්පාදිතය අතපසු වන ළමා වස්තූන් රාශියක් තිබේ නම් එය විශේෂයෙන් හොඳින් ක්‍රියාත්මක වේ (එනම් OneToManyතීරු ගොඩක් ප්‍රති results ල ගණන වනු ඇත a තීරු ගුණ කිරීම) සහ එහි වැඩිපුරම රැකියාව වැනි ය.

පළමුව ඔබ ඔබේ මව් වස්තු අයිඩී කාණ්ඩයක් ලෙස අයිඩී වගුවකට ඇතුළත් කරන්න. මෙම batch_id යනු අපගේ යෙදුම තුළ අප ජනනය කරන දෙයක් වන අතර එය තදින් අල්ලා ගනී.

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

දැන් සෑම OneToManyතීරුවකටම ඔබ ළමා වගුව (හෝ අනෙක් අතට) සමඟ SELECTඅයිඩී වගුවේ කරන්න . ප්‍රති id ල තීරු ඒකාබද්ධ කිරීම පහසු කරවන බැවින් ඔබට හැඳුනුම් තීරුව මඟින් ඇණවුම් කරන බවට සහතික කර ගැනීමට ඔබට අවශ්‍යය (එසේ නොමැති නම් එය එතරම් නරක නොවිය හැකි සමස්ත ප්‍රති result ල කට්ටලය සඳහා ඔබට හැෂ්මැප් / වගුවක් අවශ්‍ය වේ).INNER JOINWHERE batch_id=

ඔබ වරින් වර අයිඩී වගුව පිරිසිදු කරන්න.

කිසියම් ආකාරයක තොග සැකසුම් සඳහා පරිශීලකයා 100 ක් හෝ ඊට වැඩි අයිතමයක් තෝරා ගන්නේ නම් මෙයද විශේෂයෙන් ක්‍රියාත්මක වේ. විශේෂිත අයිඩී 100 තාවකාලික වගුවේ තබන්න.

දැන් ඔබ කරන විමසුම් ගණන OneToMany තීරු ගණන අනුව වේ.


1

මැට් සොල්නිට් උදාහරණය ගන්න, ඔබ කාර් සහ රෝද අතර සම්බන්ධයක් කම්මැලි ලෙස අර්ථ දක්වන අතර ඔබට රෝද ක්ෂේත්‍ර කිහිපයක් අවශ්‍ය යැයි සිතන්න. මෙයින් අදහස් කරන්නේ පළමු තේරීමෙන් පසුව, ශිශිරතාරකය සෑම මෝටර් රථයක් සඳහාම car_id =: id යන රෝද වලින් "තෝරන්න *" කිරීමට යන බවයි.

මෙය එක් එක් N මෝටර් රථයෙන් පළමු තේරීම සහ තවත් 1 තේරීමක් කරයි, එබැවින් එය n + 1 ගැටලුව ලෙස හැඳින්වේ.

මෙය වළක්වා ගැනීම සඳහා, සංගමය උනන්දුවෙන් ලබා ගැනීමට සලස්වන්න, එවිට ශිශිරතාරණය එක්වීමකින් දත්ත පටවනු ලැබේ.

නමුත් අවධානය යොමු කරන්න, ඔබ බොහෝ විට ආශ්‍රිත රෝදවලට ප්‍රවේශ නොවන්නේ නම්, එය කම්මැලි ලෙස තබා ගැනීම හෝ නිර්ණායක සමඟ ලබා ගැනීමේ වර්ගය වෙනස් කිරීම වඩා හොඳය.


1
නැවතත්, සම්බන්ධ වීම හොඳ විසඳුමක් නොවේ, විශේෂයෙන් ධූරාවලි මට්ටම් 2 කට වඩා පටවා ඇති විට. ඒ වෙනුවට "උප-තේරීම" හෝ "කණ්ඩායම් ප්‍රමාණය" පරීක්ෂා කරන්න; අන්තිමයා දෙමව්පියන්ගේ හැඳුනුම්පත් මඟින් "in" වගන්තියේ "කාර්_අයිඩ් (1,3,4,6,7,8,11,13)" රෝද වලින් තෝරන්න.
එරික් හාර්ට්
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.