    25 ГЛАВА 

    Оптимизация приложений архитектуры клиент/сервер 

    В этой главе рассматриваются способы и средства оптимизации 
производительности приложений архитектуры клиент/сервер, создаваемых с помощью 
Delphi. Методы оптимизации подразделяются на три категории: оптимизация 
приложений Delphi, оптимизация работы сервера и оптимизация работы сети. 
Изложение этой главы никоим образом не претендует на полноту; здесь скорее 
дается введение в различные способы оптимизации. Наряду с этим необходимо 
подробно изучить используемую систему управления базами данных, сеть и 
операционную систему сервера, обращая внимание на особенности каждого 
компонента рабочей среды. 

    На заметку 
    В этой главе частично затронуты вопросы оптимизации нескольких различных 
СУБД. За подробной информацией о совместной работе приложений архитектуры 
клиент/сервер с конкретными СУБД обращайтесь к главам 15-18. 

    Что такое "достаточно быстро" 

    Первое, что необходимо сделать для успешной оптимизации, —- определить ее 
цели. К сожалению, вопросы оптимизации не слишком беспокоят пользователей 
вплоть до тех пор, пока прведение системы не становится совершенно нетерпимым. 
Поэтому необходимо заранее определить, какой именно режим работы является 
оптимальным, другими словами, что должно означать утверждение "система 
работает достаточно быстро". Приступать к самой оптимизации можно только после 
определения ее целей. 

    Определение оптимизируемых параметров 

    Второе, что необходимо сделать, — определить, какие параметры необходимо 
изменить с целью оптимизации. Для этого необходимо знать, от каких параметров 
зависит производительность системы. Это, например, конфигурационные переменные 
сервера, значения свойств компонентов Delphi, параметры сети и т.п. Одной из 
целей этой главы является раскрытие роли многих системных параметров в 
производительности приложений Delphi архитектуры клиент/сервер. 

    Создание испытательной среды 

    При оптимизации производительности немаловажным является выбор сервера для 
проведения испытаний. Вначале не следует изменять находящиеся в эксплуатации 
приложения или параметры сервера. Для этого есть ряд веских причин. Самая 
очевидная из них — необходимость избежать потери или повреждения данных в ходе 
тестирования. Вот почему приложения сомнительного качества нельзя тестировать с 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер 477 

    рабочими данными. Второй причиной для проведения испытаний в отдельных 
системах является требование чистоты эксперимента. Если в ход испытаний будут 
вмешиваться другие пользователи со своими приложениями, то будут получены 
противоречивые и необъяснимые результаты. 

    Определение оптимизации производительности 

    Итак, мы рассмотрели некоторые общие положения оптимизации системы по 
производительности. Остается выяснить, что же следует понимать под этой самой 
производительностью? Как узнать, достигнуты ли оптимальные показатели? 
Производительность системы измеряется многими показателями, основные из 
которых следующие. 
    О  Количество транзакций в секунду (TPS — transactions per second). Это 
общепринятый показатель общего быстродействия системы. Для его оптимизации 
наиболее важны такие меры, как ускорение доступа к серверу из приложений, 
оптимизация работы самого сервера и распараллеливание операций. 
    Q  Время отклика на запросы (query responce time). Измерение этого 
показателя является вполне допустимым методом оценки быстродействия системы. 
Целью оптимизации в этом случае будет уменьшение этого времени. 
    Q  Время выполнения командных файлов и сценариев (batch job execution 
time). Заказчик может потребовать, чтобы конкретный командный файл или 
сценарий выполнялся не дольше определенного промежутка времени. Такой сценарий 
может содержать несколько запросов, обращений к внешним устройствам и т.п. 
Если это так, то основной целью оптимизации будет уменьшение времени 
выполнения сценария. 
    U  Время реакции приложения (application responsive ness). Заказчик может 
оценивать быстродействие системы по скорости отображения сообщений и отчетов. 
В этом случае настройка взаимодействия приложения и сервера может стать 
несколько нетривиальной задачей. 
    О   Совместный доступ (concurrency). К системе могут предъявляться 
определенные априорные требования с точки зрения обеспечивания совместного 
доступа. Например, 1000 пользователей могут одновременно добавлять строки в 
одну таблицу. Если это так, то оптимизация должна состоять в уменьшении 
конкуренции между пользователями за системные ресурсы и недопущении процессов 
взаимной блокировки. 
    Обычно понятие производительности складывается из нескольких перечисленных 
элементов. Ее зависимость от какого-нибудь одного показателя — исключительно 
редкий случай. Одной из причин этого является тесная взаимосвязь перечисленных 
показателей. Например, пусть для конкретного приложения одного клиента 
решающим фактором является количество транзакций в секунду. Но в 
многопользовательской системе этот показатель сильно зависит от того, как 
обеспечивается совместный доступ. Если процессы блокируют друг друга, то 
средняя интенсивность транзакций сильно падает. Поэтому, хотя в ходе 
оптимизации можно отдавать предпочтение одним показателям, в конечном счете 
может оказаться, что необходимо оптимизировать несколько взаимосвязанных факторов. 

    Оптимизация работы приложений 

    Приведенные ниже рекомендации касаются такого аспекта оптимизации связки 
клиент/сервер, как оптимизация работы приложений. Поскольку приложение 
является основным получателем информации, хранящейся на сервере, используемые 
в нем способы доступа к данным и их представления существенно влияют на общее 
быстродействие системы. 

    Минимизация количества подключений к серверу 

    Имеет смысл избегать открытия и поддержания ненужных подключений к 
серверу, ресурсы которого ограничены. Лишние подключения занимают ресурсы 
сервера, замедляют работу приложений и приводят к переполнению сети. Ниже 
описываются некоторые способы уменьшения количества подключений. 

    478 Часть IV. Темы повышенной сложности 

    Использование TDatabase 

    Одним из способов уменьшения количества подключений к серверу является 
использование одного компонента TDatabase для всего приложения. Вот что 
необходимо для этого предпринять. 
    1.    Поместите компонент TDatabase на главную форму приложения. 
    2.    Присвойте в качестве значения свойства AliasName компонента 
TDatabase ссылку на псевдоним BDE, используемый приложением. 
    3.   Установите свойство DatabaseName равным имени, которое будет 
использоваться в приложении как локальный псевдоним. 
    4.   Для всех компонентов семейства DataSet (например, TTable, TQuery или 
TStoredProc), установите свойство DatabaseName равным имени DatabaseName 
компонента TDatabase, а не псевдониму BDE, как это обычно делается. 
    После открытия набора данных, настроенного описанным образом, он не будет 
подключаться к серверу напрямую, а будет использовать подключение к серверу 
через компонент TDatabase. 
    Если для связи с сервером вместо отдельного псевдонима BDE используется 
компонент TDatabase, то при рткрытии в окне конструктора форм Delphi 
компонента семейства TDataSet, ссылающегося на псевдоним приложения, имейте в 
виду следующее. 
    Q  Форма, содержащая компонент TDatabase, тоже должна быть открыта в окне 
