में तेज़ी से है, मैं एक ऐसे अनुप्रयोग को विकसित कर रहा हूं जो छवियों और संबंधित मेटाडेटा को संग्रहीत करता हो। NHHernate का उपयोग करके एक निश्चित क्वेरी करते समय मैं मुद्दों में भाग ले रहा हूं। क्वेरी निषिद्ध रूप से लंबी (मेरी मशीन पर 31 सेकंड की तरह कुछ ले रही है) ले रही है, हालांकि SQL सर्वर प्रबंधन स्टूडियो में निष्पादित होने पर वही क्वेरी केवल सेकंड का एक अंश लेती है।क्वेरी क्लाइंट ऐप में बहुत अधिक समय लेती है लेकिन SQL सर्वर प्रबंधन स्टूडियो
मैं कम है और एक छोटे से परीक्षण आवेदन करने के लिए समस्या extraced है:
संस्थाओं:
टैग, ईद की (स्ट्रिंग, टैग मान ही)
public class Tag
{
public virtual string Id { get; set; }
}
मिलकर छवि, जिसमें आईडी (int), नाम (स्ट्रिंग) और टैग शामिल हैं (कई से कई, टैग उदाहरण)
public class Image
{
private Iesi.Collections.Generic.ISet<Tag> tags = new HashedSet<Tag>();
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IEnumerable<Tag> Tags
{
get { return tags; }
}
public virtual void AddTag(Tag tag)
{
tags.Add(tag);
}
}
मैं "कोड से मानचित्रण" का उपयोग कर रहा
निम्नलिखित मैपिंग के साथ:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
<property name="connection.connection_string_name">PrimaryDatabase</property>
<property name="format_sql">true</property>
</session-factory>
</hibernate-configuration>
<connectionStrings>
<add name="PrimaryDatabase" providerName="System.Data.SqlClient" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=PerfTest;Integrated Security=True" />
</connectionStrings>
मैं निम्नलिखित हासिल करना चाहते हैं:
public class TagMapping : ClassMapping<Tag>
{
public TagMapping()
{
Id(x => x.Id, map => map.Generator(Generators.Assigned));
}
}
public class ImageMapping : ClassMapping<Image>
{
public ImageMapping()
{
Id(x => x.Id, map => map.Generator(Generators.Native));
Property(x => x.Name);
Set(x => x.Tags,
map => map.Access(Accessor.Field),
map => map.ManyToMany(m2m => { }));
}
}
NHibernate/डेटाबेस विन्यास इस तरह दिखता है क्वेरी: मुझे उन सभी छवियां दें जहां नाम में एक विशिष्ट स्ट्रिंग है या जहां किसी टैग में एक विशिष्ट स्ट्रिंग है। उत्तरार्द्ध को खोजने के लिए मैं एक सबक्वायरी का उपयोग करता हूं जो मुझे मेल खाने वाले टैग के साथ सभी छवियों के आईडी देता है। तो अंत में खोज मानदंड हैं: छवि में एक विशिष्ट स्ट्रिंग वाला नाम है या इसकी आईडी सबक्वायरी द्वारा लौटाई गई है।
var term = "abc";
var mode = MatchMode.Anywhere;
var imagesWithMatchingTag = QueryOver.Of<Image>()
.JoinQueryOver<Tag>(x => x.Tags)
.WhereRestrictionOn(x => x.Id).IsLike(term, mode)
.Select(x => x.Id);
var qry = session.QueryOver<Image>()
.Where(Restrictions.On<Image>(x => x.Name).IsLike(term, mode) ||
Subqueries.WhereProperty<Image>(x => x.Id).In(imagesWithMatchingTag))
.List();
परीक्षण डेटाबेस (DBMS: एसक्यूएल सर्वर 2008 एक्सप्रेस आर 2) मैं इस क्वेरी के लिए इस परीक्षण के लिए विशेष रूप से बनाया गया था और कुछ और शामिल नहीं है चलाने
यहाँ कोड है कि क्वेरी को निष्पादित करता है। मैं इसे यादृच्छिक डेटा से भरा है: 10.000 छवियों (तालिका छवि), छवियों और टैग (तालिका टैग), यानी के बीच 4.000 टैग (तालिका टैग) और मोटे तौर पर 200.000 संघों। प्रत्येक छवि में लगभग 20 संबंधित टैग होते हैं। डेटाबेस
करें SQL NHibernate का उपयोग करने का दावा करता है:
SELECT
this_.Id as Id1_0_,
this_.Name as Name1_0_
FROM
Image this_
WHERE
(
this_.Name like @p0
or this_.Id in (
SELECT
this_0_.Id as y0_
FROM
Image this_0_
inner join
Tags tags3_
on this_0_.Id=tags3_.image_key
inner join
Tag tag1_
on tags3_.elt=tag1_.Id
WHERE
tag1_.Id like @p1
)
);
@p0 = '%abc%' [Type: String (4000)], @p1 = '%abc%' [Type: String (4000)]
इस क्वेरी मैं बना रहा हूं दिया उचित लग रहा है।
यदि मैं NHBernate का उपयोग करके यह क्वेरी चलाता हूं तो क्वेरी में लगभग 30+ सेकंड (NHibernate.AdoNet.AbstractBatcher - ExecuteReader took 32964 ms
) लेते हैं और 98 इकाइयां लौटाते हैं।
हालांकि, अगर मैं Sql सर्वर प्रबंधन स्टूडियो के अंदर सीधे एक बराबर क्वेरी निष्पादित करें:
DECLARE @p0 nvarchar(4000)
DECLARE @p1 nvarchar(4000)
SET @p0 = '%abc%'
SET @p1 = '%abc%'
SELECT
this_.Id as Id1_0_,
this_.Name as Name1_0_
FROM
Image this_
WHERE
(
this_.Name like @p0
or this_.Id in (
SELECT
this_0_.Id as y0_
FROM
Image this_0_
inner join
Tags tags3_
on this_0_.Id=tags3_.image_key
inner join
Tag tag1_
on tags3_.elt=tag1_.Id
WHERE
tag1_.Id like @p1
)
);
क्वेरी ज्यादा एक सेकंड से कम समय लगता है (और रिटर्न 98 के परिणामों की भी)।
इसके अलावा प्रयोगों:
यदि मैं केवल नाम से या केवल टैग द्वारा, यानी खोज .:
var qry = session.QueryOver<Image>()
.Where(Subqueries.WhereProperty<Image>(x => x.Id).In(imagesWithMatchingTag))
.List();
या
var qry = session.QueryOver<Image>()
.Where(Restrictions.On<Image>(x => x.Name).IsLike(term, mode))
.List();
प्रश्नों तेजी से कर रहे हैं।
मैं की तरह उपयोग नहीं करते हैं, लेकिन मेरे सबक्वेरी में एक सटीक मिलान:
var imagesWithMatchingTag = QueryOver.Of<Image>()
.JoinQueryOver<Tag>(x => x.Tags)
.Where(x => x.Id == term)
.Select(x => x.Id);
क्वेरी तेज, भी है।
नाम के लिए मिलान मोड को सटीक रूप से बदलना कुछ भी नहीं बदलता है।
जब मैं कार्यक्रम डिबग और जब क्वेरी कामयाब कॉल स्टैक के शीर्ष निष्पादित हो रहा है थामने की तरह दिखता है:
[Managed to Native Transition]
System.Data.dll!SNINativeMethodWrapper.SNIReadSync(System.Runtime.InteropServices.SafeHandle pConn, ref System.IntPtr packet, int timeout) + 0x53 bytes
System.Data.dll!System.Data.SqlClient.TdsParserStateObject.ReadSni(System.Data.Common.DbAsyncResult asyncResult, System.Data.SqlClient.TdsParserStateObject stateObj) + 0xa3 bytes
System.Data.dll!System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket() + 0x24 bytes
System.Data.dll!System.Data.SqlClient.TdsParserStateObject.ReadBuffer() + 0x1f bytes
System.Data.dll!System.Data.SqlClient.TdsParserStateObject.ReadByte() + 0x46 bytes
System.Data.dll!System.Data.SqlClient.TdsParser.Run(System.Data.SqlClient.RunBehavior runBehavior, System.Data.SqlClient.SqlCommand cmdHandler, System.Data.SqlClient.SqlDataReader dataStream, System.Data.SqlClient.BulkCopySimpleResultSet bulkCopyHandler, System.Data.SqlClient.TdsParserStateObject stateObj) + 0x67 bytes
System.Data.dll!System.Data.SqlClient.SqlDataReader.ConsumeMetaData() + 0x22 bytes
System.Data.dll!System.Data.SqlClient.SqlDataReader.MetaData.get() + 0x57 bytes
System.Data.dll!System.Data.SqlClient.SqlCommand.FinishExecuteReader(System.Data.SqlClient.SqlDataReader ds, System.Data.SqlClient.RunBehavior runBehavior, string resetOptionsString) + 0xe1 bytes
...
तो, मेरे सवाल कर रहे हैं:
- क्यों इतने क्वेरी करता है एनएचबीर्नेट द्वारा किए जाने पर भी लंबे समय तक एसक्यूएल का इस्तेमाल समान होता है?
- मैं अंतर से कैसे छुटकारा पा सकता हूं? क्या ऐसी कोई सेटिंग है जो इस व्यवहार का कारण बन सकती है?
मुझे पता है कि सामान्य रूप से क्वेरी दुनिया में सबसे कुशल चीज नहीं है, लेकिन यहां मुझे क्या हड़ताली है NHHernate और मैन्युअल क्वेरीिंग का उपयोग करने के बीच अंतर है। यहां निश्चित रूप से कुछ अजीब चल रहा है।
लंबी पोस्ट के लिए खेद है, लेकिन मैं इस मुद्दे के बारे में जितना संभव हो उतना शामिल करना चाहता था। आपकी मदद के लिए बहुत पहले धन्यवाद!
अद्यतन 1: मैं अधिक महत्वपूर्ण मूल्य के बिना NHProf साथ आवेदन परीक्षण किया है: NHProf पता चलता है कि निष्पादित एसक्यूएल
SELECT this_.Id as Id1_0_,
this_.Name as Name1_0_
FROM Image this_
WHERE (this_.Name like '%abc%' /* @p0 */
or this_.Id in (SELECT this_0_.Id as y0_
FROM Image this_0_
inner join Tags tags3_
on this_0_.Id = tags3_.image_key
inner join Tag tag1_
on tags3_.elt = tag1_.Id
WHERE tag1_.Id like '%abc%' /* @p1 */))
है कौन सा है मैं वास्तव में क्या पहले पोस्ट (क्योंकि है कि क्या NHibernate लिखा है पहले स्थान पर इसके लॉग के लिए)।
यहाँ NHProf
का एक स्क्रीनशॉट चेतावनी समझा जा सकता है, लेकिन व्यवहार की व्याख्या नहीं है। यह वास्तव में मुख्य क्वेरी तेजी से पड़ता है जबकि
var imagesWithMatchingTag = QueryOver.Of<Image>()
.JoinQueryOver<Tag>(x => x.Tags)
.WhereRestrictionOn(x => x.Id).IsLike(term, mode)
.Select(x => x.Id);
var ids = imagesWithMatchingTag.GetExecutableQueryOver(session).List<int>().ToArray();
var qry = session.QueryOver<Image>()
.Where(
Restrictions.On<Image>(x => x.Name).IsLike(term, mode) ||
Restrictions.On<Image>(x => x.Id).IsIn(ids))
.List();
:
अद्यतन 2 @surfen उप क्वेरी के परिणामों पहले डीबी से बाहर निकलने और मुख्य क्वेरी में उन्हें वापस रहना sugested फिर, मैं इस दृष्टिकोण को नहीं लेना चाहूंगा क्योंकि यह असली दुनिया के अनुप्रयोग में इच्छित उपयोग के साथ अच्छी तरह फिट नहीं है। यह दिलचस्प है कि यह बहुत तेज है, यद्यपि। मैं अपेक्षा करता हूं कि सबक्वायरी दृष्टिकोण समान रूप से तेज़ हो जाए क्योंकि यह बाहरी क्वेरी पर निर्भर नहीं है।
अद्यतन 3 यह एनएचबेर्नेट से संबंधित प्रतीत नहीं होता है।
var cmdText = @"SELECT this_.Id as Id1_0_,
this_.Name as Name1_0_
FROM Image this_
WHERE (this_.Name like @p0
or this_.Id in
(SELECT this_0_.Id as y0_
FROM Image this_0_
inner join Tags tags3_
on this_0_.Id = tags3_.image_key
inner join Tag tag1_
on tags3_.elt = tag1_.Id
WHERE tag1_.Id like @p1));";
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["PrimaryDatabase"].ConnectionString))
{
con.Open();
using (var txn = con.BeginTransaction())
{
using (var cmd = new SqlCommand(cmdText, con, txn))
{
cmd.CommandTimeout = 120;
cmd.Parameters.AddWithValue("p0", "%abc%");
cmd.Parameters.AddWithValue("p1", "%abc%");
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("Match");
}
}
}
txn.Commit();
}
}
अद्यतन 4
क्वेरी-योजनाओं (ज़ूम करने के लिए क्लिक करें)::
धीरे क्वेरी
फास्ट क्वेरी
निश्चित रूप से योजना में एक अंतर है।
अद्यतन 5
यह वास्तव में लगता है के रूप में है कि Sql सर्वर सहसंबद्ध किया जा रहा मैं कुछ अलग करने की कोशिश की के रूप में व्यवहार करता है सबक्वेरी: मैं अपने आप में एक सबक्वेरी के नाम से संबंधित कसौटी ले जाया गया:
var term = "abc";
var mode = MatchMode.Anywhere;
var imagesWithMatchingTag = QueryOver.Of<Image>()
.JoinQueryOver<Tag>(x => x.Tags)
.WhereRestrictionOn(x => x.Id).IsLike(term, mode)
.Select(x => x.Id);
var imagesWithMatchingName = QueryOver.Of<Image>()
.WhereRestrictionOn(x => x.Name).IsLike(term, mode)
.Select(x => x.Id);
var qry = session.QueryOver<Image>()
.Where(
Subqueries.WhereProperty<Image>(x => x.Id).In(imagesWithMatchingName) ||
Subqueries.WhereProperty<Image>(x => x.Id).In(imagesWithMatchingTag)
).List();
जेनरेट किए गए एसक्यूएल:
SELECT
this_.Id as Id1_0_,
this_.Name as Name1_0_
FROM
Image this_
WHERE
(
this_.Id in (
SELECT
this_0_.Id as y0_
FROM
Image this_0_
inner join
Tags tags3_
on this_0_.Id=tags3_.image_key
inner join
Tag tag1_
on tags3_.elt=tag1_.Id
WHERE
tag1_.Id like @p0
)
or this_.Id in (
SELECT
this_0_.Id as y0_
FROM
Image this_0_
WHERE
this_0_.Name like @p1
)
);
@p0 = '%abc%' [Type: String (4000)], @p1 = '%abc%' [Type: String (4000)]
यह और सहसंबंध को तोड़ने के लिए लगता है एक परिणाम क्वेरी beco के रूप में मेस "फास्ट" फिर से ("तेज" के रूप में "पल के लिए स्वीकार्य")। क्वेरी समय 30+ सेकंड से ~ 170ms तक चला गया। अभी भी एक हल्का सवाल नहीं है, लेकिन कम से कम मुझे यहां से जारी रखने की अनुमति देगा। मुझे पता है कि "like '%foo%'"
कभी भी तेज नहीं होगा। यदि यह सबसे बुरी स्थिति में आता है तो भी मैं एक विशेष खोज सर्वर (लुसेन, सोलर) या वास्तविक पूर्ण पाठ खोज में जा सकता हूं।
अद्यतन 6 मैं क्वेरी बिल्कुल सबक्वेरी का उपयोग नहीं करने के लिए फिर से लिखने में सक्षम था:
var qry = session.QueryOver(() => img)
.Left.JoinQueryOver(x => x.Tags,() => tag)
.Where(
Restrictions.Like(Projections.Property(() => img.Name), term, mode) ||
Restrictions.Like(Projections.Property(() => tag.Id), term, mode))
.TransformUsing(Transformers.DistinctRootEntity)
.List();
एसक्यूएल:
SELECT
this_.Id as Id1_1_,
this_.Name as Name1_1_,
tags3_.image_key as image1_3_,
tag1_.Id as elt3_,
tag1_.Id as Id0_0_
FROM
Image this_
left outer join
Tags tags3_
on this_.Id=tags3_.image_key
left outer join
Tag tag1_
on tags3_.elt=tag1_.Id
WHERE
(
this_.Name like @p0
or tag1_.Id like @p1
);
@p0 = '%abc%' [Type: String (4000)], @p1 = '%abc%' [Type: String (4000)]
हालांकि, क्वेरी अब संस्करण की तुलना में थोड़ा खराब प्रदर्शन करने वाला subqueries के साथ। मैं आगे की जांच करूंगा।
var qry = session.QueryOver<Image>()
.Where(Restrictions.On<Image>(x => x.Name).IsLike(term, mode) ||
Subqueries.WhereProperty<Image>(x => x.Id).In(imagesWithMatchingTag))
.List();
आप केवल पहली क्वेरी के लिए SQL प्रदान की:
संभावित डुप्लिकेट: http://dba.stackexchange.com/q/9167/724 –
@RowlandShaw एक डुप्लिकेट नहीं है क्योंकि यह समस्या सी # पक्ष में हो सकती है - जो इसे डीबीए – Mark
@AndreLoker के लिए भी दायरे से बाहर कर देती है - क्या आपने यह देखने के लिए एक प्रोफाइलर चलाया है कि समय कहाँ लिया जाता है – Mark