Добавить в корзинуПозвонить
Найти в Дзене

## Оптимизация клиент-серверных приложений и особенности их разработки для использования в модели сервиса### Введение На момент написания э

## Оптимизация клиент-серверных приложений и особенности их разработки для использования в модели сервиса ### Введение На момент написания этой статьи для работы приложения в модели сервиса требовалась интеграция с 1С:Библиотекой стандартных подсистем (БСП) 8.2. Однако сейчас ведется работа по выделению функционала для разработки приложений в модели сервиса из БСП в отдельную библиотеку — 1С:Библиотека технологий сервиса (БТС). Это позволит ускорить внедрение и обновление необходимых подсистем. Пока эта работа не завершена, в статье будет использоваться термин БСП. ```1C // Пример интеграции с БСП ПодключитьБиблиотеку("БиблиотекаСтандартныхПодсистем"); ``` ### Работа с базой данных #### Эффективные запросы 1. **Адекватность запроса**: Запрос должен соответствовать решаемой задаче и выбирать только необходимые данные. Избыточные данные увеличивают нагрузку на базу данных, особенно при большом количестве пользователей. ```1C // Пример эффективного запроса Запрос = Новый Запрос; Запрос.Те

## Оптимизация клиент-серверных приложений и особенности их разработки для использования в модели сервиса

### Введение

На момент написания этой статьи для работы приложения в модели сервиса требовалась интеграция с 1С:Библиотекой стандартных подсистем (БСП) 8.2. Однако сейчас ведется работа по выделению функционала для разработки приложений в модели сервиса из БСП в отдельную библиотеку — 1С:Библиотека технологий сервиса (БТС). Это позволит ускорить внедрение и обновление необходимых подсистем. Пока эта работа не завершена, в статье будет использоваться термин БСП.

```1C

// Пример интеграции с БСП

ПодключитьБиблиотеку("БиблиотекаСтандартныхПодсистем");

```

### Работа с базой данных

#### Эффективные запросы

1. **Адекватность запроса**: Запрос должен соответствовать решаемой задаче и выбирать только необходимые данные. Избыточные данные увеличивают нагрузку на базу данных, особенно при большом количестве пользователей.

```1C

// Пример эффективного запроса

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Справочник.Наименование,

| Справочник.Код

|ИЗ

| Справочник.Номенклатура КАК Справочник

|ГДЕ

| Справочник.ВидНоменклатуры = &ВидНоменклатуры";

```

2. **Избегайте сложных запросов**:

- Избегайте чрезмерного количества соединений (например, 15 и более), операций выбора и выражений ИЛИ, так как это усложняет понимание запроса СУБД и может привести к предупреждению timeout warning.

```1C

// Пример сложного запроса (не рекомендуется)

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| ...

|ИЗ

| Таблица1 КАК Т1

| ЛЕВОЕ СОЕДИНЕНИЕ Таблица2 КАК Т2 ПО Т1.Поле = Т2.Поле

| ЛЕВОЕ СОЕДИНЕНИЕ Таблица3 КАК Т3 ПО Т2.Поле = Т3.Поле

| ...

|ГДЕ

| Т1.Поле1 = &Значение1

| ИЛИ Т2.Поле2 = &Значение2";

```

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

```1C

// Пример вложенного запроса (использовать с осторожностью)

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Т1.Поле1,

| (ВЫБРАТЬ Т2.Поле2 ИЗ Таблица2 КАК Т2 ГДЕ Т2.Поле = Т1.Поле) КАК Поле2

|ИЗ

| Таблица1 КАК Т1";

```

3. **Простые условия соединения**: Условия соединения должны быть простыми и использовать поля, по которым есть индексы, для обеспечения эффективности.

```1C

// Пример простого условия соединения с использованием индексированного поля

Запрос.Текст =

"ВЫБРАТЬ

| Т1.Поле1,

| Т2.Поле2

|ИЗ

| Таблица1 КАК Т1

| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Таблица2 КАК Т2 ПО Т1.ИндексированноеПоле = Т2.ИндексированноеПоле";

```

4. **Количество таблиц**:

- Чем больше таблиц участвует в запросе, тем сложнее СУБД построить эффективный план запроса.

- Сокращение количества таблиц — один из способов оптимизации неэффективных запросов.