конструктора форм. 
    Q  При открытии компонента семейства DataSet (путем установки свойства 
Active в окне Object Inspector равным True), ссылающегося на TDatabase, 
происходит автоматическое подключение к серверу. Поскольку свойство 
KeepConnection компонента TDatabase по умолчанию равно True, последующее 
закрытие компонента семейства DataSet не приведет к отключению от сервера. 
Вместо этого при последующем сохранении проекта и выходе из Delphi параметры 
подключения будут сохранены вместе с проектом. При следующем открытии проекта 
будет немедленно потребован пароль, поскольку компонент TDatabase попытается 
восстановить предыдущее подключение к серверу. 
    Этого можно избежать, установив свойство KeeoConnection компонента 
TDatabase равным False; однако этот способ имеет побочный эффект — 
необходимость регистрироваться на сервере всякий раз после закрытия последнего 
активного компонента семейства DataSet при попытке открыть другой компонент. 

    SQLPASSTHRU MODE 

    Еще один способ ограничения количества соединений приложения с сервером 
заключается в использовании свойства SQLPASSTHRU MODE псевдонима базы данных. 
Это свойство можно установить для всего семейства драйверов или для отдельных 
псевдонимов. При установке его для семейства драйверов оно оказывает влияние 
только на новые псевдонимы, создаваемые впоследствии, но не влияет на 
существующие. Параметр SQLPASSTHRU MODE может принимать три значения: NOT 
SHARED, SHARED AUTOCOMMIT и SHARED NOAUTOCOMMIT. Установив его равным одному 
из значений с ключевым словом SHARED, можно минимизировать количество 
подключений, поскольку при этом значении параметра SQLPASSTHRU MODE система 
BDE может совместно использовать подключение, открытое приложением, для 
собственных нужд. 

    Динамические формы 

    Кроме вышеописанных способов, для уменьшения общего количества подключений 
приложения к серверу, можно отложить создание форм до тех пор, пока они 
действительно не понадобятся. По умолчанию в'се формы приложения создаются при 
его запуске, даже если все они никогда не используются одновременно. Намного 
лучше создавать и уничтожать формы явным образом, чем впустую расходовать 
ресурсы сервера. 
    Если какие-либо формы не должны создаваться автоматически, то их явное 
создание и уничтожение должно выполняться в обработчиках событий приложения. 
Эти операции достаточно просты, особенно для модальных форм. Для динамического 
создания и уничтожения форм выполните следующее. 
    1.    Откройте в среде Delphi диалоговое окно Project Options. 
    2.   Удалите все формы, кроме самых важных, из списка Auto-create forms и 
добавьте их в список Available forms. 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер    479 

    3.   Для каждой формы, которая должна создаваться и уничтожаться 
динамически, создайте обработчик события OnClose с оператором Action   := caFree;. 
    4.   Для     создания     формы     в     нужный     момент     
используйте     синтаксическую     конструкцию 
    Application.CreateForm   (TForml,   Forml), заменив TForml на имя класса 
формы, a Forml — на переменную объекта формы. 
    5.    В нужном месте исходного текста приложения поместите вызов метода 
showModal нужной формы. 
    6.    Когда необходимость в форме отпадает, уничтожьте ее объект, закрыв ее. 

    Оптимизация с использованием SQL 

    Приведенные ниже рекомендации относятся к оптимизации взаимодействия между 
приложениями и серверами на языке SQL. Для принятия реальных мер по 
оптимизации очень важно до тонкостей знать этот "язык межнационального 
общения" для СУБД с архитектурой клиент/сервер. 

    Использование хранимых процедур 

    Скомпилированный код обычно работает быстрее и эффективнее, чем 
интерпретируемый, независимо от языка программирования. Это справедливо и для 
кода SQL. Вместо использования компонентов TQuery и динамического SQL-кода 
Delphi используйте хранимые процедуры там, где это возможно, передавая в них 
параметры. Это быстрее, чем передача динамических операторов SQL, потому что 
сервер заранее компилирует и оптимизирует хранимые процедуры. Над 
интерпретируемым кодом все это необходимо проделывать при каждом выполнении, 
поэтому чем большая часть работы сделана заранее, тем лучше. 
    Существует одна область, в которой широкое использование хранимых процедур 
неразумно. Это обычная модификация данных — т.е., операции, которые 
выполняются операторами INSERT, UPDATE и DELETE. В последние годы появилась 
тенденция модифицировать данные с помощью хранимых процедур, а не с помощью 
элементов управления для работы с данными. Тому есть целый ряд причин — от 
повышения безопасности и скорости до более гибкой управляемости выполнением 
операторов модификации данных. 
    Приведенные причины достаточно вески, но есть и один голос против: все 
упомянутые операции сводят на нет достоинства таких средств разработки 
приложений архитектуры клиент/сервер, как Delphi. В приложениях Delphi 
единственным автоматическим способом выполнения хранимых процедур является 
использование компонента TUpdateSQL. Хотя компонент TUpdateSQL вполне 
справляется с этой задачей, тем не менее для модифицирования таблиц вам 
придется написать еще и внушительный фрагмент кода SQL. Для вызова хранимых 
процедур вставки, модификации и удаления строк таблицы, несомненно, 
понадобится список полей. При изменении структуры таблицы придется изменять 
соответственно и код SQL. Все это слишком сложно и чревато ошибками, поэтому 
лучше избегать подобных ситуаций. 
    Сказанное не означает, что компонент TUpdateSQL вообще не должен 
использоваться. Однако перед его применением необходимо вначале попытаться 
использовать встроенный механизм Delphi для модификации таблиц, прибегая к 
компоненту TUpdateSQL только в случае крайней необходимости.	
    При выполнении модификации на основе хранимых процедур без использования 
компонента TUpdateSQL вообще теряется вся легкость средств управления данными 
Delphi. При этом приходится вручную создавать элементы управления, имитирующие 
компоненты Delphi для работы данными, ограничивая диапазон принимаемых типов 
данных, выбирая данные при первом отображении формы и пересылая измененные 
значения данных на сервер с помощью хранимых процедур после закрытия формы. 
Это очень нудный и чреватый ошибками процесс, в котором к тому же не 
учитывается одно из основных преимуществ средств, подобных Delphi, — 
возможность получения и обработки мета-данных с сервера. С таким же успехом 
можно вообще отказаться от встроенных возможностей связи с сервером и 
прибегать к прямым вызовам функций API СУБД. 
    Еще одним недостатком данного подхода является то, что с его помощью 
обходятся механизмы обеспечения безопасности сервера. Большинство средств 
администрирования не уведомляет пользователей о том, есть ли у такой-то 
хранимой процедуры привилегия DELETE в такой-то таблице. Тем самым частично 
теряется возможность централизованного управления конфигурацией системы — 
правами, назначенными пользователям и группам в отношении отдельных объектов 
баз данных. Для обеспечения безопасности необходимо просматривать не только 
права, назначенные командами GRANT или REVOKE, но и содержимое процедур, 
хранимых в базах данных. 

    480	Часть IV. Темы повышенной сложности 

    На заметку 
    В отличие от Sybase, Oracle и Microsoft SQL Server, СУБД InterBase 
допускает назначение триггерам таких же прав на объекты баз данных, как 
пользователям. Информация об этих правах является частью метаданных сервера и 
может запрашиваться средствами администрирования для контроля системы 
безопасности сервера. Мне, однако, этот способ обеспечения безопасности базы 
данных кажется неудачным. 
    Некогда программные разработки для архитектуры клиент/сервер были 
