Parameterize your Ad Hoc SQL

There are two ways to submit ad hoc queries to SQL Server:

  • a string containing the parameter values
  • a string containing the parameters names and a SQLParameter array containing the values

Unparameterized SQL (passing values in ad hoc sql)

string firstName = "Bill";
string adhocSql = "SELECT *
FROM Employees
WHERE FirstName = '"+firstName+"'";

Why is this bad?

  • Allows SQL injection attack.
  • Bloats query plan cache (and therefore server RAM).  SQL Server stores every variation of this query text (for each new value).
  • Wastes server CPU on computing query plans for every variation.

For example, let’s say this query is used on a webpage to search employees by first name.

  1. User enters “Brian.” This query is passed to SQL Server: SELECT * FROM Employees WHERE FirstName = 'Brian' SQL Server computes a query plan on that, stores it in the plan cache, and then executes the plan.
  2. User enters “Sith.”  This query is passed to SQL Server: SELECT * FROM Employees WHERE FirstName = 'Sith' SQL Server computes a query plan on that, stores it in the plan cache, and then executes the plan.
  3. User enters “Sisilia.”  This query is passed to SQL Server: SELECT * FROM Employees WHERE FirstName = 'Sisilia' SQL Server computes a query plan on that, stores it in the plan cache, and then executes the plan.
  4. And so on…

Let’s say 1000 names are searched in one day.  SQL Server would compute 1000 different plans and store them all in memory.  That is wasteful for the server’s RAM and CPU.  One query plan would suffice for all these variations.

Also consider SQL injection attack.  Let’s say the user enters “Brian; DROP TABLE Employees; –”.   You’d lose your Employees table.  Not good!

Parameterized SQL (naming parameters in ad hoc sql and passing values in an array)

string firstName = "Bill";
string adhocSql = "SELECT *
FROM Employees
WHERE FirstName = @FirstName";
SQLParameter[] parameters = new [] { new SQLParameter("FirstName", firstName) };

Why is this good?

  • Prevents SQL injection.  Any malicious SQL is treated as a string value–not executable code.
  • Conserves plan cache size (and server RAM). SQL Server stores this query only once. The query text stays exactly the same, regardless of the values passed in.
  • Conserves server CPU.  SQL Server computes the query plan the first time, and then skips to execution all other times.

For example, let’s say this query is used on a webpage to search employees by first name.

  1. User enters “Brian.” This query is passed to SQL Server: SELECT * FROM Employees WHERE FirstName = @FirstName SQL Server computes a query plan on that, stores it in the plan cache, and then executes the plan.
  2. User enters “Sith.”   This query is passed to SQL Server: SELECT * FROM Employees WHERE FirstName = @FirstName SQL Server finds the query plan in cache and executes it.
  3. User enters “Sisilia.”   This query is passed to SQL Server: SELECT * FROM Employees WHERE FirstName = @FirstName SQL Server finds the query plan in cache and executes it.
  4. And so on…

Let’s say 1000 names are searched in one day.  SQL Server would compute a plan on the first run and store it in memory.  For the remaining 999 runs, it would grab the plan from memory and skip directly to execution.  Thus server RAM and CPU are conserved.

The benefits are clear. Parameterized SQL queries conserve RAM and CPU and prevent SQL injection.   So, parameterize your ad hoc SQL! :)

Posted in Performance | Tagged , , | Leave a comment

HTTP Compression (gzip) on IIS 6

The Microsoft documentation is lacking for HTTP Compression in IIS 6. And the helpful stuff is scattered all over the web.   I eventually pieced the puzzle together. And I compiled all the steps here.

There are four major steps to enable HTTP Compression in IIS 6:

  1. Configure IIS
  2. Configure the metabase.xml
  3. Reset IIS
  4. Verify it’s enabled

Configure IIS

  1. Open IIS (Control Panel -> Administrative Tools -> Internet Information Services)
  2. Right-click on the computer name.
  3. Check “Enabled Direct Metabase Edit” and click OK.
  4. Expand the compter-name node
  5. Right-click on the “Websites” folder and click Properties.
  6. Go to the Services tab.
  7. Check “Compress Application Files” and “Compress Static Files.” I kept the default temporary directory location and max size. Click OK.
  8. Left-click on the “Web Service Extensions” folder. Do you see “HTTP Compression” in the right-pane? If yes, then verify that it’s allowed and mapped to the gzip.dll. If not, then follow the next steps.
  9. Right-click on the “Web Service Extensions” folder and click “Add a new Web Service Extension.”
  10. Name it: “HTTP Compression.”
  11. Add gzip.dll from the folder: C:\WINDOWS\system32\inetsrv\
  12. Check “Set extension status to Allowed” and click OK.

Configure the metabase.xml

  1. Open Windows Explorer.
  2. Go to: C:\WINDOWS\system32\inetsrv\
  3. Open metabase.xml in Notepad.
  4. Search for the tag “IISCompressionScheme.” There are two in a row, followed by an IISCompressionSchemes tag.
  5. Replace the three tags with this snippet and save.

Notice that I added:

  • aspx, asmx, and axd to the dynamic file types
  • css, js, xml, txt, and rdf to the static file types

Only text-based files should be gzipped. Other file types, such as images, are already compressed and do not benefit from gzipping.

And I set:

  • all the Compression boolean attributes to TRUE
  • all the NoCompression attributes to FALSE
  • Compression Level to 9 (out of 10)

I copied Jeff Atwood’s metabase.xml example, except for the Compression Level. He set it to 10, and I set it to 9.

Regarding Compression Level:

“I had the opportunity of receiving an internal testing summary from Chris Adams from Microsoft regarding the compression level -vs- CPU usage which showed that the CPU needed for levels 0 – 9 is fairly low but for level 10 it hits the roof. Yet the compression for level 9 is nearly as good as level 10. I write all this to say that I recommend level 9 so make sure to change HcDynamicCompressionLevel to 9. Do this for both deflate and gzip.”

– Scott Forsyth, Director of IT at orcsweb.com; source: his blog

Reset IIS

  1. Open IIS
  2. Right-click on the computer name
  3. Go to: All Tasks->Restart IIS
  4. Use the default selection (Restart Internet Services) and click OK

Verify it’s enabled

  1. Open Firebug in Firefox.  (Firebug is a webpage monitoring add-on for Firefox).
  2. Navigate to your website.
  3. Switch to the YSlow tab in Firebug. (YSlow is an add-on for Firebug).
  4. Run the test in YSlow.
  5. Verify that you get an “A” for gzip compression.

If you don’t get an “A,” look at the files that aren’t gzipped. Check that you have those formats listed in the metabase.xml attribute, HcScriptFileExtensions, for both gzip and deflate. If they’re there, then the problem could be with the formatting of that attribute; it’s finicky.  Put each file format on its own line. And use only tabs for spacing on the line.

I goofed up on my first try with the static file list. CSS and JS formats weren’t being compressed. I must have copied-and-pasted a space character or something. I was using Notepad to edit the metabase file, and I couldn’t tell the difference.  To fix the problem: I started from scratch (“”), meticulously added each file extension, and restarted IIS. Then it worked.

Congrats

That’s it! You have now enabled HTTP Compression (gzip) in IIS 6. You should see a 60-90% reduction in your text-based payload.

Posted in Performance | Tagged | Leave a comment