```1C

// Пример запроса с минимальным количеством таблиц

Запрос.Текст =

"ВЫБРАТЬ

| Т1.Поле1,

| Т1.Поле2

|ИЗ

| Таблица1 КАК Т1";

```

#### Эффективные планы запросов

1. **План запроса**: СУБД самостоятельно определяет, как выполнить запрос, и строит план запроса, который может быть как эффективным, так и нет.

2. **Влияние на план запроса**:

- Хотя нет возможности явно указать СУБД, какой план использовать, можно косвенно влиять на него, используя:

- Текст запроса

- Существующие индексы

- Актуальную статистику СУБД

3. **Актуальность данных**: План запроса может меняться в зависимости от количества данных и актуальности статистики СУБД. Поэтому важно тестировать запросы на реальных данных и актуальной статистике.

#### Индексы

1. **Назначение индексов**: Индексы ускоряют выборку данных из таблицы. 1С:Предприятие автоматически создает индексы, но разработчик может влиять на них, устанавливая признак индексирования для реквизитов.

```1C

// Пример установки индексирования для реквизита

РеквизитОбъекта.Индексирование = Истина;

```

2. **Эффективность индексов**:

- Индексы, добавленные разработчиками, часто неэффективны, так как создаются без учета реальных условий использования.

- Чрезмерное количество индексов может ухудшить производительность, так как СУБД будет тратить больше времени на выбор подходящего индекса.

3. **Использование индексов**:

- Индекс будет использоваться, если поля, по которым происходит выборка, находятся в вершине индекса.

```1C

// Пример использования индекса

Запрос.Текст =

"ВЫБРАТЬ

| Т1.Поле1

|ИЗ

| Таблица1 КАК Т1

|ГДЕ

| Т1.ИндексированноеПоле = &Значение";

```

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

4. **Селективность индексов**:

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

- Индексы по неселективным полям (например, по полю типа Булево) неэффективны, так как не позволяют значительно сузить выборку.

```1C

// Пример неэффективного индекса (поле типа Булево)

РеквизитОбъекта.Индексирование = Истина; // Неэффективно для полей типа Булево

```

#### Транзакции

1. **Назначение транзакций**: Транзакции обеспечивают атомарность, согласованность, изолированность и долговечность операций с данными.

2. **Явное управление транзакциями**:

- В большинстве случаев транзакции открываются автоматически платформой.

```1C

// Пример явного управления транзакциями

НачатьТранзакцию();

Попытка

// Операции с данными

ЗафиксироватьТранзакцию();

Исключение

ОтменитьТранзакцию();

ВызватьИсключение;

КонецПопытки;

```

- При необходимости можно открывать транзакции явно с помощью методов НачатьТранзакцию(), ЗафиксироватьТранзакцию() и ОтменитьТранзакцию().

- Вложенные транзакции невозможны; при открытии новой транзакции увеличивается внутренний счетчик, но фактически транзакция остается одна.

3. **Управляемые блокировки**:

- Блокировки используются для предотвращения конфликтов при одновременном доступе к данным.

```1C

// Пример установки управляемой блокировки

БлокировкаДанных = Новый БлокировкаДанных;

ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрСведений.СчетаФактуры");

ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;

БлокировкаДанных.Заблокировать();

```

- Не следует блокировать данные, которые не изменяются или не требуют согласованного состояния.

- При изменении данных следует использовать исключительные блокировки, чтобы избежать взаимоблокировок (deadlocks).

4. **Изменения в 8.3**:

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

5. **Длительные транзакции**:

- Длительные транзакции нежелательны, так как увеличивают нагрузку на СУБД и могут привести к исчерпанию ресурсов.

- Рекомендуется разбивать длинные транзакции на более короткие.

#### Использование динамических списков

1. **Режимы работы**:

- Динамические списки могут работать в режиме динамического считывания данных, что является наиболее эффективным.

```1C

// Пример настройки динамического списка

ДинамическийСписок = Элементы.ДинамическийСписок;

ДинамическийСписок.ОсновнаяТаблица = "Справочник.Номенклатура";

ДинамическийСписок.ДинамическоеСчитываниеДанных = Истина;

```

- При отсутствии динамического считывания данные считываются порциями и хранятся в буфере на сервере.

- В отсутствие основной таблицы список считывает данные целиком, что неэффективно.

