Мое приложение: я написал сценарий на Go, который загружает большое количество (> 10 КБ) файлов JSON по FTP и записывает их содержимое в таблицу в локальном экземпляре SQL Server 2016. После того, как папка с файлами была импортирована, она запускает процедуру T-SQL, которая занимается дальнейшей обработкой данных. В целом это решение работает очень хорошо. Однако мне нужна высокая степень параллельных операций, чтобы иметь достаточную производительность. Обычно в каждом файле около 2000 записей, и мне нужно импортировать около 5000 файлов в папку. SQL Server работает локально, и производительность операторов не является проблемой.
Структура выполнения. Список файлов загружается через FTP, и каждое имя файла передается в Goroutine. Подпрограмма go выбирает файл (также через FTP), анализирует его и запускает вставку SQL. Простая инструкция вставки подготавливается в начале приложения ("INSERT INTO tbl (val1, val2, val3) SELECT $ 1, $ 2, $ 3" ), а затем выполняется в Goroutine (stmt .Exec (var1, var2, var3)) для каждой записи. Я контролирую максимальное количество выполняемых подпрограмм с помощью переменной канала (make (chan bool, MAXPAR), где MAXPAR = 10, но должно быть 30).
Проблема: всякий раз, когда я запускаю более ~ 15 параллельных подпрограмм (30 было бы оптимальным), через короткое время я получаю следующую ошибку (~ 1 КБ файлов)
Произошла ошибка, связанная с сетью или конкретным экземпляром, при установке соединения с SQL Server по адресу 127.0.0.1:1433. Обычно разрешается только одно использование каждого адреса сокета (протокол / сетевой адрес / порт).
Я мог найти в Интернете, что это может указывать на исчерпание стека TCP / IP (?), Но пока не смог найти решение моей проблемы. Из-за структуры кода только 30 операторов (если для MAXPAR установлено значение 30) будут выполняться одновременно, поэтому проблема не в количестве подключений. Как я понял, может быть, здесь ограничивающим фактором является не степень параллелизма, а количество выполнений в секунду.
Мои идеи: в настоящее время я могу придумать три сценария, которые могут помочь:
- Ограничение MAXPAR до 10. Однако это приводит к снижению производительности, и я не знаю, возникнет ли проблема в конечном итоге при этой настройке.
- Установление большего количества соединений в начале программы и случайное присвоение каждому рутине одного из соединений. Это снизит количество выполнений / подключений, но я не уверен, решит ли это мою проблему, если общее количество выполнений в секунду является проблемой.
- Использование формы массового оператора для отправки одного оператора вставки для каждого файла, а не одного для каждой записи.
Последний вариант на данный момент единственный, где я уверен, что он может сработать. Однако, поскольку у меня есть аналогичные сценарии вставки (где проблема до сих пор не возникала), я бы предпочел понять основную проблему и есть ли способ предотвратить ее, прежде чем я пойду и изменю все приложения.
Вопрос: Мой вопрос в том, есть ли у кого-нибудь опыт возникновения этой проблемы (т.е. предсказуемо ли это) и будет ли одно из вышеперечисленных решений (или другое решение) работать, исходя из вашего опыта. Если бы это был всего лишь один сценарий, я бы просто попробовал все решения, но, поскольку есть и другие сценарии, в которых я не уверен, могут ли они столкнуться с аналогичными проблемами, я бы хотел найти решение, в котором я могу быть достаточно уверен что это решено.
Система
- Windows 10 x64
- Версия для разработчиков SQL Server 2016 (x64)
- Go 1.6
- Библиотека: github.com/denisenkom/go-mssql
Спасибо