Sie sind auf Seite 1von 5

Copyrignt (c) 1996 by Edward Barlow

SQL Server Performance And Tuning

We often define tuning as optimizing the performance of your data server, but real systems are not so simple. Performance depends on a combination of the Client Hardware and Software, the Physical Database design, the network configuration, the Server hardware, operating system, and SQL Server configuration. Most of the benefits of performance tuning come out of optimizing either the Client Application or the Physical Database Design. This paper deals with tuning your Server (UNIX and Sybase) and with optimizing your physical database design. This document refers to several stored procedures that are provided for free and can be found on the web site

Diagnosing Your Performance

When you start a tuning effort, it is important to identify the sub system that any problems are coming from. An easy place to start is to log in to UNIX and run a few iostats, vmstats, and mpstats. Multiprocessor UNIX systems should look like they are multiprocessing, and none of the disk drives should be running at over 10% capacity for any period of time. If iostat reveals significant IO, check to see if the IO is split evenly between disks. If not, you should spend some time optimizing both the UNIX and Sybase IO sub-system. Remember to test your system at steady state, not at system startup due to Sybase page caching. Use the ps command or a utility like top to monitor the top processes running in UNIX. Most production Sybase servers will be running only Sybase related processes, so the only process that should have significant utilization is your Sybase dataserver. Another way to diagnose the systems is through Sybase auditing. Sybase auditing is free, so you might as well use it, but it only provides some of the information you need (i.e. it gives a query start time but no query end time). I suggest auditing logins, logouts and queries for a few hours during a reasonably busy time and then reviewing the number of connections and the types of queries run on the system. Beware running out of space in the sybsecurity database. If this happens, you will not be able to log in to your system. If analysis of the audit reveals lots of connections, your client programs might be have a problem (some clients are coded to make one connection per query). The percentage of queries that are implemented as stored procedures should be high. There should be no adhoc insert / update / delete statements because these should be only performed through stored procedures. The final method I use to diagnose and isolate server problems is to run some of the augmented Sybase stored procedures on the Sybase server. I use these stored procedures because they print more useful output than the built in Sybase procedures. I look at system users (sp__who, sp__whodo), the number of locks ( sp__lock, sp__lockt), the overall system performance (sp__quickstats), and the number of connections, IO, and CPU used (sp_monitor).

Copyrignt (c) 1996 by Edward Barlow

Tuning Your Configuration

If iostat has identified an IO problem, the easiest way to fix it is through the purchase of a raid array. This tends to be more cost effective than tuning the IO by moving tables and indexes around. The biggest benefit comes when you replace Sybase mirroring with hardware raid mirroring. Disk striping also is very effective in enhancing your performance. Raid arrays create a large virtual disk from many smaller disks, and striping makes Sybase think it is using a single disk when in reality it is using many disks at once. Spreading the IO across disks is much faster than when you are dealing with one disk (provided the stripe size is set correctly). The volume manager that comes with your disk system allows you to manage these disk stripes. Pick a standard device size (say 500 MB) and create multiple devices for your system instead of creating one gigantic device. Sybase tends to run fine on its default configuration and I recommend not spending too much time changing internal Sybase parameters. You obviously need to adjust tempdb and the amount of RAM, and you should change the number of engines on multiprocessor systems. Systems with either a lot or a small amount of RAM should tune the data cache parameter. You can increase the network packet size on purely decision support systems. You should review your systems configuration, which can be generated by using the sp__server stored procedure. You should also be 100% sure that you update statistics on a regular basis. Transaction logging can be a bottleneck in high transaction environments. The transaction log is flushed to disk by the COMMIT at the end of each transaction. The use of multi statement transactions will reduce the number of log writes.

Tuning Your Database Design

I use the following steps to review the Data Model and to tune Sybase code: 1) The first thing to do is print a good data model. If one is not available, use your data modeling tool to reverse engineer one, inferring primary keys from the indexes that are on the model. 2) Review this model and look for missing relationships. Also look for tables that do not have a unique primary key or are self-referencing. Orphaned tables (tables with no relationships to anybody) should really not exist in a well built model. The model should have several well defined subsystems (like the error handling tables). It is important to have a simple, normalized model. 3) Look at the table layouts using the sp__helptable procedure. Mark large and small tables on the model. Identify and investigate any tables that have no rows. Ask yourself which tables are static and which are dynamic. This information is useful in the next step because you can over index static tables with no performance penalty. 4) Look at the index layout using sp__helpindex. Compare the indexes to the key layouts that are identified in the model. Review the order of the key fields in the indexes so that your obvious selects will always use the index. A clustered index is a

