views:

46

answers:

3

Hi

When sql server is receiving two queries (SELECT * From the_Same_Table), at exactly the same time, and if u have a server with multiple processors, Can sql server retrieve the data at the same time?

I am trying to understand what will happen if a cheap select statement that finish within .01 sec, and 1000 users run the same query exactly at the same time. I think what will happen that if the server is having four processors, then SQL server will serve the first four in .01 sec and serve next four users in 0.02 sec, extra.

Is that even close to what will actually happen?

I think I will try to use some kind of code and loggers to test that, or maybe there is reliable testing tools to do that.

Thanks

+2  A: 

No, your assumption of sequential processing is not correct, this topic gets very complex and deep - trying to make what I know of it simple:

Each query has a thread assigned and the scheduler uses co-operative scheduling, not pre-emptive, so each thread can give up the allocation (quanta) of time before being forced to by the scheduler. Each query is going to have to make requests for data from the buffer pool, which can mean waiting on IO / network etc, so they will hit wait states and yield their quanta of processing time.

When a process is no longer waiting on the resource is can then re-enter the list of threads available to process and will then get another quanta of time to process the request on.

In effect your requests are run in parallel, but you could not deterministically predict the order in which the requests will finish.

To get real in-depth on this I'm thinking the SQL 2008 Internals book form Kalen Delaney (Adam Mechanic, Kimberley Tripp, Paul Randal) is a good starting point, or the older SQL Server 2000 Architecture book by Ken Henderson, which was also very low level.

I would have to refer back to course notes to remember the exact process - it get's to the point of asking - 'since you can not affect this directly, why do you ask?'

Andrew
To verify the reason that will make me Cache a cheap data. based on this thread I will NOT cache cheap data that related to one customer, I will cache all data that probably needed by all customers, because in avg_case throughput will be decreased when the number of users increased. Do u agree?Thanks
Costa
You must mean caching external to SQL Server since it will handle the procedure / data cache for you. If data is requested often enough it will either be in the SQL Server data cache, or possibly be in the IO Subsystem cache (SAN cache for example).Be careful you are not performing a premature optimization with no actual instrumented data to indicate you do have a performance issue - it's ok to expect the worst case, but you have no way of knowing whether some custom coded cache will be be any faster than what you are replacing, since you have no times to compare against.
Andrew
+5  A: 

Each batch (request) that comes into SQL Server creates a task. Task are scheduled for execution and picked up by a worker. A worker is very similar to a thread. A task stays with a worker until it finishes, then it frees the worker to pick up another task. There are a limited number of workers in the system, configured by sp_configure 'max worker threads'. At a minimum there are 256 workers, from which about 35 are system. A worker needs a scheduler to run, and there is one single scheduler for each CPU core. Workers cooperate in sharing the scheduler.

Some tasks spawn subtasks, like parallel queries. These tasks are also queued for execution and need a worker to complete. A tasks that spawn subtasks cannot complete untill all the tasks it spawn are complete.

There are also some user action driven system tasks, like the login handshake. When a client opens a new conneciton, the handshake and login authentication/authorization are done by a task, which requires a worker.

When 1000 requests come to the server, 1000 tasks are created and queued for execution. The free workers pick up tasks and start executing them. As they finish one task, they pick up the next task, untill all tasks created by the 1000 requests complete.

The DMVs that show what's going on are:

These details are described in SQL Server Batch or Task Scheduling and in Slava's blog.