настолько примитивны, что выполнение простейшей модификации данных с помощью 
хранимых процедур было неизбежным злом. Однако с появлением средств, подобных 
Delphi, эти трудные времена канули в Лету, и я рекомендую вам в полной мере 
воспользоваться преимуществами встроенных механизмов Delphi для модификации 
данных. Встроенные процедуры следует использовать, как правило, для сложных 
запросов и для других задач посложнее, чем простейшие манипуляции данными. 

    Использование метода Prepare 

    Метод Prepare компонентов TQuery вызывается перед их открытием. Этот метод 
передает запрос SQL системе драйверов базы данных для синтаксического анализа 
и оптимизации. Если метод Prepare явно вызывается из динамического запроса, 
который будет выполняться несколько раз, то при каждом последующем выполнении 
передаются только параметры запроса, а не весь его текст. Если метод Prepare 
заранее" явно "не вызывается, то запрос автоматически подготавливается при 
каждом его открытии. Подготавливая запрос заранее, можно избавить от этой 
обязанности систему доступа к базе данных и позволить открывать и закрывать 
запрос несколько раз без повторной подготовки. Это один из способов повышения 
быстродействия операций над запросами. 

    Свойство UpdateMode 

    Свойством UpdateMode обладает как компонент ТТаЫе, так и TQuery. Это 
свойство определяет элементы списка ключевого слова SQL WHERE, используемого 
при модификации данных. По умолчанию его значение равно UpWhereAll, при этом 
BDE генерирует в качестве списка ключевого слова WHERE перечень всех полей 
таблицы. Это может быть довольно обременительно, особенно для больших таблиц. 
Альтернативный, более быстрый, подход заключается в задании значения 
UpWhereChanged, при котором генерируется список, содержащий только ключевые и 
измененные поля таблицы. Это лучше всего продемонстрировать на примере. 
    Предположим, приложение Delphi только что модифицировало поле LastName 
таблицы CUSTOMER. Вот вариант оператора SQL, который будет сгенерирован в 
режиме UpWhereAll (обратите внимание на длину элемента WHERE). 

    UPDATE CUSTOMER 
    SET LastName='newlastname' 
    WHERE CustomerNumber=l 
    AND LastName='Doe' 
    AND FirstName='John' 
    AND StreetAddress='123 SunnyLane' 
    AND City='Anywhere' 
    AND State='OK' 
    AND Zip='73115' 

    Для сравнения ниже приведен оператор, сгенерированный в режиме UpWhereChanged. 

    UPDATE CUSTOMER SET LastName='newlastname' WHERE CustomerNumber=l AND LastName='Doe' 

    Обратите внимание на то, насколько второй оператор короче, а также на то, 
что в нем устранена возможность внесения другим пользователем изменений в поле 
LastName, поскольку в список, определяемый с помощью ключевого слова WHERE, 
помещено исходное значение этого поля. Если другой пользователь, вклинившись, 
попытается осуществить модификацию между операциями чтения этого поля и его 
модификации текущим пользователем, то оператор UPDATE, сгенерированный в 
режиме UpWhereChanged, не сработает — что и требовалось. Это несколько менее 
надежная защита, чем в случае режима UpWhereAll. Другой пользователь может 
удалить нужную строку после ее чтения вашим приложением и 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер 
    16  Delphi 3 и системы клиент/сервер 481 

    вставить в таблицу новую запись с тем же ключом и тем же значением поля 
LastName. При ее последующей модификации вашим оператором UPDATE операция 
будет выполняться уже не над той строкой, которая нужна. Однако такой вариант маловероятен. 
    Другой режим обновления, UpWhereKeyOnly, еще менее надежен, но и для него 
находится применение. В нем проверяется только ключевое значение изменяемой 
строки, и тем самым не учитывается возможность изменения полей другим 
пользователем уже после прочтения строки из таблицы. Это может не иметь 
пагубных последствий, но может и иметь. 
    Для большинства многопользовательских приложений небезопасно предполагать, 
что прочитанная запись ни разу не изменялась со времени ее прочтения кпиентным 
приложением. Поэтому режим UpWhereKeyOnly следует использовать в приложениях с 
осторожностью, полностью осознавая возможные опасные последствия. 
    Использование режима UpWhereKeyOnly — своего рода оптимизация, применяемая 
изредка и только в случае крайней необходимости. Благодаря более короткому 
списку, задаваемого с помощью ключевого слова WHERE, запрос, конечно, будет 
работать быстрее, но ввиду потенциальных разрушительных последствий перед 
использованием этого режима необходимо проконсультироваться с администратором 
базы данных. 

    Модифицируемые компоненты TQuery 

    Как правило, модифицируемых компонентов TQuery следует избегать, используя 
вместо них представления таблиц. Для этого есть несколько причин. Во-первых, 
возможность модификации компонента TQuery взваливает дополнительные 
обязанности по анализу запроса SQL и модификации его базовых таблиц на 
приложение (и на систему Borland Database Engine), а не на сервер. В 
нормальных системах типа клиент/сервер эту работу должен выполнять сервер — 
его ресурсы специально предназначены для сложных операций над базами данных. К 
тому же сервер лучше понимает собственный диалект SQL, чем любое клиентное 
приложение, так что в этом случае модифицируемые запросы являются более 
гибкими, а обновления выполняются быстрее. 

    Кэширование обновлений 

    Для минимизации объемов операторов SQL, пересылаемых на сервер, и 
количества блокировок баз данных можно применять предусмотренный в Delphi 
механизм кэширования обновлений. Кэшированные обновления (cashed update) 
хранятся до тех пор, пока их явно не применят к базе данных. При этом они 
отсылаются на сервер все вместе. Это уменьшает количество блокировок и общее 
время блокировки сервера, тем самым существенно ускоряя выполнение приложения. 
Для кэширования обновлений в приложении Delphi выполните следующее. 
    1.    В окне Object Inspector установите свойство CachedUpdates равным 
True для объекта семейства_ DataSet, обновления данных которого необходимо кэшировать. 
    2.    Установите свойство UpdateRecordTypes этого же объекта семейства 
DataSet для управления видимыми строками кэшированного набора данных. Это 
свойство может принимать следующие значения: 

    rtModified, rtlnserted, rtDeleted и rtUnmodifled. 

    3.   Установите обработчик события OnUpdateError на обработку любых ошибок 
при вызове метода  ApplyUpdates. 
    4.    Модифицируйте данные DataSet из приложения. 
    5.    Вызовите метод ApplyUpdates для сохранения изменений или метод 
CancelUpdates для их сброса. 

    Использование средства SQL Monitor 

    Средство Delphi под названием SQL Monitor — удобный инструмент просмотра 
операторов SQL, генерируемых приложением. Его можно использовать, например, 
для изучения тех операций, которые работают необычно медленно. В зависимости 
от ситуации может оказаться, что генерируемые операторы SQL необходимо 
преобразовать в представление или хранимую процедуру. Может также оказаться, 
что применяемые способы поиска или модификации данных являются неэффективными, 
и приложение можно оп

    482	Часть IV. Темы повышенной сложности 

    химизировать по этим критериям. Как бы там ни было, информация о 
