Автор работы: Пользователь скрыл имя, 30 Января 2013 в 03:08, курсовая работа
Методологической и теоретической основой исследования послужили работы зарубежных и российских авторов в области бизнес-планироания, менеджмента и маркетинга, связанные с тематикой работы.
Информационную основу исследования составили маркетинговые исследования, проводимые российскими консалтинговыми компаниями, работающими на рынке недвижимости, а также данные, полученные в ходе исследования деятельности конкурентов.
В качестве инструментов исследования были использованы сравнительный и системный анализ, качественное и количественное изучение реальности, экономико-статистические методы.
Практическое значимость предложенных рекомендаций заключается в том, что их использование позволит создаваемому агентству недвижимости успешно выйти на рынок недвижимости Москвы.
Для того, чтобы избавиться от такого поведения pFIBDataSet нужно свойство TimeOutAction пишущей транзакции установить в taCommit (по умолчанию taRollback) - после чего автоматическое завершение этой транзакции будет происходить по Commit, а не по CommitRetaining.
В IBX такой проблемы нет, т.к. упомянутый компонент IBUpdateSQLW при AutoCommit выполняет вызов Commit для связанной с ним транзакции.
Обработка исключений и транзакции
В транзакциях обычно выполняется несколько операторов, и у разработчика может возникнуть вопрос - как правильно писать код, если выполнение одного из таких операторов может привести к ошибке?
Здесь может быть два случая
старт транзакции, выполнение операторов и завершение транзакции "разбросано" по коду. Например, когда старт транзакции производится при открытии формы, затем выполняются интерактивные действия, а затем опять же по нажатию кнопки выбирается применение или отмена действий. Этот случай мы не будем рассматривать, т.к. тут все достаточно просто.
старт, операторы и завершение транзакции выполняется в одном блоке кода, например в процедуре обработчика нажатия кнопки
Второй случай в коде может выглядеть так:
TW.StartTransaction;
try
Q1.ExecQuery;
Q2.ExecQuery;
...
TW.Commit;
except
TW.Rollback;
... // здесь может быть или raise, или сообщение пользователю об отмене
// операции в результате ошибки. Учитывайте, что это всего-лишь пример,
// и в реальном приложении обработка except может быть куда сложнее,
// с обработкой типа ошибки и т.п.
end;
Обрамлять StartTransaction в блок try особого смысла нет. Транзакция или стартует, или нет, соответственно, если при StartTransaction произошла ошибка, то вызов Commit в данном блоке не произойдет.
Иногда, например если вы вызываете операторы DDL, например alter table drop constraint, drop index и т.п., при выполнении Commit может возникнуть ошибка - ряд операторов действует именно по Commit, а не во время выполнения оператора. Таким образом, единственным вариантом завершения транзакции в этом случае получается явный вызов Rollback. Приведенный код, с Commit "рядом" с выполнением операторов, корректно обработает данную ситуацию.
Если кроме Q1 и Q2 есть другие действия, которые также могут вызвать ошибку, или просто хочется пользователю показать ошибку правильно и на русском языке, то тогда нужно использовать обработку конкретного exception
except
on E: Exception do
...
и обращаться или к общему E.Message, или проверять E на класс ошибки EIBError.
Если же в блоке try/except вызывается только один оператор SQL (и это не оператор DDL, о чем было сказано выше), то можно обойтись без Rollback:
TW.StartTransaction;
try
Q1.ExecQuery;
finally
TW.Commit;
end;
Дело в том, что если оператор не выполнился, то никаких изменений на сервере не произошло (кроме случая вызова процедуры с for select, внутри которого при suspend происходят изменения данных). Поэтому даже если вы вызовете Rollback, сервер автоматически превратит такой Rollback в Commit. Разумеется, если вы впоследствии допишете в блоке try/finally выполнение других операторов SQL, то такой код приведет к безусловному сохранению тех изменений, которые выполнились успешно. Сервер разрешает вызвать Commit независимо от того, были ошибки при выполнении операторов с момента StartTransaction, или нет. То есть, необходимость вызова Rollback определяется только вашим приложением и целостностью блока операторов (с логической точки зрения) внутри транзакции.
Связывание транзакций и компонент данных
Пока разработчик кладет компоненты IBX на форму, вроде бы все нормально - он сам связывает компоненты IBTransaction с IBDataSet, IBQuery, IBSQL, IBTable. Иногда разработчик забывает это сделать, в результате чего в приложении возникают длительные транзакции, "не сохраняются данные", данные "не видны" и т.п.
Такие же ошибки могут возникать, и возникают чаще при создании экземпляров компонент в коде. Например, в одном случае разработчик динамически создавал IBTransaction и IBQuery, выполнял запрос и ... где-то в коде потерял завершение транзакции. IBQuery занимался только чтением данных, поэтому ошибка проявлялась где-то через пару часов и выглядела невразумительно - заканчивались "какие-то хэндлы".
Просмотр состояния сервера (а это был IB 7.1 с временными системными таблицами), показал что количество незакрытых транзакций растет постоянно (tmp$transactions), и так было идентифицировано наличие ошибки. Затем, после часа копания в коде, ошибка была обнаружена и исправлена.
Также бывает, когда разработчик до использования IBX имел дело с BDE, где всего одна транзакция на коннект, или с другими серверами, где тоже не бывает более одной транзакции на соединение с БД.
Клиентская часть InterBase и Firebird может стартовать какое угодно число транзакций одновременно, и выполнять их "параллельно". Поэтому компоненты получения данных с сервера должны быть обязательно привязаны к конкретной транзакции. Например, если мы выполняем код
TR1:=TIBTransaction.Create...
Q:=TIBQuery.Create...
TR1.StartTransaction;
Q.SQL.Clear;
Q.SQL.Add('SELECT ...');
Q.ExecQuery;
TR1.Commit;
то
1. выполнение Q.ExecQuery может
привести к ошибке 'transaction is not active',
если используемые компоненты
не стартуют транзакции
2. Q.ExecQuery может выполниться в DefaultTransaction, которая создается автоматически для IBDatabase
Как результат, запрос Q может выполниться вовсе не в транзакции TR1.
Значит, мы забыли выполнить код
Q.Transaction:=TR1;
Добавить тут практически нечего, вывод один - создаваемые динамически компоненты должны быть "привязаны" к транзакции, и привязка должна производиться явно в коде "рядом", так чтобы вы всегда могли видеть что же за транзакция используется для компонента с запросом.
Есть еще вариант, когда
вместо явного TR1.StartTransaction вызывают Q.Transaction.
TR1:=TIBTransaction.Create...
Q:=TIBQuery.Create...
Q.Transaction:=TR1;
Q.Transaction.
Q.SQL.Clear;
Q.SQL.Add('SELECT ...');
Q.ExecQuery;
Q.Transaction.Commit;
Правда, на мой взгляд это менее "явное" использование транзакций, чем прямой вызов StartTransation/Commit у именованной переменной IBTransaction.
Число транзакций в сутки
Для мониторинга транзакций (и других параметров статистики) существует инструмент IBAnalyst. Он автоматически подсчитывает среднее число стартуемых вашими приложениями транзакций для базы данных в сутки, и показывает предупреждения если, например, число активных транзакций выше 30% от среднего за день (другими словами, такие симптомы означают, что ваши приложения содержат транзакции, активные по нескольку часов). Также в меню Сервис есть Калькулятор транзакций, где вы можете определить сколько транзакций в секунду стартует 1 приложение на 1-ом рабочем месте. Исходя из этой информации вам решать - насколько интенсивно работают с транзакциями ваши приложения.
Например, в среднем в день - 100 тысяч транзакций. Рабочий день 8 часов, и одновременно работают 30 пользователей. Калькулятор выдает - 3 транзакции в секунду происходит в БД, причем в среднем 1 рабочее место стартует очередную транзакцию каждые 10 секунд. Если сопоставить это с действиями пользователя в приложении, то может оказаться, что старт транзакций в такой системе происходит чаще, чем требуется.
(например, у вас вышло, что одно рабочее место стартует 3 транзакции в секунду. Много это или мало? А вы представьте себе человека, который работает с вашей программой. Он разве похож на робота, который оформляет по 3 документа в секунду? В одной системе, где разработчик сделал выполнение каждого действия оператора в одной транзакции (тоже эдакая крайность), получилось 1500 транзакций в час на 1 рабочее место. То есть, 0.5 транзакций в секунду. Как видите, даже в этом случае число транзакций в секунду меньше.).
В промышленных системах со средней нагрузкой (до 100-150 пользователей) нормальное число транзакций в сутки (при явном управлении транзакциями) может составлять от 90 до 250 тысяч (в отдельных случаях от 400 тысяч до миллиона). Само по себе число стартуемых и завершаемых транзакций не оказывает влияния на производительность (в одной системе я наблюдал из-за ошибки разработчика по 6 миллионов транзакций в сутки, а в другой - из-за особенности ПО, автоматически вставляющего записи в БД, и делающего это в режиме "по одной транзакции на запись" - 3.5 миллиона транзакций в сутки. В обоих случаях ухудшения производительности никто и не заметил.). Ухудшить производительность может только накапливаемый в базе данных мусор, а он то как раз и "растет", когда транзакции активны длительное время (версии записей удерживаются активными транзакциями от перехода из актуального состояния в состояние "мусор"). Понятно, что если приложение, написанное на BDE, внезапно начало сильно "тормозить" при увеличении числа пользователей, то сделать здесь уже ничего нельзя. Конечно, если используется InterBase 7.x, то можно "отловить" приложения, в которых транзакции активны длительное время, при помощи временных системных таблиц (tmp$transactions, tmp$attachments). Но это может иметь смысл только в редких случаях. А если сами приложения функционируют именно так, с долго работающими транзакциями, то исправить поведение приложений извне невозможно.
То есть, проблемы с транзакциями на сервере - это проблемы приложений, и только.
Что еще читать по транзакциям
Обязательными к прочтению (если вы собираетесь писать качественные приложения, работающие с InterBase или Firebird) являются:
Многоверсионность в двух словах
Описание транзакций и их параметров
Жизненный цикл транзакций
Сборка мусора
Как правильно получать статистику
Описание транзакций в IBAnalyst
Как блокировать записи в InterBase/Firebird
ClientDataSet
В отличие от "живого" просмотра и редактирования данных при помощи IBDataSet+TDataSource, данный компонент предназначен в основном для трехзвенных систем с кэшированием данных, пулами коннектов и т.п. спецификой. Также ClientDataSet используется как источник данных для Data Controls в тех случаях, когда библиотека доступа к серверу не содержит компонент, аналогичных DataSet. Это, например, dbExpress. По различным аспектам работы с ClientDataSet есть много статей, в данном контексте важно другое - ClientDataSet считывает все записи запроса, после чего их можно просматривать не только завершив транзакцию, но и вообще отсоединившись от сервера БД (пример BriefCase в поставке Delphi, C++Builder). Таким образом, даже длительность читающей транзакции можно сократить до минимума.
В качестве дополнительной информации
по транзакциям рекомендуется
о многоверсионности в двух словах
кооперативная сборка мусора
описание параметров транзакций
как правильно собирать статистику
описание транзакций в IBAnalyst
Перечитывание и сортировка записей
Для начала, примем за утверждение, что пользователь программы практически всегда хочет видеть данные (записи) упорядоченные каким-либо образом. Также ему может потребоваться периодически смотреть данные, отсортированные в том или ином виде (например список сотрудников по фамилии, по адресу, по дате рождения...). Если работать с IBDataSet/IBQuery, то это означает, что например по OnColumnClick в гриде надо сформировать запрос заново (буквально, заменить последнюю строку ORDER BY CLICKED_FIELD), и перевыполнить его. В результате при каждой такой "пересортировке" запрос отправляется на сервер, сервер выполняет запрос и сортирует данные (или выбирает их в порядке индекса), и данные передаются от сервера клиенту. Если речь идет о сортировке больших объемов данных (десятки тысяч записей), то все это порождает как повышенную загрузку сервера, так и повышенный трафик в сети.
Решить данную проблему можно двумя способами.
Первый способ основан
на ограничении выбираемых записей
путем предварительной
Второй способ основан на локальной буферизации выборки, и сортировки при помощи ClientDataSet. Этот компонент позволяет не только сортировать данные, но и индексировать столбцы в памяти, что повышает скорость локальной обработки данных. Если данные на сервере меняются не столь часто, или по крайней мере те данные, которые положено видеть пользователю, не меняются часто, то это - наилучший выход.
На первом этапе ClientDataSet загрузит все данные, что вызовет трафик в сети, но сам запрос может быть без указания сортировки (select * from table) , что существенно упростит работу сервера. После чего сортировки, поиски и т.п. можно выполнять уже над ClientDataSet, который все эти операции будет производить локально, без обращения к серверу. Но, нужно помнить, что клиентский компьютер должен иметь достаточно памяти, если выполняемый запрос вернет в ClientDataSet десятки и сотни тысяч записей.
Двухфазный коммит
Смотрите описание в www.ibase.ru/devinfo/ibtrans.
Примеры
В поставке IBX (как отдельно, так и вместе с Delphi), идут примеры. Если у вас этих примеров нет - значит версия IBX слишком старая, и надо брать обновление. Примеры находятся тут:
Delphi/Demos/DB/IBX или Delphi2005/Demos/
Admin Пример использования компонент Services API, с палитры InterBase Admin. Управление пользователями, сертификатами (лицензиями), backup/restore, статистика, свойства сервера.
CachedUp Стандартный пример, переведенный на IBX.
IBSilentInstall Пример использования ibinstall - библиотеки функций для создания собственных инсталляций InterBase 7.x.
IBXEvents Стандартный пример работы с событиями, переведенный на IBX
SQLMonitor Пример использования компонента IBSQLMonitor, трассировки операций (запросов, транзакций и т.п.), выполняемых приложением.
ThreadedIBX Пример параллельной работы
с соединениями из разных Threads. Как обычно,
в соединении с БД указан локальный коннект,
который не работает параллельно. Поэтому
для нормальной работы примера добавьте
к IBDatabase.DatabaseName имя сервера, например localhost:
(в итоге должно получиться localhost:C:\Program
Files\InterBase Corp\InterBase\Examples\
Выше на один каталог находится застарелый пример IBMastApp, в котором на все приложение есть только один компонент IBTransaction, да еще постоянно вызывается CommitRetaining. То есть, этот пример является примером, как не надо работать с транзакциями в IBX.
Другие примеры есть там же, где находятся все дистрибутивы и обновления IBX:
Остальные примеры те же самые, что упомянуты выше по тексту статьи.