Автор: Дмитрий
Приходько
По материалам дискуссий на форуме Microsoft
SQL Server проекта SQL.RU
Клиентские утилиты SQL Server 2000 (Enterprise Manager) при
работе с сервером баз данных используют функцию has_dbaccess
("database_name"), суть которой в том, что она выдает значения
0 или 1, которые определяют, доступна ли база данных в
настоящий момент. В клиентских утилитах SQL Server 7.0 данная
функция не использовалась, но в серверной части она есть,
причём выполняется гораздо медленней, чем у SQL Server
2000.
Разброс по времени выполнения на одну базу у нас
наблюдался такой: обычно -4ms, в плохом случае приблизительно
4000ms.
Особенности работы Enterprise Manager 2000 (EM):
Когда EM запрашивает список баз данных сервера, выполняется
запрос:
Select ..., has_dbaccess(name) from master..sysdatabases
Этот запрос выполняется без условия where, то есть по всем
базам данных. Мало того, этот запрос выполняется в цикле
столько раз, сколько есть баз данных на сервере. На нашем
сервере было около 100 баз, в результате их список открывался
5 - 10 минут. В SP1 для SQL Server 2000 ситуацию исправили,
теперь запрос выполнятся только один раз. Так что рекомендация
первая - ставьте сразу SP1 на SQL Server 2000. Но проблема с
долгим выполнением функции has_dbaccess осталась.
Что влияет на время выполнения этой функции?
В основном
на выполнение этой функции влияют установленные флаги:
autoclose и autoshrink. При чём очень сильно. Это замедление
заметно и на SQL Server 2000 но в меньшей степени. Исходя из
этого, следует рекомендация вторая - снять со всех баз флаги
autoclose и autoshrink.
Функция has_dbaccess() на SQL
Server 7.0 может быть заблокирована процессом восстановления
из Backup. При чем, когда процесс восстановления завершается,
оба коннекта падают, восстановление базы часто завершается с
ошибкой и его приходится повторять. Данная ситуация происходит
не всегда, но часто. Особенно эта ситуация была острой до
появления SP1. Рекомендация третья - не используйте EM 2000
без SP1 для работы с SQL Server 7.0, когда идёт восстановление
баз данных.
Общий вывод такой: функция has_dbaccess()
опасна для MS SQL Server 7.0. Скорее всего, происходит
взаимная блокировка в базе master, которая некорректно
разрешается сервером.
Зарисовки на тему has_dbaccess()
Функция has_dbaccess() используется еще в нескольких
местах.
1. В Query Analyzer когда вы выбираете базу из
списка.
2. В ODBC драйвере - настройка подключения.
То
есть потенциально любой пользователь, у которого стоит клиент
SQL Server 2000 может нарушить процесс восстановления из
Backup.
К слову, а как Query Analyzer 2000 работает с SQL Server
6.5? Там же такой функции нет. А очень просто. C начала
делается запрос, содержащий функцию has_dbaccess(name).
Естественно сервер выдает ошибку. Ошибка обрабатывается на
клиенте и выдается второй запрос просто:
select name from master..sysdatabases.
В итоге повторюсь:
1. Всегда снимайте со всех баз флаги
autoclose и autoshrink.
2. Установите на клиентских
компьютерах SP1 для SQL Server 2000.
3. Администрируйте SQL
Server 7.0 с помощью EM от 7.0. Для этого мы держим отдельный
компьютер с клиентом 7.0.
После этого можно жить, время
работы запросов резко уменьшается (но вероятность падения в
процессе восстановления базы остаётся, при чём, иногда даже
полностью падает сервер).
Рекомендация последняя - если все
базы под SQL Server 7.0 не используйте клиента для SQL Server
2000, если же начат процесс перехода на SQL Server 2000 - не
затягивайте его, побыстрее переводите все базы данных на новую
версию.
Прилагаю скрипт. Те базы, которые вы увидите, проверьте
функцией sp_dboption 'name' на соответствие представленным
выше рекомендациям.
set nocount on
if (select object_id('tempdb..#bases')) is not null exec( 'drop table #bases')
create table #bases (t_name varchar(255), t_time int , dba int)
go
declare @start datetime, @finish datetime,
@my_base varchar(255),
@dba int
select @my_base = char (0)
while (1=1)
begin
set rowcount 1
select @start = getdate()
select @my_base = name,
@dba = has_dbaccess(name)
from master..sysdatabases
where name > @my_base
order by name
if @@rowcount = 0
begin
set rowcount 0
break
end
set rowcount 0
select @finish = getdate()
insert #bases (t_name, t_time, dba)
select @my_base, datediff(ms,@start, @finish), @dba
end
select cast(t_name as varchar (30) ), t_time, dba from #bases where t_time > 20
Дополнительная информация по решению описанной в статье
проблемы содержиться в этом документе:
FIX:
Opening the Database Folder in SQL Server Enterprise Manager
2000 Takes a Long Time