"закулисных" операциях, поступающая от средства SQL Monitor, всегда может 
пригодиться. Команда SQL Monitor находится в подменю Database главного меню Delphi. 

    Кэширование структуры 

    Система BDE теперь поддерживает кэширование структуры (schema cashing) — 
локальное сохранение информации о структуре объектов базы данных. Применение 
кэширования структуры может существенно уменьшить объем запросов, пересылаемых 
из приложения на сервер для восстановления информации о структуре базы данных. 
В результате быстродействие приложения может существенно возрасти. 
    Режим кэширования структуры включается в утилите BDE Administration. В ней 
имеются четыре параметра, связанные с этим видом кэширования. Они перечислены 
в табл. 25.1. 

    Таблица 25.1. Параметры конфигурации BDE, связанные с кэшированием структуры 

    Параметр  Действие 
    ENABLE SCHEMA CACHE	Разрешение/запрещение кэширования структуры (параметр 
уровня драйвера) 
    SCHEMA CACHE SIZE	Количество таблиц с кэшируемой структурой 
    SCHEMA CACHE TIME	Срок хранения информации о структуре (в секундах) 
    SCHEMA CACHE DIR 

    Каталог для хранения информации о структуре (параметр уровня драйвера) 

    Параметр SCHEMA CACHE TIME по умолчанию равен -1, при этом информация о 
структуре хранится в кэш-памяти вплоть до закрытия базы данных. Диапазон 
допустимых значений этого параметра — от 1 до 2147483647 с. 
    Применение кэширования структуры особенно существенно сказывается на 
быстродействии приложения в глобальных сетях. Следует заметить, что для 
кэширования естественным образом предполагается неизменность (статичность) 
структуры базы данных, из-за чего не для всех баз данных такое кэширование 
приемлемо. В частности, кэширование структуры не должно применяться в 
следующих случаях. 
    О   При частом добавлении или удалении столбцов таблицы или ее индексов. О 
  При частом изменении значений столбцов NULL /NOT NULL. 
    При попытке применить кэширование структуры к базам данных, для которых 
оно не подходит, могут возникнуть следующие ошибки. 
    О	Unknown Column (неизвестный столбец). 
    Q	Invalid Bind Type (недопустимый тип привязки). 
    Q	Invalid Type (недопустимый тип). 
    О	Invalid Type  Conversion (недопустимое преобразование типов). 
    Q	Column Not  a Blob (столбец имеет тип, отличный от BLOB). 

    Фильтры 

    Средства фильтрации Delphi используются для отбора данных приложением из 
возвращаемого запросом набора данных. Для небольших объектов семейства DataSet 
использование обработчика событий OnFilterRecord может оказаться более 
быстрым, чем повторный запрос к серверу, потому что при этом снижается 
интенсивность взаимодействия сервера с сетью. Небольшие наборы данных часто 
могут кэшироваться на клиентном компьютере целиком, поэтому имеет смысл 
локально фильтровать их всегда, когда это возможно. Для локальной фильтрации 
наборов данных в приложениях Delphi выполните следующее. 
    1.    Настройте обработчик события OnFilterRecord объекта семейства 
DataSet на включение либо исключение строк набора с использованием параметра Accept. 
    2.    Установите свойство Filtered объекта DataSet равным True. 
    3.    При обращении к объекту DataSet из приложения будет создаваться 
впечатление, что набор данных содержит только строки, удовлетворяющие 
критериям фильтрации. 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер 483 

    На заметку 
    В описанных выше операциях для отбора строк набора данных используется 
событие OnFilterRecord, a не свойство Filter, поскольку при использовании 
свойства Filter создаются нелокальные фильтры. Возвращаемые строки, 
несомненно, будут отбираться по критериям фильтрации. Однако настройка 
фильтров с помощью компонентов Table, подключенных к серверу, всего лишь 
изменяет параметры создаваемо
    выполняется на сервере, а не локально. 

    Использование TField 

    Вместо использования функции FieldByName или свойства Fields объекта 
DataSet рекомендуется определить и использовать компоненты TField. Постоянные 
компоненты TField более эффективны, потому что информация о полях сохраняется 
прямо в приложении, и ее не нужно запрашивать у BDE. Они также более 
безопасны, поскольку генерируют исключительную ситуацию при изменении типа 
данных в таблице. В противоположность этому, функция FieldByName и свойство 
Fields должны для определения типа поля прочитать информацию о структуре 
таблицы. Они менее надежны и работают медленнее. Используйте объекты TField — 
они легче в применении, безопаснее и более устойчивы к изменениям в объектах 
базы данных. 

    Использование словаря данных 

    Всегда, когда это возможно, в первую очередь ищите бизнес-правила вашего 
приложения на сервере. Однако время от времени, несомненно, будет возникать 
необходимость настройки бизнес-правил в клиентном приложении. При создании 
бизнес-правил активно используйте словарь данных Delphi и его набор атрибутов. 
После определения всех возможных бизнес-правил со стороны клиента определите 
остальные с помощью компонентов DataSet и атрибутов TField. При определении 
бизнес-правил с использованием словаря данных, а не в конкретном приложении, 
они становятся легкодоступными для других приложений. 

    DBText и предназначенные только для чтения поля 

    Если поле предназначено только для чтения, то вместо компонента DBEdit 
используйте для него компонент DBText. Поля DBText экономнее и при этом 
обеспечивают те же функции. Можно также использовать статические компоненты 
Label для данных полей, которые не изменяются после появления формы на экране. 
Если необходимо предусмотреть возможность изменения данных, то используйте 
поля DBText. В противном случае задайте свойство Caption компонента TLabel в 
обработчике события OnShow и тем самым сэкономьте ресурсы, необходимые для 
средств управления данными, даже таких нетребовательных, как DBText. 
Использование экономных средств управления данными не только способствует 
уменьшению объема ресурсов, необходимых приложению, но и повышает его 
быстродействие за счет сокращения обмена информацией с базой данных. 

    Многопоточные приложения баз данных 

    Одной из наиболее мощных возможностей 32-разрядных систем Windows является 
распараллеливание выполнения приложений на несколько потоков. Система Delphi 
поддерживает эту возможность путем предоставления объектов класса TThread. 
Многопоточное выполнение (multi-threading) применимо и в приложениях баз 
данных. Очень часто преимущества многопоточности можно применить для фонового 
выполнения запроса. Если какой-либо запрос выполняется достаточно долго, то 
можно выполнять его в фоновом режиме и продолжать выполнение приложения. 
Разделение на потоки может также применяться для ускорения доступа к базе 
данных. Если, например, выполняется чтение записей из целого набора файлов и 
вставка их в таблицы сервера SQL, то для каждого файла можно создать отдельный 
поток, чтобы операции над одним файлом не задерживали обработку другого. 
Вообще в приложениях баз данных можно найти целый ряд полезных применений 
многопоточности. В листингах 25.1-25.4 приведены небольшие программы, 
использующие разделение н а потоки для одновременного доступа к трем различным таблицам. 

    484	Часть IV. Темы повышенной сложности 

    На заметку 
    Приведенные приложения используют стандартный псевдоним базы данных Delphi 
IBLOCAL. Для его использования необходимо, чтобы сервер InterBase находился в 
активном состоянии и база данных IBLOCAL была ему доступна. 

    Листинг 25.1. Исходный код проекта для программы демонстрации разделения 