2. **Оптимизация запросов**:

- Запросы динамических списков должны быть простыми, чтобы обеспечить эффективное использование индексов.

```1C

// Пример запроса для динамического списка

ДинамическийСписок.ТекстЗапроса =

"ВЫБРАТЬ

| Справочник.Наименование,

| Справочник.Код

|ИЗ

| Справочник.Номенклатура КАК Справочник

|ГДЕ

| Справочник.ВидНоменклатуры = &ВидНоменклатуры";

```

- Для полей, по которым происходит сортировка, следует включать индексирование с дополнительным упорядочиванием.

```1C

// Пример установки индексирования с дополнительным упорядочиванием

РеквизитОбъекта.Индексирование = Истина;

РеквизитОбъекта.ДополнительноеПорядок = "Возр";

```

#### Поддержка различных СУБД

1. **Тестирование**:

- Приложение должно быть протестировано на всех поддерживаемых СУБД, чтобы обеспечить одинаковую эффективность работы.

```1C

// Пример проверки работы на разных СУБД

// Функция проверки работы на разных СУБД

Функция ПроверитьРаботуСУБД()

Если БазаДанных = "Файловая" Тогда

// Проверка для файловой СУБД

ИначеЕсли БазаДанных = "Microsoft SQL Server" Тогда

// Проверка для Microsoft SQL Server

ИначеЕсли БазаДанных = "IBM DB2" Тогда

// Проверка для IBM DB2

ИначеЕсли БазаДанных = "PostgreSQL" Тогда

// Проверка для PostgreSQL

ИначеЕсли БазаДанных = "Oracle Database" Тогда

// Проверка для Oracle Database

КонецЕсли;

КонецФункции;

```

- В идеале тестирование следует проводить на всех СУБД, но на практике можно ограничиться файловой версией и одной-двумя сторонними СУБД.

#### Поддержка веб-клиента

1. **Использование веб-клиента**:

- Приложение должно быть полностью работоспособно в веб-клиенте, если оно предназначено для использования в сервисе.

```1C

// Пример использования веб-клиента

// В процессе разработки и отладки используется веб-клиент

```

- Рекомендуется использовать веб-клиента в процессе разработки и отладки для обеспечения совместимости.

2. **Расширение для работы с файлами**:

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

```1C

// Пример использования расширения для работы с файлами

// Проверка наличия расширения

Если ЕстьРасширениеДляРаботыСФайлами() Тогда

// Использование расширения

Иначе

// Альтернативный вариант

КонецЕсли;

```

#### Производительность интерфейса

1. **Максимальное время отклика**:

- Рекомендуется стремиться к тому, чтобы максимальное время отклика на одно интерактивное действие пользователя не превышало 1 секунду.

```1C

// Пример измерения времени отклика

// Измерение времени отклика с помощью секундомера

```

2. **Измерение времени отклика**:

- Для измерения времени отклика следует использовать секундомер, а не показатели производительности или дополнительный код на встроенном языке.

```1C

// Пример измерения времени отклика с помощью секундомера

// (невозможно реализовать в коде)

```

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

3. **Факторы, влияющие на производительность**:

- **Количество клиент-серверных вызовов**: Их количество следует минимизировать, объединяя несколько вызовов в один.

```1C

// Пример минимизации клиент-серверных вызовов

// Объединение нескольких вызовов в один

ОбъединенныйВызов = Новый Массив;

ОбъединенныйВызов.Добавить("Вызов1");

ОбъединенныйВызов.Добавить("Вызов2");

ВыполнитьВызовы(ОбъединенныйВызов);

```

- **Программное изменение формы**: Изменение формы программным способом может снизить производительность из-за особенностей кэширования.

```1C

// Пример программного изменения формы (использовать с осторожностью)

Форма.Элементы.Поле1.Видимость = Ложь;

```

- **Сложные формы**: Формы с большим количеством элементов и условным оформлением работают медленнее.

```1C

// Пример создания сложной формы (не рекомендуется)

Форма.Элементы.Добавить("Закладка1", Тип("ГруппаФормы"));

Форма.Элементы.Добавить("Закладка2", Тип("ГруппаФормы"));

...

```

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

```1C

// Пример передачи большого объема данных (не рекомендуется)

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| ...

|ИЗ

| Таблица1 КАК Т1";

```

