Параметры пула соединений с базой данных

база данных

пул соединений с базой данных

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

Вообще говоря, опытный инженер сталкивается с любой проблемой и пытается ответить на три вопроса: почему, что и как. Знание того, почему вы можете понять истинную цель проблемы, может помочь открыть ваш разум и избежать напрасных усилий. Какова основная концепция ответа на вопрос и является гарантией правильного ответа. Руководство дает повторяющиеся идеи решения проблем, чтобы проблема всегда решалась правильным и эффективным способом. В этой статье решается проблема настройки пула соединений с базой данных в соответствии с этой идеей.

1 Зачем нужен пул соединений

Для доступа к любой базе данных необходимо сначала установить соединение с базой данных. Это сложный, медленный процесс. Он включает в себя ряд задач, таких как установление связи (включая трехстороннее рукопожатие TCP), аутентификация, авторизация, инициализация и выделение ресурсов. Более того, сервер баз данных обычно отделен от сервера приложений, а все операции — это распределенные сетевые запросы и обработка.время подключения к базе данныхОбычно 100 мс и более. Обычно операции базы данных CRUD для небольших данных выполняются на уровне мс или короче, а с сетевой задержкой от 10 до 50 мс можно выполнить большинство результатов обработки базы данных. Некоторые подключения к базе данных устанавливаются заранее при запуске приложения, и приложение может значительно повысить скорость отклика, используя существующие подключения. Кроме того, когда в приложениях веб-службы много клиентов, много потоков, слишком много соединений, а частое создание/удаление соединений также влияет на производительность базы данных.

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

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

2 Что такое пул соединений с базой данных

2.1 Принцип реализации

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

В принципе, набор подключений к базе данных создается при запуске приложения. Также возможно динамически создавать, но повторно использовать существующие соединения. Эти соединения хранятся в структуре данных общего ресурса, называемой пулом соединений. Это типичная модель параллелизма производитель-потребитель. Каждый поток заимствует (заимствует) соединение, когда ему необходимо получить доступ к базе данных, и освобождает соединение (освобождает) обратно в пул соединений для использования другими потоками, когда использование завершено. Лучший компонент пула потоков будет иметь два параметра для динамического управления размером пула потоков: минимальное число и максимальное число. Минимум означает поддержание минимального количества подключений к базе данных в случае небольшой нагрузки. Когда количество потоков, одновременно обращающихся к базе данных, превышает минимальное число, динамически создается больше соединений. Максимальное число — это максимально допустимое количество подключений к базе данных.Когда используется максимальное количество подключений и новому потоку требуется доступ к базе данных, новый поток будет заблокирован до тех пор, пока подключение не будет возвращено в пул подключений. Когда нагрузка становится низкой, количество подключений в пуле превышает минимальное количество и используются только подключения ниже или равные минимальному количеству, подключения, превышающие минимальное количество, будут закрыты и удалены для экономии системных ресурсов.

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

Соединение выполняется в потоке, который его заимствовал, когда оно используется, а не в новом потоке. Но поскольку каждое соединение должно реализовывать используемый механизм тайм-аута, официальныйJava.sql.Connection.setNetworkTimeout APIОпределение интерфейсаsetNetworkTimeoutExecutor executor, int milliseconds). Здесь вам нужно указать пул потоков для обработки отчета об ошибке тайм-аута. То есть для каждого соединения, выполняющего доступ к базе данных, будет фоновый поток, отслеживающий состояние тайм-аута ответа. Многие реализации пула соединений используют кэшированный пул потоков или фиксированный пул потоков. Chached Thread Pool не имеет ограничений на потоки, динамическое создание и повторное использование, что подходит для многих динамических приложений с короткими запросами. Фиксированный пул потоков подходит для относительно фиксированных запросов на подключение.

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

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

2.2 Системная архитектура пула соединений с базой данных

Суть пула соединений — это счетный семафор, принадлежащий процессу (процессу) операционной системы, который используется для контроля количества потоков, которые могут параллельно использовать соединения с базой данных. В Java SDK естьSemaphore ClassМожет использоваться для управления различными ограниченными объемами ресурсов. Основная функция управления пулом соединений заключается в выделении соединения с базой данных из пула требуемому потоку и повторном использовании соединения с пулом после того, как поток израсходован. Поскольку пул соединений ограничен, количество потоков, которые могут выполнять параллельный доступ к базе данных, не превышает максимального размера пула соединений. Если учесть возможность того, что поток приложения может использовать несколько подключений к базе данных, количество потоков, которые могут одновременно обращаться к базе данных, будет меньше.