потоков thrdex 

    program thrdex; 
    uses Forms, 
    thrdexOO in 'thrdexOO.pas ' {Forml}, thrdexOl in 'thrdexOl.pas ' ; 
    {?R *.RES} 
    begin 
    Application. Initializer-Application. CreateForm(TForml, Forml); Application.Run; 
    end. 

    Листинг 25.2. Исходный код модуля thrdexOO. pas, первого из двух модулей 
демонстрационной программы thrdex 

    unit thrdexOO; interface 
    uses 
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
StdCtrls, DB, Grids, DBGrids, DBTables, ThrdexOl, ExtCtrls; 
    type 
    TForml = class(TForm) 
    Queryl: TQuery; 
    DataSourcel: TDataSource; 
    DBGridl: TDBGrid; 
    Buttonl: TButton; 
    DBGrid2: TDBGrid; 
    Query2: TQuery; 
    DataSource2: TDataSource; 
    Databasel: TDatabase; 
    Sessionl: TSession; 
    Session2: TSession; 
    Database2: TDatabase; 
    Query3: TQuery; 
    DataSourceS: TDataSource; 
    DBGridS: TDBGrid; 
    Button2: TButton; 
    Databases: TDatabase; 
    procedure ButtonlClick(Sender: TObject); 
    procedure FormClose(Sender: TObject; var Action: TCloseAction); 
    procedure Button2Click(Sender: TObject); 
    procedure DatabaselLogin(Database: TDatabase; LoginParams: TStrings); private 
    { Private declarations } public 
    { Public declarations } end; 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер 485 

    Forml: TForml; 
    implementation var 
    QueryThreadl, QueryThread2 : TQueryThread; 
    {$R *.DFM} 
    procedure TForml.ButtonlClick(Sender: TObject); begin 
    Databasel.Open; 
    Database2.Open; 
    QueryThreadl:=TQueryThread.Create(Queryl) ; 
    QueryThread2:=TQueryThread.Create(Query2); 
    Buttonl.Enabled:=False ; end; 
    procedure TForml.FormClose(Sender: TObject; var Action: TCloseAction); begin 
    QueryThreadl.Terminate; 
    QueryThread2.Terminate; end; 
    procedure TForml.Button2Click(Sender: TObject); begin 
    with queryS do begin if active then close/open; end; end; 
    procedure TForml.DatabaselLogin(Database: TDatabase; 
    LoginParams: TStrings); begin 
    LoginParams.Values['USER NAME'] := 'SYSDBA'; 
    LoginParams.Values['PASSWORD'] := 'masterkey'; end; 
    end. 

    Листинг 25.3. Файл формы DFM для модуля thrdexOO. pas, первого из двух 
модулей демонстрационной программы thrdex                                    
                                                                               
                   § 
    object Forml: TForml Left = 26 Top = 9 Width = 595 Height = 434 Caption = 
