Base de datos de Messenger (parte 2): partición "con ánimo de lucro"

Deseñamos con éxito a estrutura da nosa base de datos PostgreSQL para almacenar correspondencia, pasou un ano, os usuarios están a enchela activamente e agora contén millóns de rexistros, e... algo comezou a ralentizarse.

Base de datos de Messenger (parte 2): partición "con ánimo de lucro"
Feito é que A medida que o tamaño da táboa crece, tamén o fai a "profundidade" dos índices. - aínda que logarítmicamente. Pero co paso do tempo isto obriga ao servidor a realizar as mesmas tarefas de lectura/escritura procesar moitas veces máis páxinas de datosque ao principio.

Aquí é onde se trata do rescate seccionamento.

Permítanme sinalar que non estamos a falar de sharding, é dicir, de distribuír datos entre diferentes bases de datos ou servidores. Porque mesmo dividindo os datos en algúns servidores, non se librará do problema dos índices "inchados" co paso do tempo. Está claro que se pode permitirse o luxo de poñer en funcionamento un novo servidor todos os días, os seus problemas xa non estarán no plano dunha base de datos específica.

Consideraremos non scripts específicos para implementar a partición "en hardware", senón o enfoque en si: que e como se debe "cortar en franxas" e a que leva ese desexo.

Concepto

Definamos o noso obxectivo unha vez máis: queremos asegurarnos de que hoxe, mañá e nun ano, a cantidade de datos lidos por PostgreSQL durante calquera operación de lectura/escritura siga sendo aproximadamente a mesma.

Para calquera datos acumulados cronoloxicamente (mensaxes, documentos, rexistros, arquivos, ...) a elección natural como clave de partición é data/hora do evento. No noso caso, tal acontecemento é momento de enviar a mensaxe.

Teña en conta que os usuarios case sempre traballar só cos "últimos". tales datos: len as mensaxes máis recentes, analizan os rexistros máis recentes,... Non, por suposto, poden desprazarse máis atrás no tempo, pero fan isto moi raramente.

A partir destas limitacións é evidente que a solución de mensaxes óptima sería seccións "diarias". - despois de todo, o noso usuario case sempre lerá o que lle veu "hoxe" ou "onte".

Se escribimos e lemos case só nunha sección durante o día, isto tamén nos dá uso máis eficiente da memoria e do disco - xa que todos os índices de sección encaixan facilmente na memoria RAM, en contraste cos "grandes e gordos" de toda a táboa.

paso a paso

En xeral, todo o dito anteriormente soa como un beneficio continuo. E é alcanzable, pero para iso teremos que esforzarnos moito, porque a decisión de partición dunha das entidades leva á necesidade de “ver” aos asociados.

Mensaxe, as súas propiedades e proxeccións

Dado que decidimos cortar as mensaxes por datas, ten sentido dividir tamén as entidades-propiedades que dependen delas (arquivos adxuntos, lista de destinatarios) e tamén pola data da mensaxe.

Dado que unha das nosas tarefas típicas é precisamente ver os rexistros de mensaxes (non lidos, entrantes, todos), tamén é lóxico "debuxalos" para dividilos por datas de mensaxes.

Base de datos de Messenger (parte 2): partición "con ánimo de lucro"

Engadimos a clave de partición (data da mensaxe) a todas as táboas: destinatarios, ficheiro, rexistros. Non tes que engadilo á propia mensaxe, pero usa o DateTime existente.

Temas

Como só hai un tema para varias mensaxes, non hai forma de "cortalo" no mesmo modelo; tes que confiar noutra cousa. No noso caso é ideal data da primeira mensaxe na correspondencia — é dicir, o momento da creación, de feito, do tema.

Base de datos de Messenger (parte 2): partición "con ánimo de lucro"

Engade a clave de partición (data do tema) a todas as táboas: tema, participante.

Pero agora temos dous problemas á vez:

  • En que sección debo buscar mensaxes sobre o tema?
  • En que sección debo buscar o tema da mensaxe?

Podemos, por suposto, seguir buscando en todas as seccións, pero isto será moi triste e anulará todas as nosas ganancias. Polo tanto, para saber onde buscar exactamente, faremos ligazóns/punteiros lóxicos a seccións:

  • engadiremos na mensaxe campo de data do tema
  • imos engadir ao tema a data da mensaxe definida esta correspondencia (pode ser unha táboa separada ou unha matriz de datas)

Base de datos de Messenger (parte 2): partición "con ánimo de lucro"

Dado que haberá poucas modificacións na lista de datas de mensaxes para cada correspondencia individual (despois de todo, case todas as mensaxes caen en 1-2 días adxacentes), centrareime nesta opción.

En total, a estrutura da nosa base de datos adoptou a seguinte forma, tendo en conta a partición:

Táboas: RU, se tes aversión ao alfabeto cirílico nos nomes de táboas/campos, é mellor non mirar

-- секции по дате сообщения
CREATE TABLE "Сообщение_YYYYMMDD"(
  "Сообщение"
    uuid
      PRIMARY KEY
, "Тема"
    uuid
, "ДатаТемы"
    date
, "Автор"
    uuid
, "ДатаВремя" -- используем как дату
    timestamp
, "Текст"
    text
);

CREATE TABLE "Адресат_YYYYMMDD"(
  "ДатаСообщения"
    date
, "Сообщение"
    uuid
, "Персона"
    uuid
, PRIMARY KEY("Сообщение", "Персона")
);

CREATE TABLE "Файл_YYYYMMDD"(
  "ДатаСообщения"
    date
, "Файл"
    uuid
      PRIMARY KEY
, "Сообщение"
    uuid
, "BLOB"
    uuid
, "Имя"
    text
);

CREATE TABLE "РеестрСообщений_YYYYMMDD"(
  "ДатаСообщения"
    date
, "Владелец"
    uuid
, "ТипРеестра"
    smallint
, "ДатаВремя"
    timestamp
, "Сообщение"
    uuid
, PRIMARY KEY("Владелец", "ТипРеестра", "Сообщение")
);
CREATE INDEX ON "РеестрСообщений_YYYYMMDD"("Владелец", "ТипРеестра", "ДатаВремя" DESC);

-- секции по дате темы
CREATE TABLE "Тема_YYYYMMDD"(
  "ДатаТемы"
    date
, "Тема"
    uuid
      PRIMARY KEY
, "Документ"
    uuid
, "Название"
    text
);

CREATE TABLE "УчастникТемы_YYYYMMDD"(
  "ДатаТемы"
    date
, "Тема"
    uuid
, "Персона"
    uuid
, PRIMARY KEY("Тема", "Персона")
);

CREATE TABLE "ДатыСообщенийТемы_YYYYMMDD"(
  "ДатаТемы"
    date
, "Тема"
    uuid
      PRIMARY KEY
, "Дата"
    date
);

Aforra un bonito centavo

Ben, que pasa se usamos non opción clásica de seccionamiento en función da distribución dos valores de campo (a través de disparadores e herdanza ou PARTITION BY) e "manualmente" a nivel de aplicación, notarás que o valor da clave de partición xa está almacenado no nome da propia táboa.

Entón, se é así Estás moi preocupado pola cantidade de datos almacenados?, entón podes desfacerte destes campos "extra" e abordar táboas específicas. É certo que todas as seleccións de varias seccións neste caso terán que ser transferidas ao lado da aplicación.

Fonte: www.habr.com

Engadir un comentario