Потребителями пула соединений являются бизнес-приложения. Обычно существует два типа: один — поток службы HTTP, основанный на запросе пользователя/службы, обычно с использованием пула потоков. Характерно, что количество потоков меняется динамически, режимы доступа к БД более разнообразны, а время обработки также велико или мало, что может сильно различаться. Другой — фоновый сервис, количество потоков относительно фиксировано, а режим доступа к базе данных и время обработки относительно стабильны.

Пул соединений обеспечивает только установленные соединения для бизнес-приложений, и все запросы на доступ перенаправляются на фоновый сервер базы данных через соединение. Серверы баз данных также обычно используют пул потоков (один процесс на каждое соединение PostgreSQL) для обработки всех запросов на доступ.

В частности, пул соединений является промежуточным каналом между двумя пулами потоков. Его можно представить в виде следующей структуры:

Внутри одного или нескольких процессов службы приложений (пул потоков пул соединений с базой данных) пул потоков (или процессов) сервера базы данных

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

3 Как настроить пул соединений с базой данных

3.1 Настройка цели

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

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

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

Не перегружать сервер базы данных — глобальная проблема. Потому что может быть несколько пулов соединений нескольких серверов приложений, делающих запросы одновременно. Согласно документации PostgreSQL V1118.4.3. Resource Limits, каждое соединение обрабатывается отдельным процессом. Даже если каждый процесс простаивает, он потребляет много системных ресурсов, таких как память, семафор, файловый/сетевой дескриптор (обработчик), очередь и т.д. эта статьяNumber Of Database ConnectionsОбсуждается количество соединений для PostgreSQL V9.2. Предлагаемая формула приведена((core_count * 2) + effective_spindle_count), что в два раза больше количества ядер ЦП плюс количество шпинделей жесткого диска. MySQL использует другую сервисную архитектуру,MySQL Too many connectionsКоличество подключений по умолчанию равно 151. Эти две системы сильно отличаются конкретным механизмом реализации, методом расчета и рекомендуемым значением, поскольку программисты приложений должны иметь базовое понимание.

этоOLTP performance -- Concurrent Mid-tier connectionsВидео моделируется с помощью пула потоков службы приложений. Пул потоков службы приложений имеет 9600 потоков, которые постоянно обращаются к базе данных.Когда размер пула соединений составляет 2048 и 1024, база данных находится в перегруженном состоянии, существует много событий ожидания базы данных, а загрузка ЦП базы данных достигает 95%. . Когда пул соединений уменьшается до 96, сервер базы данных не имеет событий ожидания, загрузка ЦП составляет 20%, время ожидания запроса на доступ к базе данных сокращается с 33 мс до 1 мс, а время выполнения SQL базы данных сокращается с 77 мс до 2 мс. Общее время отклика на доступ к базе данных сократилось со 100 мс до 3 мс. В настоящее время, в случае одного пула потоков службы приложений по сравнению с одним пулом потоков службы базы данных, производительность обработки базы данных в общей сложности 96 пулов соединений намного превышает производительность 1000 пулов соединений. Сервер базы данных должен выделять ресурсы для каждого соединения.

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

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

3.2 Метод настройки

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

Ограничение двух концов: найдите максимальное значение двух концов, и меньшее значение является верхним пределом пула соединений. Размер пула потоков службы приложений, например, значение по умолчанию для максимального размера пула потоков Tomcat — 200. Если каждый поток использует только одно соединение с базой данных, максимальное количество пулов соединений должно быть меньше или равно 200. Если некоторые запросы используют более одного соединения, увеличьте его соответствующим образом. Если максимальный размер пула потоков (процессов) базы данных равен 151, возьмите меньшее из двух значений (200, 151), тогда максимальный размер пула соединений должен быть меньше или равен 151. Если есть другие пулы соединений, также рассмотрите их глобально. Это значение является верхней строкой пула соединений.

Ограничения нагрузки приложений. Учитывайте характер нагрузки служб приложений. Сервисы приложений можно разделить на две категории. Один тип — это пул потоков службы веб-приложений с большим количеством изменений, тогда пул соединений также может быть настроен как динамический, и могут быть настроены соответствующие минимальные и максимальные значения. Другой тип — это бизнес-приложение с фиксированной нагрузкой, такое как почтовая служба, которую можно настроить с помощью пула процессов фиксированного размера. Эти два типа приложений можно оценить по сложности доступа к базе данных и времени отклика. используется здесьLittle's Law:并发量 = 每秒请求数量 * 数据库请求响应时间. Примечание. Время ответа на запрос здесь включает сетевое время + время доступа к базе данных. Время в сети во много раз превышает время доступа к базе данных. Если поток приложения имеет несколько запросов на доступ к базе данных, особенно при обработке транзакций, время ответа на этот запрос к базе данных фактически равно времени удержания соединения, и формула принимает следующий вид:并发量(连接数): 每秒请求数 (QPS)* 数据库连接持有时间.