Further, once the task is in execution, the request will be compiled. Compilation will first look up the requests text in memory, and search for an existing compiled plan for a request with an identical plan. You can read my reply for http://stackoverflow.com/questions/1608522/dynamically-created-sql-vs-parameters-in-sql-server/1608856#1608856 for a more detailed drill into how that happens. Also see Execution Plan Caching and Reuse. Once a plan is created, it is launched into execution. A query like SELECT ... FROM table will create a trivial plan that has a just a couple of operators that basically fetch each row and place it in the TDS stream back to the client. A query plan is a tree of operators, and the query is always executed by asking the root of the tree for the next row, in a loop until the root returns EOF. The query operators down the tree get more and more specific, until the bottom operator will be a physical access to the chosen access path (the index or heap chosen by the optimizer to satisfy the query). See SQL Statement Processing. The index access will always request the data from the buffer pool, never from disk. When the buffer pool does not have the requested page cached, a PAGEIOLATCH is placed on the page and a request to read the page is submitted to the IO subsystem. Subsequent requests for the same page will wait for this IO to complete, and once the page is in the buffer pool all other queries that need this page will it from the buffer pool. unused pages are evicted when the buffer pool need free pages, but if the system has enough RAM the page will never be evicted once loaded. Index and heap scan operations will requests read-aheads, anticipating that pages ahead of the current one in the page link-chain will be requested. Read aheads are limited by the index contiguos fragments, and this is when the index fragmentation comes into picture, as it reduces the size of read-ahead requests, see Understanding Pages and Extents.

Another dimension of the query execution is the logical locking of rows. For stability, a read may place row locks or range locks, depending on the isolation model, on the rows it reads, to prevent concurent updates while the query traverses a scan. Under SNAPSHOT isolation level the query will not request locks at all, but instead a version mark will be used to possible serve the data requested from the version store (see SQL Server 2005 Row Versioning-Based Transaction Isolation). Under READ UNCOMMITED isolation (or when nolock hint is used) the query does not requests locks on the rows it reads, but the reads are inconsistent if concurent updates occur (uncommited rows are read, same row may be read twice, or an existing row may be not read at all).

Remus Rusanu
+1 Remus remembers it better than I do, surely you must be looking at the masters certification Remus?
Andrew
@Andrew: I don't have that much cash to burn :)
Remus Rusanu
Know that feeling - and I'd have hotel + flights to pay for.
Andrew
I think couple of books will help me on that :), but I have the impression that 0.1 query will not likely to be 1 second query, even there is 1000 tasks, so for caching cheap data SQL server is not the issue which I should care about, may be network traffics forward and back from database server will make me think about caching a cheap data, Do u think that I should consider that?
Costa
As a general rule, the fewer roundtrips to the server, the better the throughput. But there are sooo many details that I really can't say do this or do that. Most important factor imho is how often the data is updated. If is updated frequently, then is better to fetch it each time. If is seldom updated then you can use something like Query Notifications to get a notification when your cache is stale and refresh it. See http://rusanu.com/2006/06/17/the-mysterious-notification/. If is never updated ('never' is a dangerous word), then read it once and cache it in the client.
Remus Rusanu
BTW a 0.1 query, meaning 100 ms, is a quite expensive query. Fast queries like a lookup by key on an index usually take under 8ms. An 2 socket x 4 core server with 16 Gb RAM on a reasonable RAID array should be able to serve at least 5000-6000 requests like this per second.
Remus Rusanu
If you want good books on this, I agree with Andrew and recommend the Inside SQL 2005 series (all volumes) edited by Kelan Delaney. The MVP Deep Dive I also hear praises for, but did not have the chance to read it.
Remus Rusanu
A: 

Sql server is optimized for doing concurrent multiple reads. The only time you may run into a deadlock is if you have a lot of update operations occurring on the same table you are trying to access. However if this is the case you can use the nolock or even set the transaction isolation level to READ UNCOMMITTED.

Now in regards to your threading question. SQL Server uses something called fibers, which are like a sub thread. So you will not necessarily see the same processor thread scaling that you are expecting to see in a multi-processor environment. While SQL server can access a certain number of threads there is a max number of fibers that can be used as well. This is why you can have 1000s of clients accessing the same server at the same time on even small boxes.

If you really really want to get into the thread / fiber scheduling you will need to go find a good SQL Server nuts and bolts guy that really really gets it as it's complicated.

Just realize that sql server is optimized for this and you don't need to unit test it in any way as it's been proven already with tools you probably won't be able to recreate.

Joshua Cauble