'Forml' Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = 
-11 Font.Name = 'MS Sans Serif Font.Style = [] OnClose = FormClose 
PixelsPerlnch = 96 TextHeight = 13 object DBGridl: TDBGrid 
    Left = 72  • 
    Top = 16 
    Width = 497 
    Height = 113 
    DataSource = DataSourcel 
    TabOrder = 0 

    486	Часть IV. Темы повышенной сложности 

    т 
    TitleFont.Charset = DEFAULT_CHARSET 
    TitleFont.Color = clWindowText 
    TitleFont.Height = -11 
    TitleFont.Name = 'MS Sans Serif 
    TitleFont.Style = [] end object Buttonl: TButton 
    Left = 72 
    Top = 374 
    Width = 121 
    Height = 25 
    Caption = 'Start Query Threads' 
    TabOrder = у™——™— -—————— 
    OnClick = ButtonlClick end object DBGrid2: TDBGrid 
    Left = 72 
    Top = 132 
    Width =497 
    Height = 113 
    DataSource = DataSource2 
    TabOrder = 2 
    TitleFont.Charset = DEFAULT_CHARSET 
    TitleFont.Color = clWindowText 
    TitleFont.Height = -11 
    TitleFont.Name = 'MS Sans Serif 
    TitleFont.Style = [] end object DBGridS: TDBGrid 
    Left = 72 
    Top = 248 
    Width = 497 
    Height = 113 
    DataSource = DataSource3 
    TabOrder = 3 
    TitleFont.Charset = DEFAULT_CHARSET 
    TitleFont.Color = clWindowText 
    TitleFont.Height = -11 
    TitleFont.Name = 'MS Sans Serif 
    TitleFont.Style = [] end object Button2: TButton 
    Left = 240 
    Top = 374 
    Width = 145 
    Height = 25 
    Caption = 'Open Query in Main Thread' 
    TabOrder = 4 
    OnClick = Button2Click end object Queryl: TQuery 
    DatabaseName = 'dbthreadl' 
    SessiqnName = 'Sesl' 
    SQLTstrings = ( 
    'select * from EMPLOYEE') 
    Left = 16 
    Top = 8 end object DataSourcel: TDataSource 
    DataSet = Queryl 
    Left = 16 
    Top =40 end object Query2: TQuery 
    DatabaseName = 'dbthread2' 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер 487 

    SessionName = 'Ses2' SQL.Strings = ( 
    'select * from SALES') 
    Left = 16 
    Top =80 end object DataSource2: TDataSource 
    DataSet = Query2 
    Left = 16 
    Top = 120 end object Databasel: TDatabase 
    AliasName = 'IBLOCAL' 
    DatabaseName = 'dbthreadl' 
    LoginPrompt = False 
    Params.Strings = ( 'USER NAME=SYSDBA' 1PASSWORD=masterkey') 
    SessionName = 'Sesl' 
    OnLogin = DatabaselLogin 
    Left = 16 
    Top = 152 end object Sessionl: TSession 
    Active = True 
    SessionName = 'Sesl' 
    Left = 16 
    Top = 224 end object Session2: TSession 
    Active = True 
    SessionName = 'Ses2' 
    Left = 16 
    Top = 256 end object Database2: TDatabase 
    AliasName = 'IBLOCAL'-.. 
    DatabaseName = 'dbthread2' 
    LoginPrompt = False 
    Params.Strings = ( 'USER NAME=SYSDBA' 'PASSWORD=masterkey') 
    SessionName = 'Ses2' 
    OnLogin = DatabaselLogin 
    Left = 16 
    Top = 192 end object Query3: TQuery 
    DatabaseName = 'dbdefaultthread' 
    SQL.Strings = ( 
    'SELECT * FROM CUSTOMER') 
    Left = 16 
    Top = 288 end object DataSourceS: TDataSource 
    DataSet = QueryS 
    Left = 16 
    Top = 320 end object Databases: TDatabase 
    AliasName = 'IBLOCAL' 
    DatabaseName = 'dbdefaultthread' 
    LoginPrompt = False 
    Params.Strings = ( 'USER NAME=SYSDBA' 

    488 Часть IV. Темы повышенной сложности 

    'PASSWORD=masterkey') SessionName = 'Default' OnLogin = DatabaselLogin 
Left = 16 Top = 352 end end 

    Листинг 25.4. Исходный код модуля thrdexOl. pas, второго из двух модулей 
демонстрационной программы thrdex 

    unit thrdexOl; interface 
    uses 
    Classes, Forms, DBTables; 
    type 
    TQueryThread = class(TThread) private 
    { Private declarations } 
    FQuery : TQuery; protected 
    procedure Execute; override; 
    procedure OpenQuery; 
    procedure DoOpenQuery; public 
    constructor Create(Query: TQuery); 
    procedure OnTerm(Sender: TObject); end; 
    implementation 

    { Примечание: методы и свойства объектов VCL можно изменять только с 
помощью вызова метода Synchronize, например, 

    Synchronize(UpdateCaption) ; где UpdateCaption — метод следующего вида 
    procedure TQueryThread.UpdateCaption; begin 
    Forml.Caption := 'Updated in a thread'; end; } 
    { TQueryThread } 
    constructor TQueryThread.Create(Query: TQuery); begin 
    inherited Create(False); 
    FQuery := Query; end; 
    procedure TQueryThread.OpenQuery; begin 
    FQuery.Open; 
    With FQuery do begin 
    With Owner.Owner as TApplication do ProcessMessages; While not EOF do 
Next; Close; 
    With Owner.Owner as TApplication do ProcessMessages; end; end; 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер	489 

    procedure TQueryThread.DoOpenQuery; begin 
    Synchronize(OpenQuery); end; 
    procedure TQueryThread.Execute; 
    var 
    Counter : Integer; 
    begin 
    { Здесь размещаются операторы, которые должны выполняться в данном потоке. 
} For Counter:=0 to 100 do begin DoOpenQuery; 
    If Terminated then exit/end; 
    end; 
    procedure  TQueryThread.OnTerm(Sender:   TObject); begin 
    Application.MessageBox(PChar('Поток,   выполнявший  запрос   '+ 
    FQuery.Name+'   завершен.',   PChar(FQuery.Name),IDOK) ; end; end. 

    В приведенном примере приложения определяются два компонента TSession и 
два фоновых потока, каждый из которых последовательно открывает запрос 100 
раз. Третий запрос может открываться в основном потоке приложения. При 
внимательном изучении исходного текста этого приложения (он находится на 
прилагаемом компакт-диске) можно заметить, что фоновые и основные потоки не 
взаимодействуют друг с другом — каждый из них выполняется независимо. Для 
повторного выполнения основного потока можно несколько раз щелкнуть на кнопке 
Open, что никак не повлияет на фоновые процессы. В приложениях Delphi 
архитектуры клиент/сервер распараллеливание запросов можно использовать для 
фонового выполнения громоздких задач. Чтобы понять, как это делается, изучите 
приведенный пример. 

    Оптимизация работы сервера 

    Оптимизация работы сервера состоит из оптимизации операторов SQL, 
параметров сети, конфигурации баз данных и т.д. Разумеется, выполнение такой 
оптимизации скажется на работе всех приложений, использующих сервер, а не 
только приложений, созданных с помощью Delphi. 

    Оптимизация конфигурации сервера 

    Производительность сервера можно повысить многими способами. Параметры, 
установленные по умолчанию, как правило, можно считать оптимальными лишь для 
простейших приложений. Ниже рассмотрены некоторые потенциальные источники 
повышения производительности. 

    Память 

    Нередко можно обнаружить, что, несмотря на наличие у компьютера, 
используемого для работы сервера СУБД, большого объема памяти, программное 
обеспечение сервера баз данных не настроено на ее полноценное использование. В 
большинстве систем память не распределяется автоматически — ее необходимо 
настраивать. В общих чертах принцип таков: необходимо выделять серверу баз 
данных как можно больше памяти, избегая буферизации страниц на диске. 

    Кэширование 

    Рекомендуется запретить кэширование на уровне операционной системы и 
передать эти функции серверу баз данных. Сервер лучше осведомлен о данных и их 
логической организации, поэтому он может принять более обоснованное решение, 
что и как кэшировать. 
    Особую важность представляет вопрос о кэшировании процедур. Как Sybase, 
так и Microsoft SQL Server подразделяют кэш-память на две части: кэш-память 
данных и кэш-память процедур. Объем кэш-памяти процедур задается в процентах 
от общего объема кэш-памяти. При увеличении общего объема памяти 

    490	Часть IV. Темы повышенной сложности 

    системы кэш-память процедур пропорционально увеличивается. Это может быть 
нежелательным, например, если добавление оперативной памяти было предназначено 
специально для увеличения кэш-памяти данных. В этом случае часть оперативной 
памяти будет расходоваться впустую. Для компенсации необходимо снизить 
процентную долю кэш-памяти процедур. 

    Процессоры 

    В большинстве современных СУБД предусмотрено распределение нагрузки между 
процессорами многопроцессорных систем. Например, в Microsoft SQL Server 
настраивается специальная битовая маска (affinity mask), которая определяет, 
какие процессоры могут использоваться сервером. В системе Sybase можно выбрать 
значение параметра max online engines, определяющего допустимое количество 
процессоров. Система Oracle является наиболее гибкой из всех — в ней нагрузка 
сервера может распределяться даже между несколькими компьютерами. Следует 
заметить, что при добавлении новых процессоров повышение производительности не 
является линейным — удвоение количества процессоров не приводит .к, удвоению 
быстродействия. Почему это так? Потому что в большинстве операций СУБД 
^пользуются не только процессоры, но и средства ввода-вывода. Тем не менее и 
средства ввода-вывода управляются процессором, поэтому более быстрое 
прохождение инструкций через систему, несомненно, повышает быстродействие. 

    Асинхронный ввод-вывод 

    Все основные СУБД поддерживают асинхронные дисковые операции ввода-вывода. 
Очевидно, что асинхронное выполнение этих операций предпочтительнее, чем 
синхронное. Если ваша система поддерживает асинхронный ввод-вывод, то 
поэкспериментируйте с ним с целью повышения быстродействия. Особенно заметным 
оно может быть при использовании дисковода со встроенным процессором или 
технологии RAID. Использование асинхронного ввода-вывода более естественно при 
наличии большого количества дисковых устройств. 

    Регистрация транзакций 

    Регистрацию транзакций и повторений операций следует производить на 
отдельном устройстве. Это не только повышает быстродействие, но и помогает при 
восстановлении поврежденных данных. Для текущих баз данных или баз данных, 
которые можно полностью восстановить какой-либо рабочей программой, некоторые 
операции по контролю транзакций можно смело отменить. Например, в системах 
Microsoft и Sybase SQL Server при включенном режиме trunc. log on chkpt записи 
о законченных транзакциях удаляются из журнала регистрации транзакций во время 
ожидания. Благодаря этому размер журнала регистрации поддерживается на 
минимальном уровне, количество операций регистрации снижается, и 
быстродействие системы в целом возрастает. 
    Будьте осторожны с режимом усечения транзакций: он делает невозможным 
восстановление транзакций. Не может быть выполнено частичное резервное 
копирование (incremental backup), а при повреждении данных будет невозможным 
полное восстановпение. Поэтому необходимо тщательно взвесить необходимость 
усечения журнала регистрации транзакций. Это не означает, что этот режим 
нельзя применять; необходимо лишь понимать, как и для чего он используется. 

    Оптимизация запросов 

    В этой книге постоянно встречается термин оптимизатор запросов (query 
optimizer). Читатель, должно быть, уже заинтригован, что это за зверь такой и 
как он работает. Все ведущие серверы баз данных выполняют оптимизацию 
запросов, передаваемых им клиентными приложениями. Они анализируют запросы SQL 
и пытаются выбрать наиболее эффективный способ их выполнения. Средство, 
выполняющее этот анализ и выбор, и называется оптимизатором запросов. В 
терминологии большинства серверов план, предлагаемый оптимизатором, называется 
планом выполнения запроса (query execution plan). При разработке этого плана 
принимаются во внимание такие факторы, как доступные индексы, объем дискового 
пространства, который должен быть прочитан, статистическая информация о 
распределении ключей индексов и т.п. Довольно часто план, вырабатываемый 
сервером, оказывается наилучшим способом выполнения запроса, но иногда и 
оптимизатору требуется помощь. 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер 491 

    Помощь оптимизатору 

    Одним из способов помощи оптимизатору запросов является обновление 
статистической информации о выборке индекса. Выборка индекса (index 
selectivity) означает распределение ключевых значений внутри индекса. Если эта 
информация постоянно обновляется, то оптимизатор запросов вырабатывает свой 
план на основе самых свежих и достоверных данных. 
    В СУБД InterBase для обновления выборки индекса используется следующая команда. 

    SET STATISTICS INDEX INVOICES03 

    Здесь INVOICES 03 — имя обновляемого индекса. 
    В Sybase и Microsoft используется следующий синтаксис. 

    UPDATE STATISTICS INVOICES.INVOICES03 

    Здесь INVOICES — имя таблицы, по которой построен индекс, a INVOICES03 — 
имя самого индекса. Поскольку индексы СУБД SQL Server уникальны только в 
таблице, по которой они строятся, в команде должно использоваться имя таблицы. 
С другой стороны, если опустить имя индекса, то для данной таблицы будут 
созданы сразу все индексы. 

    UPDATE STATISTICS INVOICES 

    Статистика индексов Oracle обновляется с использованием команды ANALYZE. 
Эта команда собирает статистическую информацию о таблицах, индексах и 
кластерах. Для обновления статистики одного индекса используется следующая команда. 

    ANALYZE   INDEX  CUSTOMER03   COMPUTE   STATISTICS; 

    Статистика по таблице и ее индексам собирается следующей командой. 

    ANALYZE   TABLE   CUSTOMER  COMPUTE   STATISTICS   CASCADE; 

    С помощью следующей команды можно собрать статистику по всему кластеру 
(включая таблицы и индексы). 

    ANALYZE  CLUSTER  acctrecv  COMPUTE   STATISTICS   CASCADE; 


    Отображение плана оптимизатора запросов 

    При выполнении запроса на большинстве SQL-серверов можно отображать план 
этого выполнения. Для просмотра планов, генерируемых сервером InterBase, 
включите режим Display Query Plan в диалоговом окне Basic ISQL Set Options, 
открываемом с помощью соответствующей команды меню Session утилиты WISQL (рис. 25.1). 
    Этот режим эквивалентен команде SET PLAN ON. 
    Статистическая информация о запросах после их выполнения может 
отображаться также по команде SET STATS ON. Эту команду не следует путать с 
упомянутой ранее командой SET STATISTICS, которая предназначена для обновления 
информации в индексе. 
    В СУБД Sybase и Microsoft SQL Server для отображения плана оптимизатора 
запросов используется команда SET SHOWPLAN ON. Применяя команду SET NOEXEC, 
можно просматривать план выполнения запроса, не выполняя его на самом деле. 
    Статистическую информацию, относящуюся к выполнению запроса, можно также 
просматривать с помощью команды SQL Server SET STATISTICS. По команде SET 
STATISTICS 10 ON отображается статистика ввода-вывода для каждого выполняемого 
запроса. По команде SET STATISTICS TIME ON отображается статистика времени 
выполнения для каждого выполняемого запроса. 
    Доступ к плану выполнения запросов СУБД Oracle осуществляется командой 
EXPLAIN PLAN PL/SQL. В команду EXPLAIN PLAN передается в качестве параметра 
оператор, который необходимо выполнить, после чего сервер Oracle отображает 
план его выполнения в специальной таблице. Если включен режим оптимизации по 
затратам, то вычисляется также и относительная стоимость запроса. 
    Просматривая план выполнения запроса, сгенерированный оптимизатором, можно 
лучше понять работу запросов и определить способы их оптимизации. На рис. 25.2 
показано, как план оптимизатора запросов помогает понять, что данный запрос на 
самом деле не использует индекс (строки таблицы перебираются в их естественном 
порядке): на это указывает наличие ключевого слова NATURAL. 

    Рис. 25.1. Диалоговое окно Basic ISQL Set Options 492 

    Часть IV. Темы повышенной сложности 

    Теперь перепишем запрос так, чтобы он использовал индекс. На рис. 25.3 
показан новый запрос и план выполнения, разработанный для него сервером InterBase. 
    Этот пример показывает, как важно понимать, что такое план выполнения 
запроса сервером. Изучив этот план, нередко можно принять решение о 
рациональной оптимизации запроса. 

    Рис. 25.2. План выполнения запроса без индекса 
    
    Рис. 25.3. План выполнения запроса с индексом 

    Принудительное использование плана 

    В СУБД InterBase предусмотрено расширение оператора SELECT, с помощью 
которого можно заставить оптимизатор использовать заданный план выполнения 
запроса, а не его собственный. Вот синтаксис этой команды: 

    SELECT  LastName,   FirstName FROM  CUSTOMER 
    PLAN    (CUSTOMER  ORDER  CUSTOMER03) ORDER BY  LastName 

    Обратите внимание на ключевое слово PLAN. Его первый аргумент сообщает 
оптимизатору, для какой таблицы запроса предназначен план, второй аргумент — 
какие действия необходимо выполнить (в данном случае упорядочить таблицу). 
Последний аргумент сообщает оптимизатору, какое средство должно использоваться 
для проведения плана в жизнь — в данном случае это индекс CUSTOMER03. 
    Принудительная реализация плана полезна в том случае, если нет уверенности 
в том, что оптимизатор может выработать оптимальный план. 
    В СУБД SQL Server имеется еще несколько средств для выполнения той же 
задачи. Первый способ предусматривает использование определенного индекса с 
помощью следующей команды. 

    SELECT LastName, FirstName FROM CUSTOMER (3) ORDER BY LastName 

    Обратите внимание на тройку в скобках после имени таблицы. Этот параметр 
указывает оптимизатору использовать третий индекс, построенный по таблице 
CUSTOMER. (Именно по этой причине я обозначаю индексы именем таблицы с 
порядковым номером. Иначе запомнить номера всех индексов было бы невозможно.) 
    Если указать значение 0, то будет выполняться перебор таблицы, тогда как 
значение 1 заставляет оптимизатор использовать кластерный индекс таблицы, если 
таковой имеется. 

    В SQL Server индекс можно указывать и по имени, например: 
    SELECT LastName, FirstName 
    FROM CUSTOMER (INDEX = CUSTOMER03) 
    ORDER BY LastName 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер 493 

    Второй способ заключается в использовании оптимизатором SQL Server команды 
SET FORCEPLAN для присоединения таблиц в определенном порядке, заданном с 
помощью ключевого слова FROM. Обычно оптимизатор присоединяет таблицы в 
оптимальном порядке, но это случается не всегда. Если изучение плана 
выполнения запросов показывает, что оптимизатор ошибается, то можно включить 
режим FORCEPLAN и управлять порядком присоединения таблиц с помощью ключевого 
слова FROM. 

    Оптимизация работы сети 

    В этом разделе приводится ряд советов по оптимизации производительности 
приложений архитектуры клиент/сервер при работе в сети. В частности, обычным 
источником нежелательных задержек во взаимодействии приложения и сервера 
являются глобальные сети. 

    На заметку 
    Используемый здесь термин "глобальная сеть" относится к сетям с 
передачей данных по цифровым линиям со скоростью не меньшей, чем 56 Кбит/с. 
Поскольку пропускная способность даже скоростных линий Т-1 достигает всего 
лишь 1/7 от пропускной способности сетей Ethernet (10Мбит/с), использование 
ресурсов в приложениях для глобальных сетей должно быть продумано особенно тщательно. 
    Ниже приведены некоторые рекомендации по оптимальной конфигурации 
подключений к сетевым базами данных. 
    Q  Очевидно, что быстродействие сетевых приложений, в том числе приложений 
архитектуры клиент/сервер, повышается в более скоростных сетях (например, 
имеющих пропускную способность 100 Мбит/с). 
    U   Существенно влияет на быстродействие установка более быстрых сетевых 
плат, сегментация сети и оптимизация других аппаратных средств. Все это 
достаточно очевидно, но очевидные вещи нередко упускаются в первую очередь. 
    О   Если сетевой протокол поддерживает пересылку пакетов больших размеров 
(это справедливо, например, для протокола Novell Large Internet Packet), то 
можно поэкспериментировать с увеличением размера пакетов для уменьшения их 
общего количества. Чем меньше пакетов передается по сравнительно медленным 
глобальным сетям, тем лучше. 
    Q   Рекомендуется создавать второстепенные формы динамически, а не 
автоматически. Каждое открытое подключение к серверу отвлекает на себя часть 
пропускной способности сети. В сравнительно скоростных локальных сетях это не 
так заметно, как в глобальных. Оцените сами, насколько рационально создавать и 
уничтожать формы там, где это действительно необходимо, экономя пропускную способность. 
    Q   При разработке приложений для работы в глобальных сетях необходимо 
критически взглянуть на элементы управления DBGrid и DBCtrlGrid. Сетки вполне 
уместны в приложениях для локальных сетей, но они нередко содержат намного 
больше данных, чем необходимо для работы приложения. Из-за них зря расходуется 
пропускная способность и замедляется работа приложения. 
    О   Необходимо свести к минимуму отношения вида "главный-подчиненный". В 
связи с их особенностями каждый шаг по главной таблице вызывает поток запросов 
к подчиненным таблицам. Использование форм такого вида может привести к 
излишнему обмену данными по сети. 
    Q Для чтения неизменяемых данных из вторичных таблиц можно использовать 
компоненты Label и статические операции Lookup/Locate, а не компоненты DBText. 
Хотя применение логических компонентов DBText проще и естественнее, каждый 
логический элемент данных отнимает часть пропускной способности. Стоит излишне 
загрузить сеть, и приложение станет неработоспособным. 
    О  Свойство KeepConnection компонента TDatabase рекомендуется установить 
равным False. Хотя это замедляет работу приложения из-за необходимости каждый 
раз заново подключаться к серверу, несомненным преимуществом является 
повышение пропускной способности для других удаленных пользователей. Для 
глобальных сетей это является решающим аргументом. Еще одно преимущество 
данного подхода заключается в сокращении количества подключений к серверу, 
которое может быть ограничено. В этом случае сброс неактивных подключений 
(KeepConnections:=False) позволяет большему количеству пользователей 
обратиться к системе. 

    494	Часть IV. Темы повышенной сложности 

    Q  Используйте хранимые процедуры и запросы для сосредоточения больших 
массивов информации на сервере, прежде чем пересылать их по сети приложению. 
Другими словами, не нужно выполнять в приложении построчных операций — вместо 
этого следует использовать процедуры, хранящиеся на сервере. 
    Q  Используйте кэширование обновлений для группировки изменений в базе 
данных, чтобы их можно было применять вместе, а не по одному за раз. 
    Q  При необходимости применяйте кэширование структуры BDE для хранения 
информации о структуре базы данных на локальном компьютере. Это делается путем 
установки параметра ENABLE SCHEMA CACHE в утилите BDE Configuration равным True. 
    Q  Используйте локальные фильтры Delphi для локального отбора данных из 
небольших наборов. Это экономит усилия по повторному обращению к ним по сети. 
    Q  Используйте компоненты Delphi BatchMove для перемещения больших объемов 
строк между таблицами. Не выполняйте построчно никаких операций. 
    Q   По умолчанию протокол TCP/IP ожидает заполнения пакета, прежде чем 
отправить его. Если объем пересылаемых данных превышает размер пакета, то 
из-за этого может упасть производительность. На большинстве серверов эту 
задержку можно устранить путем несложных изменений в конфигурации. Подробности 
можно узнать у системного администратора. Обычно все зависит от одного 
конфигурационного параметра либо главного компьютера в целом, либо 
программного обеспечения сервера СУБД. Например, в Sybase SQL Server версии 
System 11 предусмотрен специальный переключатель sp_conf igure, отвечающий за 
компоновку пакетов TCP. В версии System 10 это реализовано с помощью 
трассировочного флажка (1610) командной строки сервера: для включения режима 
TCP_NODELAY следует добавить в командную строку параметр -Т1610. 
    Q Для оптимизации приложений можно поэкспериментировать с параметрами 
протокола TCP/IP. Вот некоторые полезные для этой задачи параметры системы UNIX: 

    ndd  -set   /dev/tcp  tcp_rexmit_interval_max   
значение ndd  -set   /dev/tcp  tcp_conn_req_max  
значение ndd -set   /dev/tcp  tcp_close_wait_interval   
значение ndd  -set  /dev/tcp tcp_keep_alive_interval  
значение 

    Большинство современных серверов допускает гибкую настройку параметров 
протокола TCP/IP. Если клиентные приложения используют для связи с сервером 
базы данных протокол TCP/IP, то ускорение его операций может существенно 
улучшить производительность системы в целом. 

    Резюме 

    Оптимизацию приложений Delphi архитектуры клиент/сервер можно разделить на 
две части: оптимизация приложения и оптимизация сервера. В свою очередь, 
оптимизация сервера подразделяется на оптимальное программирование SQL и 
настройку системы. Перечисленные в этой главе способы оптимизации приложений 
Delphi далеко не исчерпывают весь возможный список, однако дают о нем ясное 
представление. Конечно, наилучшее понимание оптимизации для конкретной системы 
приходит после долгих лет работы со множеством проб и ошибок. 

    Что дальше... 

    В главе 26, "Borland Database Engine", дается введение в принципы 
функционирования механизма, благодаря которому работают приложения Delphi. В 
этой главе вы заглянете во внутреннее устройство Delphi и поймете, в чем 
секрет многих ее трюков. 

    Глава 25. Оптимизация приложений архитектуры клиент/сервер 495 