Если есть 100 запросов на доступ к базе данных в секунду, и каждый запрос на доступ к базе данных занимает 20 мс, то уровень параллелизма равен100 * 0.02 = 2, достаточно двух одновременных подключений к базе данных. Точно так же, если каждый запрос занимает 100 мс, требуется 10 одновременных подключений.

Принимая во внимание только размер двухтерминального пула потоков (процессов), можно настроить чрезмерно большой пул соединений, поскольку это верхний предел системы. Потому что доступ к базе данных — это только часть работы потока приложения. Причина в формуле расчета количества потоков:线程数目 = CPU核数 * CPU 利用率 * (1 + 等待时间 / CPU计算时间), время ожидания базы данных составляет лишь часть времени ожидания всех операций потока.

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

3.3 Внешне связанная, на самом деле не относящаяся к делу формула

Поскольку пулы соединений и пулы потоков часто путают, необходимо ввести еще одну часто упоминаемую, но не относящуюся к делу формулу расчета количества потоков. Эта формула взята из того, что должен прочитать каждый Java-программист.Java Concurrency in Practice. В разделе 8.2 на странице 171 оригинальной книги автор приводит знаменитую формулу для расчета количества потоков:线程数目 = CPU核数 * CPU 利用率 * (1 + 等待时间 / CPU计算时间). Эта формула учитывает различные режимы обработки, требующие больших вычислительных ресурсов (время вычислений) и операций ввода-вывода (задержка).Рассчитать использование ЦП процессомДаны конкретные технические методы и сценарий. Однако эту формулу можно использовать для оценки размера пула потоков службы приложений или любого пула потоков, но она не имеет ничего общего с оценкой размера пула соединений с базой данных. Поскольку пул процессов не контролирует количество потоков службы приложений, он контролирует количество параллельных потоков доступа к базе данных. Эти потоки используют соединение с базой данных для завершения асинхронной работы сетевой службы и удаленной базы данных и в настоящее время в основном не используют вычислительное время ЦП машины. Применение формулы приведет к очень большим числам, которые являются спорными.

4 Конфигурация пула соединений для приложений Spring + MySQL

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

4.1 Количество потоков службы приложений

веснаserver.tomcat.max-threadsПараметр задает максимальное количество параллельных потоков, значение по умолчанию — 200. Благодаря специальной обработке эти потоки могут обрабатывать большее количество HTTP-соединений.server.tomcat.max-connections, значение по умолчанию — 10000.spring.task.execution.pool.max-threadsконтролировать использование@AsyncМаксимальное количество потоков, значение по умолчанию не ограничено. Лучше всего настроить диапазон на основе характеристик приложения.

4.2 Количество соединений в базе данных

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

4.3 Настройка основных параметров

Spring использует по умолчаниюHikariCP.

Основные параметры, которые необходимо настроить, следующие.

  • maxPoolSize: максимальное количество подключений. За пределами этого числа новые потоки доступа к базе данных блокируются. Значение по умолчанию — 10.
  • MinimumIdle: минимальное количество подключений. По умолчанию установлено максимальное количество подключений.
  • LeakDetectionThreshold: время сигнала тревоги соединения не было возвращено. Значение по умолчанию равно 0, что не включено. Если это значение больше 0, если соединение используется дольше, чем это значение, будет зарегистрирован аварийный сигнал (информация журнала на уровне предупреждений). Учитывая нагрузку на сеть, его можно установить в 3 или 5 раз больше максимального времени запроса к базе данных. Без этого сигнала трудно гарантировать правильность программы.
  • maxLifetime: максимальное время жизни соединения. Значение по умолчанию — 30 минут. Официальная документация рекомендует устанавливать это значение немного меньше, чем максимальное время жизни соединения с базой данных. Значение по умолчанию для MySQL — 8 часов. Можно установить на 7 часов 59 минут, чтобы избежать повторного подключения каждые полчаса.

4.4 Оптимизированные настройки базы данных

HikariCP Рекомендуемая конфигурация MySQLПараметры и рекомендуемые значения следующие, эти конфигурации помогают улучшить производительность доступа к базе данных.Значения этих параметров по умолчанию находятся вДокументация MySQL JDBC

  • prepStmtCacheSize: 250–500. По умолчанию: 25.
  • prepStmtCacheSqlLimit: 2048. По умолчанию: 256.
  • cachePrepStmts: true По умолчанию: false.
  • useServerPrepStmts: true По умолчанию: false.

4.5 Корректировка прогноза дальнейшего развития бизнеса