- **Использование ключевого слова Знач**:

- При объявлении параметров серверных процедур следует использовать Знач, если возвращаемое значение не нужно на клиенте, чтобы избежать передачи данных обратно.

```1C

// Пример использования ключевого слова Знач

Процедура СервернаяПроцедура(Знач Параметр1)

// Код процедуры

КонецПроцедуры;

```

- Для параметров типа Булево можно не использовать Знач, так как они передаются эффективно.

#### Длительные операции

1. **Механизм длительных операций**:

- Для выполнения длительных операций рекомендуется использовать механизм длительных операций БСП, который позволяет избежать "зависания" клиента.

```1C

// Пример использования механизма длительных операций

// Запуск фонового задания

ФоновоеЗадание = ФоновыеЗадания.СоздатьОбъект();

ФоновоеЗадание.Наименование = "ДлительнаяОперация";

ФоновоеЗадание.НачатьЗадание(ЭтотОбъект, "ВыполнитьДлительнуюОперацию");

```

- Функционал выполняется в фоновом задании, а клиент подключает обработчик ожидания для проверки завершения операции.

#### Бережное использование ресурсов

1. **Оперативная память**:

- Оперативная память является ценным ресурсом, особенно на сервере.

```1C

// Пример экономии оперативной памяти

// Использование курсорных выборок

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

// Обработка данных

КонецЦикла;

```

- Необходимо избегать неэффективного использования памяти, например, формирования больших структур данных в памяти.

- Для работы с большими файлами следует использовать последовательные методы чтения и записи, такие как ЧтениеXML, ЧтениеТекста, ЗаписьXML, ЗаписьТекста.

```1C

// Пример использования последовательных методов чтения и записи

ЧтениеXML = Новый ЧтениеXML;

ЧтениеXML.ОткрытьФайл("C:\Файл.xml");

Пока ЧтениеXML.Прочитать() Цикл

// Обработка данных

КонецЦикла;

ЧтениеXML.Закрыть();

```

2. **Утечки памяти**:

- Утечки памяти возникают из-за циклических ссылок и сложны в диагностике.

```1C

// Пример создания циклической ссылки (не рекомендуется)

Объект1 = Новый Объект1;

Объект2 = Новый Объект2;

Объект1.СсылкаНаОбъект2 = Объект2;

Объект2.СсылкаНаОбъект1 = Объект1;

```

- Следует избегать создания циклических ссылок и обращать внимание на структуры данных, которые могут их создавать.

3. **Повторное использование возвращаемых значений**:

- Модули с повторным использованием возвращаемых значений следует использовать только для данных, которые часто используются и не изменяются.

```1C

// Пример использования модуля с повторным использованием возвращаемых значений

// (не рекомендуется для строковых констант)

ОбщийМодуль.ПолучитьСтроку("Константа");

```

- Возвращаемые значения должны быть неизменяемыми, чтобы избежать ошибок.

4. **Работа с временными файлами**:

- Для создания временных файлов следует использовать функцию ПолучитьИмяВременногоФайла().

```1C

// Пример создания временного файла

ИмяВременногоФайла = ПолучитьИмяВременногоФайла();

```

- Разработчик должен самостоятельно удалять временные файлы после использования, так как платформа не всегда может это сделать.

5. **Использование неразделённых данных**:

- Использование неразделённых данных может повысить эффективность, но увеличивает сложность обслуживания.

```1C

// Пример использования неразделённых данных

// (необходимо соблюдать осторожность)

```

- Неразделённые данные следует использовать только для данных, которые должны быть общими для всех пользователей.

- Данные, введенные пользователем, не должны храниться в неразделённых данных.

6. **Оптимизация кода на встроенном языке**:

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

```1C

// Пример оптимизации кода

// Предварительное вычисление значения в цикле

Значение = ВычисляемоеЗначение();

Для i = 1 По 10000 Цикл

// Использование значения

КонецЦикла;

```

7. **Минимизация времени обновления версии информационной базы**:

- Обработчики обновления должны быть оптимизированы, чтобы сократить время простоя базы данных.

```1C

// Пример оптимизации обработчиков обновления

// Использование неразделенных обработчиков обновления

Процедура Обновление_1_0_0_1()

// Код обработчика обновления

КонецПроцедуры;

```

- Рек