I have a legacy .Net 4 project (name it "A") which uses Linq-to-SQL to query a database and I have another .Net 4 project (name it "B") with similiar but not the same code which queries the same database as "A".
Both projects:
The auto-generated DataContext is specific for each project but instantiated the same way:
The way the Linq query is constructed is not exactly the same for the projects but the resulting Linq query is the same. The generated (T-)SQL select statement is the same as well! (monitored and verified the SQL handles on the db server)
The database server is:
If ran the monitored CPU time (on db server) increased drastically for the query of project "A" and a command timeout exception was thrown. (System.Data.SqlClient.SqlException: Timeout expired)
On the other hand the query of "B" executed within seconds (around 3). I was able to reproduce the behavior by calling the code of "A" with the same parameters again (no changes to code or database). "B" even executed within seconds at the same time "A" was increasing its CPU time.
Regrettably after a co-worker created the indices anew I can no longer reprocude the behavior. The same co-worker mentioned that the query ran fast "last month" (although no code changed from "last month"...).
I debugged the code for both projects - both DataContext instances looked alike. The db server process' sql handle contains the same SQL statement. But "A" threw a timeout exception and "B" executed within seconds - repetitive!
Why does the same Linq-to-SQL query consume much more CPU time on the database server for project "A" as for "B"?
To be precise: If the query runs "slow" due to reasons - repetitive - how can the same query run faster just because it is called by another Linq-to-SQL code?
Can there be side effects I do not know of (yet)? Are there some instance values of the DataContext I have to look at runtime specifically?
By the way: the SQL statement - via SSMS - does use the same query plan on each run.
For the sake of completeness I have linked a sample of:
Please keep in mind that I cannot disclose the full db schema nor the code nor the actual data I am querying against. (The SQL tables have other columns beside the named ones and the C# code is a bit more complex because the Linq query is constructed conditionally.)
Some properties of both DataContext
instances:
Log = null;
Transaction = null;
CommandTimeout = 60;
Connection: System.Data.SqlClient.SqlConnection;
The SqlConnection was created from a connection string like that (both cases):
"Data Source=server;Initial Catalog=sourceDb;Persist Security Info=True;User ID=user;Password=password"
There are no explicit SqlCommands being run to pass SET
options to the database session. Neither contains the inline TVF SET
options.
Although there are many possible causes of high CPU usage that occur in SQL Server, the following ones are the most common causes: High logical reads that are caused by table or index scans because of the following conditions: Out-of-date statistics. Missing indexes.
If you want to find the top 'n' queries that are currently in the cache, which are consuming more CPU, then you are at the right place. sys. dm_exec_query_stats DMV contains all the information about the resource (CPU, Memory, I/O) consuming queries that are currently in the cache.
Stored procedures are faster as compared to LINQ query since they have a predictable execution plan and can take the full advantage of SQL features. Hence, when a stored procedure is being executed next time, the database used the cached execution plan to execute that stored procedure.
You need to run a trace on SQL Server instead of debugging this from the C# side. This will show you everything both A
and B
are executing on the server. The execution plan does you no good because it's precisely that - just a plan. You want to see the exact statements and their actual performance metrics.
In the rare event you were to tell me that both SELECT
statements are exactly the same but had vastly different performance I would be virtually certain they are running under different transaction isolation levels. A single SQL command is an implicit transaction even if you aren't explicitly creating any.
If for whatever reason the trace doesn't make it clear you should post the commands being ran along with their metrics.
Note: running a trace has some performance overhead cost to it so I would try to keep the timeframe small or run during off-peak if possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With