Совет: адрес исходного кода указан ниже, пожалуйста, получите его самостоятельно.
предисловие
Наиболее часто используемой реляционной базой данных в интернет-проектах является MySQL.С ростом числа пользователей и бизнеса традиционный режим с одной базой данных и одной таблицей не может удовлетворить большой объем хранения бизнес-данных и запросов.Эффективность ввода и запросов очень высока. медленно.В настоящее время для ее решения следует принять стратегию подбазы данных и подтаблицы.
Напоминание: Ниже приведен текст этой статьи, дело только для справки.
1. Введение в бизнес-сценарии
Предполагая, что существует система электронной коммерции, использующая MySQL, решение с большим хранением данных, высоким совпадением, высокой производительностью и масштабируемостью необходимо спроектировать, и в базе данных необходимо выполнить пользовательскую таблицу. Там будет много пользователей, а для достижения высокой масштабируемости, как бы вы спроектировали? Хорошо, давайте сначала посмотрим на традиционный путь подзабои подставки
Конечно, некоторые мелкие партнеры умеют разделять базы данных по провинциям/регионам или определенным деловым отношениям.Хорошо, вопрос в том, как обеспечить разумное хранение данных в разных таблицах в разных библиотеках? Позволить библиотеке уменьшить давление параллелизма? Как мы должны сформулировать правила для подбазы данных и подтаблицы? Не волнуйся, это придет
Во-вторых, горизонтальный метод подтаблицы подбиблиотеки
1.RANGE
В первом методе вы можете указать диапазон данных для разделения таблицы, например, от 1 до 1000000, 1000001-2000000, используя один миллион таблиц, как показано на следующем рисунке.
Конечно, этот метод должен поддерживать идентификатор таблицы, особенно в распределенной среде.Для этого типа распределенного идентификатора рекомендуется использовать Redis без использования стороннего инструмента разделения таблицы.Операция incr Redis может легко поддерживать распределенный идентификатор Table ID.
Преимущества метода RANGE:Расширение простое, просто создайте библиотеку и таблицу заранее.
Недостатки метода RANGE:Большинство операций чтения и записи будут обращаться к новым данным, и есть узкие места ввода-вывода. Это создаст слишком большую нагрузку на новую библиотеку. Это не рекомендуется.
2. ХЭШ по модулю
Ввиду проблемы узкого места ввода-вывода в вышеупомянутом методе RANGE мы можем использовать метод по модулю в соответствии с идентификатором пользователя HASG для разделения базы данных и таблицы, как показано на рисунке:
Таким образом, данные могут быть рассредоточены по разным библиотекам и таблицам, что позволяет избежать проблем с узкими местами ввода-вывода.
Преимущества метода HASH по модулю:Это может гарантировать, что данные равномерно распределены по разным библиотекам и таблицам, что снижает нагрузку на базу данных.
Недостатки метода HASH по модулю:Расширяться хлопотно, и каждый раз при переносе данных нужно пересчитывать хеш-значение и назначать его разным библиотекам и таблицам.
3. Согласованный ХЭШ
Взять по модулю HASH — не самый идеальный способ, так что же это?
Используйте согласованный алгоритм HASH для идеального решения проблемы.
Обычный алгоритм HASH:
Обычные алгоритмы хеширования сопоставляют двоичные значения произвольной длины с более короткими двоичными значениями фиксированной длины, и это маленькое двоичное значение называется хеш-значением. Хэш-значение — это уникальное и чрезвычайно компактное числовое представление части данных.
Обычный алгоритм HASH не подходит для распределенных приложений: в распределенной системе хранения данных для хранения данных на определенном узле, если мы используем обычный алгоритм HASH для маршрутизации, сопоставления данных с определенным узлом, например, Key%N, Key — это ключ, n машинного узла, если есть машина для присоединения или выхода из кластера, все сопоставления данных недействительны, если это постоянное хранилище, вы хотите выполнить миграцию данных, если это распределенный кеш, другие кеши недействительны .
Последовательный алгоритм HASH:В соответствии с широко используемым алгоритмом хеширования соответствующий ключ хэшируется в пространство с 2^32 узлами, то есть в цифровое пространство 0~(2^32)-1. Теперь мы можем соединить эти числа «голова к хвосту» и представить себе замкнутый цикл, как показано на изображении ниже.
Это кольцо соединено встык, тогда допустим есть три узла сервера базы данных node1, node2, node3, каждый узел отвечает за свою часть хранилища пользовательских данных, допустим есть пользователи user1, user2, user3, мы можем выполнять операции над серверными узлами.Операция HASH, предполагая, что после вычисления HASH user1 попадает на node1, user2 попадает на node2, а user3 попадает на user3
Хорошо, теперь давайте предположим, что узел node3 выходит из строя.user3 упадет на node1, а предыдущие данные node1 и node2 не изменятся, а дальше предположим, что добавлен node node4
Вы найдете, что User3 будет упасть на Node4, и вы обнаружите, что благодаря анализу добавления и удаления узлов последовательный алгоритм хеширования поддерживает монотонность при минимизации миграции данных. Это очень подходит для кластера, что позволяет избежать большого количества Миграция данных и уменьшает давление на сервере.
Конечно, есть также проблема, которая должна быть решена, и это равновесия. Из рисунка можно увидеть, когда возникает относительно небольшой немногим сервером, когда возникает проблема, является фокусом на этот раз неизбежно приведет к большому количеству данных в узле выше, очень мало централизованных данных на другой узел выше.
Чтобы решить эту проблему перекоса данных, алгоритм последовательного хеширования вводит механизм виртуального узла, то есть несколько хэшей вычисляются для каждого сервисного узла, и узел помещается в каждую позицию результата вычисления, которая называется виртуальным узлом. В частности, вы можете сначала определить количество виртуальных узлов, связанных с каждым физическим узлом, а затем добавить число после IP-адреса или имени хоста. Например, в приведенном выше случае для каждого сервера можно рассчитать три виртуальных узла, поэтому «узел 1–1», «узел 1–2», «узел 1–3», «узел 2–1», «узел 2 -2", "узел 2-3", "узел 3-1", "узел 3-2", "узел 3-3" хеши, таким образом образуя девять виртуальных узлов
Например, когда user1 расположен на узле 1-1, узле 1-2 и узле 1-3, он фактически находится на узле 1, что может решить проблему неравномерности данных при небольшом количестве сервисных узлов. количество виртуальных узлов не фиксировано, три или максимум, минимум три, это просто пример, количество конкретных виртуальных узлов необходимо определить в соответствии с реальной бизнес-ситуацией.
Преимущества последовательного метода HASH:Метод виртуального узла может гарантировать, что данные распределяются равномерно по разным базам данных и таблицам, а добавление и удаление узлов не влияет на данные других узлов, которые отличаются высокой доступностью и устойчивостью к сбоям.
Недостатки согласованного метода по модулю:Ну, по сравнению с вышеупомянутым двумя, можно считать, что нет.
3. Модульное тестирование
Хорошо, без ерунды, давайте перейдем к модульному тесту, предполагая, что есть три узла, и каждый узел имеет три виртуальных узла.
package com.hyh.core.test;
import com.hyh.utils.common.StringUtils;
import org.junit.Test;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* 一致性HASH TEST
*
* @Author heyuhua
* @create 2021/1/31 19:50
*/
public class ConsistentHashTest {
//待添加入Hash环的服务器列表
private static String[] servers = {"192.168.5.1", "192.168.5.2", "192.168.5.3"};
//真实结点列表,考虑到服务器上线、下线的场景,即添加、删除的场景会比较频繁,这里使用LinkedList会更好
private static List<String> realNodes = new LinkedList<>();
//虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称
private static SortedMap<Integer, String> virtualNodes = new TreeMap<>();
//一个真实结点对应3个虚拟节点
private static final int VIRTUAL_NODES = 3;
/**
* 测试有虚拟节点的一致性HASH
*/
@Test
public void testConsistentHash() {
initNodes();
String[] users = {"user1", "user2", "user3", "user4", "user5", "user6", "user7", "user8", "user9"};
for (int i = 0; i < users.length; i++)
System.out.println("[" + users[i] + "]的hash值为" +
getHash(users[i]) + ", 被路由到结点[" + getServer(users[i]) + "]");
}
/**
* 先把原始的服务器添加到真实结点列表中
*/
public void initNodes() {
for (int i = 0; i < servers.length; i++)
realNodes.add(servers[i]);
for (String str : realNodes) {
for (int i = 0; i < VIRTUAL_NODES; i++) {
String virtualNodeName = str + "-虚拟节点" + String.valueOf(i);
int hash = getHash(virtualNodeName);
System.out.println("虚拟节点[" + virtualNodeName + "]被添加, hash值为" + hash);
virtualNodes.put(hash, virtualNodeName);
}
}
System.out.println();
}
//使用FNV1_32_HASH算法计算服务器的Hash值,这里不使用重写hashCode的方法,最终效果没区别
private static int getHash(String str) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < str.length(); i++)
hash = (hash ^ str.charAt(i)) * p;
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
// 如果算出来的值为负数则取其绝对值
if (hash < 0)
hash = Math.abs(hash);
return hash;
}
//得到应当路由到的结点
private static String getServer(String key) {
//得到该key的hash值
int hash = getHash(key);
// 得到大于该Hash值的所有Map
SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);
String virtualNode;
if (subMap.isEmpty()) {
//如果没有比该key的hash值大的,则从第一个node开始
Integer i = virtualNodes.firstKey();
//返回对应的服务器
virtualNode = virtualNodes.get(i);
} else {
//第一个Key就是顺时针过去离node最近的那个结点
Integer i = subMap.firstKey();
//返回对应的服务器
virtualNode = subMap.get(i);
}
//virtualNode虚拟节点名称要截取一下
if (StringUtils.isNotBlank(virtualNode)) {
return virtualNode.substring(0, virtualNode.indexOf("-"));
}
return null;
}
}
Здесь мы смоделируем ситуацию, когда 9 пользовательских объектов маршрутизируются после хеширования, и посмотрим на результаты
Суммировать
В среде с распределенной микросервисной архитектурой рекомендуется строго использовать согласованный алгоритм HASH для подбазы данных и подтаблицы.Конечно, проблемы согласованности бизнес-данных и распределенных транзакций также возникнут в распределенной среде.В следующем выпуске, мы обсудим согласованность данных и распределенные бизнес-решения
Примечание автора
Путь долгий.Я готов идти вверх и вниз с вами.Спасибо за ваши лайки, подборки и комментарии, и до встречи в следующем выпуске.
Смотрите, как я провожу вас по пути роста архитектора.
Адрес источника:Нажмите здесь, чтобы просмотреть исходный код.