Пока все наши расчеты и конфигурации основаны на существующей программно-аппаратной конфигурации и масштабах бизнеса, а бизнес постоянно развивается, поэтому у нас также должно быть соответствующее рассмотрение для будущего развития бизнеса. Например, предположим, что через год объем нашего бизнеса удвоился, тогда мы можем соответственно удвоить наш фиксированный расчет (при условии, что лимит машин не достигнут). Эта часть тесно связана с оценкой бизнеса. Лучше всего настроить машину в соответствии с верхним пределом машины в начале и отслеживать эффективность бизнеса всех аспектов в режиме реального времени. Когда существующая машина не может ее поддерживать, она можно растянуть по горизонтали во времени. (Если вы используете здесь технологию динамического масштабирования, вы можете быть уверены, что вам не нужно настраивать вручную).

5 других

5.1 Другие детали реализации пула соединений

Конкретная реализация пула соединений должна учитывать множество деталей приложения.

  • Использование соединений с базой данных также включает обработку транзакций.Синхронный доступ к базе данных Spring использует ThreadLocal для сохранения состояния, связанного с транзакцией. Следовательно, когда пул соединений выполняет доступ к базе данных, он должен находиться в потоке вызывающего объекта и не может выполняться в новом потоке. Spring асинхронный доступ к базе данных может пересекать потоки.
  • Избыточные соединения не закрываются немедленно, а перед закрытием ждут некоторое время простоя.
  • Соединение имеет ограничение на максимальное время жизни.Даже если пул соединений игнорируется, база данных автоматически закроет соединение, которое превышает время жизни. В MySql максимальное время жизни соединения составляет 8 часов. Пул соединений должен регулярно отслеживать и очищать недействительные соединения.
  • Пул соединений должен регулярно проверять состояние доступности и даже время отклика базы данных, а также своевременно сообщать о состоянии работоспособности.HikariCP Dropwizard HealthChecksявляется примером.
  • Когда необходимо создать соединение для доступа нового потока, новый поток должен ожидать первого доступного соединения в пуле, не дожидаясь созданного для него потока. Документация для HikariCPWelcome to the JungleОписаны преимущества этой реализации: избежать создания множества ненужных соединений и иметь лучшую производительность. Хикари использовал 5 подключений для обработки 50 пакетов краткосрочных запросов на доступ к базе данных, что повысило скорость ответа и позволило избежать создания дополнительных подключений.
  • Обработка исключений базы данных.Bad Behavior: Handling Database DownПредложите различные способы обработки времени ожидания блокировки потока в различных реализациях компонентов пула соединений. Многие компоненты пула соединений не обрабатывают это правильно.
  • Мониторинг производительности пулов потоков.HikariCP Dropwizard MetricsПриведены контролируемые показатели производительности.
  • Механизм блокировки потоков и связанные с ним структуры данных сильно влияют на производительность пула соединений.Down the Rabbit HoleДан метод оптимизации в Java. Недостатком является то, что некоторые оптимизации слишком тривиальны, что делает код неясным и требует дополнительного обслуживания.

5.2 Некоторые эталонные конфигурации по умолчанию

HikariCP: DEFAULT_POOL_SIZE = 10

DBCP: Max pool size : 8

c3p0: MIN_POOL_SIZE = 3, MAX_POOL_SIZE = 15

JIRA Tuning database connections:pool-max-size = 20. В отличие от первых трех, это приложение базы данных. Обсуждается количество подключений к базе данных.С одной стороны, база данных может поддерживать сотни параллельных подключений.С другой стороны, подключение на сервере приложений все еще является относительно ресурсоемким.Количество рекомендуется устанавливать как как можно меньше.

5.3 Отступление

Я много искал в Интернете, но я не ожидал, что такая простая проблема конфигурации пула соединений с базой данных не будет иметь более всеобъемлющего и ясного документа. Есть много людей, которые путают пулы соединений с пулами потоков. Даже программисты, внедряющие HikariCP, инициализировали пулы соединений с неправильным количеством пулов потоков. Накладные расходы на создание пула потоков в основном связаны с задержкой сетевых запросов и запросов к удаленной базе данных, и они почти не потребляют ресурсы ЦП. Согласно формуле расчета потоков, в настоящее время пул потоков может быть очень большим. Но программисты HikariCP до сих пор используют толькоRuntime.getRuntime().availableProcessors()Количество потоков, используемых для создания пула соединений. Правильным числом должно быть минимальное количество настроенных пулов подключений, которое не является расточительным (когда количество подключений меньше количества ядер ЦП) и обеспечивает наилучшую производительность (когда количество подключений превышает количество ядер ЦП). ). Обратитесь к этому выпуску:Change the thread pool size to minimumIdle on blocked initialization. Эта ошибка неудивительна, так как у HikariCP плохой стиль кодирования. Многие широко используемые программы с открытым исходным кодом на самом деле не отличаются высоким качеством кода.Каждый должен понимать концепцию и характер проблемы, понимать чужие идеи, но сохранять скептическое и независимое мышление.