good choice to have on fields accessed by a range or used in order by and group by statements. An example of this is a record_create_date field which you would often access using a between statement. Indexes that are not selective will not be useful and should be discarded. Indexes that cover queries are very useful. Exceptionally long indexes are slow. The optimizer likes indexes to be unique. Dont index large fields or fields that allow nulls unless you need to because nulls make the index longer and your key fields should really not allow nulls in a good design. The stored procedure sp__badindex identifies many potential index problems. The following explains the best indexes by query type: SELECTS: Add as many indexes as possible. INSERTS: A clustered index and no non clustered indexes. The clustered index reduces contention on the last page, although there is slightly more overhead inserting into a clustered index than into a table with only non clustered indexes. UPDATES: Indexes will speed the retrieval of the row, but the updates that are not done in place act as a delete followed by an insert. DELETES: Each index adds some overhead to the operation. 5) Look at the trigger layout using sp__helptrigger and by reviewing the trigger code. Triggers should only be used if you are allowing users interactive update access to the database or to archive data to history tables. Triggers should always be as lean as possible. Use referential integrity constraints and check constraint instead of triggers. It is good practice to break trigger code into two parts based on the number of rows (@@rowcount). The syntax of these triggers should resemble select @rc=@@rowcount if @rc=1 then begin ... end else begin ... end The single row case can be simplified by selecting inserted and deleted information into variables. All trigger execution is performed within a transaction, so large triggers can lead to deadlock situations in multi user environments. Slow triggers are a major reason applications do not perform and as triggers fire in the background, developers dont really think about them. This can easily cause the perception that the whole system is slow, when in reality the system is doing a lot more work than they give it credit for. Analyze the SQL in your triggers and over optimize your trigger code. 6) Run sp__find_missing_keys. This procedure will find key relationships that do not have associated indexes. It is not incorrect for a key to not be associated with an index, but each of these missing indexes should be investigated. An example of a

key that should not be an index is a document table that has a color field which is related to the color table. If you dont care to find all red documents, document need not be indexed on color, although color is still a foreign key. 7) Look through your stored procedures for weak transact SQL statements. Certain transact SQL statements are going to force Sybase NOT to use an index. Review the next section (Tuning Transact SQL Statements). Compare WHERE clauses with existing indexes. Identify major transactions and be sure they are indexed appropriately. You can use sp_depends to identify the procedures that use each table. I find it very helpful to analyze critical transactions by drawing them out graphically. 8) Any remaining slow transactions can be tuned by using the set showplan, set statistics io, and set statistics time options. 9) Look for poor Sybase transaction design. These can often be identified by looking at locks. You never want to do table scans within a transaction. Look for long transactions (the shorter the better). Remember a trigger is an implicit transaction. 10) Look carefully at both loops and cursor coding. It is often difficult to use cursors in an easy and fast manner, but it can be done. Often cursors will prove significantly faster than while loops. Transact SQL is tuned to use set logic instead of cursor processing. 11) If you have locking contention on heavily inserted / updated tables, look at the index design. Sybase will insert to the last page if there is no clustered index or if that index is monotonically increasing or an identity column. Heavily insert environments should cluster on a key which allows rows to be inserted reasonably randomly.


The following is a brief summary of things to remember when tuning your SQL: Avoid mathematical manipulation in WHERE clauses (WHERE x * 100 > 50 ).

The use of built in functions like rtrim and upper on a column will cause that column to be ignored for the purposes of index selection. Be careful of left hand and right hand arguments. WHERE id=@x is useful for index selection, while WHERE @x=id does not. You must specify the leading portion of the index to use it. This means that if the index exists on columns a,b,c (in that order), then Sybase will not consider it for queries that only specify columns b and c. OR and IN clauses cause dynamic indexes and work tables to be created and will cause a performance penalty. Avoid multiple Or clauses, particularly on different columns within the same table. Over specify your where clauses. This means changing your query from WHERE and by adding and

Avoid comparisons of fields with data type mismatches. Comparisons of fields which are defined differently (i.e. float and int) will usually not use an index. The sp__helpcolumn is very useful to identify columns that have identical names but are of different types. The like operator will cause the optimizer to not use an index when used with wildcards at the beginning of a query (i.e. like %abc will never use an index). Not equal (!=) expressions will not be used for index selection. Avoid 5 or more table scans unless you have specifically optimized them. Optimizer tables are joined in groups of 4. Under system 11 this parameter is configurable. These joins can be reduced by using temp tables, variables, or denormalization. Make sure that the WHERE clause of Update and Delete transactions do not scan the entire table. Transactions should be as short as possible, and there should be no user interaction within a transaction. Avoid the use of WHILE loops and GOTO statements. Avoid using temp tables to communicate between stored procedures. This will cause the called procedures to be reresolved and recompiled each time. Use exists and in instead of not exists and not in. These queries will perform better and most code can easily be rewritten. Use select 1 instead of select * in exists clauses because an asterisk can cause locking problems on syscolumns when used a lot. Use exists instead of count wherever possible. Exists will stop after the first match, as opposed to count which will go through all rows to get a total count. Use >= and <= instead of > and <. >= can save substantial I/O in some cases. Use select into instead of insert/select because these operations are not logged.