Практика многопоточности для начинающих-продвинутых статей

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

предисловие

выше«Многопоточность для новичков»Остается один вопрос:

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

Я оставлю вам упражнение после чтения.Сцена похожа:

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

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

Планы пользователей сети

Я получил все ответы на официальном аккаунте и некоторых других платформах, и это действительно так.众人拾柴火焰高Какие.




Спасибо всем, кто принимал участие.

На самом деле, прочитав планы всех, большинство из них подумали, что данные надо сегментировать, потому что за один раз нельзя сделать большой объем данных.loadна память.

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

На самом деле идея неплохая, но есть проблема:

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

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

Но если хранилище заменить наRedisизStringСтруктура так не работает.

схема данных обхода

Есть ли решение, которое использует многопоточную загрузку для повышения эффективности, и потоки не должны конкурировать друг с другом за блокировки?

Давайте посмотрим на этот план:

Во-первых, при хранении этих десятков миллионов чисел мы выделяем его числовой сегмент и сохраняем его избыточно один раз.

Например, число18523981123Затем вам также нужно сохранить числовой сегмент:1852398.

Итак, когда у нас есть эти числа:

18523981123 18523981124 18523981125 13123874321 13123874322 13123874323

Мы также будем поддерживать данные сегмента номера как:

1852398 1312387

Поэтому я думаю, что все должны понять, что делать дальше.

Когда требуется обход:

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

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

Кто-то может сказать, а что, если количество чисел продолжает увеличиваться и данные числового сегмента достигают десятков тысяч или даже сотен тысяч?

На самом деле это та же идея, вы можете разделить числовой сегмент.

такой как раньше1852398числовой сегмент, затем я продолжаю разбивать на1852.

Таким образом, вам нужно только запустить поток для запроса сегмента дополнительного номера на основе предыдущего.fork/joinвкус.

Эта идея на самом деле похожа на ConcurrentHashMap в JDK1.7, и для позиционирования реальных данных требуется два позиционирования.

Распределенное решение

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

Этот верхний предел связан с конфигурацией сервера и количеством потоков.垂直扩展Увеличение производительности обработки одной машины.

Поэтому по мере увеличения объема данных нам обязательно нужно пройти水平扩展Для достижения наилучшей производительности это распределенное решение.

Предположим, теперь у меня есть сотни миллионов данных для прохождения, но моя текущая конфигурация сервера может поддерживать приложение только для запуска N потоков и работы в течение 5 минут.5000WДанные.

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

Но предпосылка здесь та же, что и выше: каждое приложение может толькообрабатывать собственные данные, блокировки быть не может (это сильно снизит производительность).

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

Давайте сначала визуализируем эту логику через картинку:

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

Первые данные относятся к приложению 0, вторые — к приложению 1, а третьи — к приложению 2. Последние данные аналогичны (простая операция по модулю).

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

Распределенные проблемы

Кажется, это не проблема, но как только будет введена распределенная система, она неизбежно появится.CAPЗдесь не нужно слишком много обсуждать, а заинтересованные друзья могут искать сами.

Первая проблема, которую необходимо решить:

Как эти три приложения узнают, какие данные сегмента номера они должны принимать? Например, приложение номер 0 взято0 3 6(это эквивалентно нижнему индексу сегмента номера), это настраивается в файле конфигурации?

Тогда если количество данных снова увеличится, а количество соответствующих машин увеличится до 5, то естественно приложение №0 не будет выбрано.0 3 6(данные изменятся после взятия модуля).

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

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

Когда он действительно начнет просматривать данные, распределительный центр сообщит этим трем приложениям:

Вы, ребята, собираетесь приступить к работе, ваш рабочий контент для приложения 00 3 6, приложение № 1 содержание вашей работы1 4 7, приложение № 2 содержание вашей работы2 5 8.

Таким образом, каждое приложение знает, с какими данными оно должно иметь дело.

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

Он возьмет существующий числовой сегмент по модулю 4 (приложения 3+1) и перераспределит данные, чтобы можно было снова гарантировать распределение данных.

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

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

Суммировать

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

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

Вы также можете оставить сообщение для обсуждения. 😄

Ваши лайки и ретвиты — лучшая поддержка.