AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 193287
Accepted
ChrisJ
ChrisJ
Asked: 2017-12-16 20:36:35 +0800 CST2017-12-16 20:36:35 +0800 CST 2017-12-16 20:36:35 +0800 CST

Desempenho de pesquisa de Numérico vs String

  • 772

Estou trabalhando em um projeto que está usando o formato de chave do instagram .

TL;DR IDs inteiros de 64 bits.

Eles serão usados ​​para pesquisas, e também gostamos deles para classificar e agrupar, pois eles serão classificados naturalmente pelo tempo de criação.

Os valores estão entre 2^63 e 2^64, então (apenas) grande demais para caber dentro de um arquivo BIGINT.

Portanto, parece que nossas opções de armazenamento são numeric(20)ou varchar. varcharnão é tão ideal, já que teríamos que zerá-los para que a classificação funcione, mas haveria um impacto no desempenho ao usar um numérico para pesquisas?

postgresql index
  • 3 3 respostas
  • 4154 Views

3 respostas

  • Voted
  1. Best Answer
    Evan Carroll
    2017-12-17T14:12:02+08:002017-12-17T14:12:02+08:00

    Odeio ser o capitão óbvio neste, mas o Instagram generosamente fornece uma função que você vinculou que armazena as chaves comobigint .

    CREATE SCHEMA insta5;
    CREATE SEQUENCE insta5.table_id_seq;
    
    CREATE OR REPLACE FUNCTION insta5.next_id(OUT result bigint) AS $$
    DECLARE
        our_epoch bigint := 1314220021721;
        seq_id bigint;
        now_millis bigint;
        shard_id int := 5;
    BEGIN
        -- The %1024, is just a way of saying they only want 10bit wraparound.
        SELECT nextval('insta5.table_id_seq') % 1024 INTO seq_id;
    
        SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
        result := (now_millis - our_epoch) << 23;
        result := result | (shard_id << 10);
        result := result | (seq_id);
    END;
    $$ LANGUAGE plpgsql;
    

    Na verdade, eles estão usando o PostgreSQL. A partir dessa função, você pode ver que eles estão retornando um arquivo bigint. Então certamente você pode armazenar o resultado dessa função em bigint. Como uma nota especial, essa provavelmente não é a função que eles estão usando. Essa função provavelmente tem uma assinatura mais assim,

    insta5.next_id(smallint shard, OUT result bigint);
    

    Sabemos disso porque codificar um fragmento de 5não é tão útil e eles parecem indicar que estão usando essa funcionalidade. Então, nesse id de blog, eles se gabam de que seu ID compromete

    • total de 64 bits
    • 64-23 = 41 bits para carimbo de data/hora
    • 64-41 = 23 bits para estilhaço + ID de sequência
    • 10 bits para o id de sequência.
    • 13 bits para o fragmento.

    Teste rápido em seu código,

    test=# SELECT insta5.next_id();
           next_id       
    ---------------------
     1671372309237077023
    (1 row)
    

    Decompondo o ID

    Agora vamos jogar. Para o extra sexy, podemos criar funções auxiliares que obtêm os componentes internos do ID. Caso você queira saber o fragmento que o Instagram está usando ou seu carimbo de data/hora interno.

    -- 13 bits for shard
    CREATE FUNCTION insta5.get_shard(id bigint)
    RETURNS smallint
    AS $$
      SELECT ((id<<41)>>51)::smallint;
    $$ LANGUAGE sql;
    
    -- 10 bits for sequence id
    CREATE FUNCTION insta5.get_sequence(id bigint)
    RETURNS smallint
    AS $$
      SELECT ((id<<54)>>54)::smallint;
    $$ LANGUAGE sql;
    
    -- 41 bits for timestamp
    CREATE OR REPLACE FUNCTION insta5.get_ts(id bigint)
    RETURNS timestamp without time zone
    AS $$
      SELECT to_timestamp(((id >> 23) + 1314220021721 ) / 1000 )::timestamp without time zone;
    $$ LANGUAGE sql;
    

    Brincando, vamos pegar um id de teste.

    SELECT insta5.next_id();
           next_id       
    ---------------------
     1671390786412876801
    (1 row)
    
    SELECT
      insta5id,
      insta5.get_ts(insta5id),
      insta5.get_shard(insta5id),
      insta5.get_sequence(insta5id)
    FROM (VALUES
      (1671390786412876801::bigint),
      (insta5.next_id())
    ) AS t(insta5id);
    

    Retorna o seguinte,

          insta5id       |       get_ts        | get_shard | get_sequence 
    ---------------------+---------------------+-----------+--------------
     1671390786412876801 | 2017-12-16 17:02:09 |         5 |            1
     1671392537048257538 | 2017-12-16 17:05:38 |         5 |            2
    (2 rows)
    

    Lançando nosso próprio domínio de ID do Instagram

    Você pode até criar um explícito DOMAINsobre o tipo se quiser realmente limpar isso. É assim que eu pessoalmente armazenaria isso, observe que fiz algumas modificações adicionais.

    • Acrescentei COMMENTS- sempre uma boa prática.
    • Feito as funçõesIMMUTABLE
    • Adicionado insta5.next_idrequer um estilhaço explícito.

    Vamos largar o que tínhamos,

    DROP SCHEMA insta5 CASCADE;
    

    E recomeçar,

    CREATE SCHEMA insta5;
    COMMENT ON SCHEMA insta5 IS 'Instagram';
    
    CREATE DOMAIN insta5.id AS bigint;
    COMMENT ON DOMAIN insta5.id IS $$Instagram's internal ID type, based on example from "Sharding & IDs at Instagram"$$;
    
    CREATE SEQUENCE insta5.table_id_seq;
    
    CREATE OR REPLACE FUNCTION insta5.next_id(shard_id smallint)
    RETURNS insta5.id
    AS $$
    DECLARE
        our_epoch bigint := 1314220021721;
        seq_id bigint;
        result insta5.id;
        now_millis bigint;
    BEGIN
        SELECT nextval('insta5.table_id_seq') % 1024 INTO seq_id;
    
        SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
        result := (now_millis - our_epoch) << 23;
        result := result | (shard_id << 10);
        result := result | (seq_id);
        RETURN result;
    END;
    $$ LANGUAGE plpgsql;
    
    COMMENT ON FUNCTION insta5.next_id(smallint)
      IS 'Modifications made to require shard id';
    
    
    CREATE OR REPLACE FUNCTION insta5.get_shard(id insta5.id)
    RETURNS smallint
    AS $$
      SELECT ((id<<41)>>51)::smallint;
    $$ LANGUAGE sql
    IMMUTABLE;
    
    COMMENT ON FUNCTION insta5.get_shard(insta5.id)
      IS '13 bits from insta5.id representing shard';
    
    
    CREATE OR REPLACE FUNCTION insta5.get_sequence(id insta5.id)
    RETURNS smallint
    AS $$
      SELECT ((id<<54)>>54)::smallint;
    $$ LANGUAGE sql
    IMMUTABLE;
    
    COMMENT ON FUNCTION insta5.get_sequence(insta5.id)
      IS '10 bits from insta5.id representing sequence';
    
    
    CREATE OR REPLACE FUNCTION insta5.get_ts(id insta5.id)
    RETURNS timestamp without time zone
    AS $$
      SELECT to_timestamp(((id >> 23) + 1314220021721 ) / 1000 )::timestamp without time zone;
    $$ LANGUAGE sql
    IMMUTABLE;
    
    COMMENT ON FUNCTION insta5.get_ts(insta5.id)
      IS '41 bits from insta5.id representing timestamp';
    

    Tudo funciona como antes, mas agora você pode

    CREATE SCHEMA mySchema;
    CREATE TABLE mySchema.mydata ( insta5id  insta5.id ) ;
    

    Esta é provavelmente a melhor solução que você pode obter tímido de uma implementação C, e você provavelmente não deseja gerar um insta5idnunca. Esse é o trabalho deles. ;)

    Como outro aparte importante, você provavelmente nunca quer fazer isso. Não siga pelo exemplo. É para isso que serve o uuidtipo , e você deve usá-lo em vez de enrolar manualmente o seu. Especificamente, isso é estranhamente semelhante a uuid_generate_v1()in uuid-ossp, que armazena um MAC (shard) e timestamp

    Esta função gera um UUID de versão 1. Isso envolve o endereço MAC do computador e um carimbo de data/hora. Observe que UUIDs desse tipo revelam a identidade do computador que criou o identificador e a hora em que o fez, o que pode torná-lo inadequado para determinados aplicativos sensíveis à segurança.

    • 10
  2. Erwin Brandstetter
    2017-12-16T22:51:31+08:002017-12-16T22:51:31+08:00

    numericé maior e mais lento que bigint(8 bytes) em todos os aspectos. Infelizmente, como você disse, bigintnão é grande o suficiente. O máximo é 2^63-1= 9223372036854775801, 19 dígitos decimais.

    varcharou text(mesma coisa internamente) são maiores e mais lentos, ainda. Além disso, os tipos de caracteres são sobrecarregados com COLLATIONregras, a menos que sejam explicitamente definidos sem.

    SELECT pg_column_size(numeric '18446744073709551616')  -- 16 bytes in RAM
         , pg_column_size(text    '18446744073709551616')  -- 24 bytes in RAM
    

    (Mas 13/21 bytes no disco.) Veja:

    db<>fique aqui

    numericeconomiza espaço adicional para zeros à direita, o que textnão pode acontecer.

    numericnão permite que não-números sejam armazenados e impõe alguma sanidade fora da caixa.

    Perante as suas exigências, escolha numerice nunca olhe para trás.

    Relacionado:

    • Qual é a sobrecarga para varchar(n)?

    Ou você pode instalar a extensãopguint de Peter Eisentraut, um dos principais hackers do projeto Postgres.

    Certifique-se de ler o leia- me primeiro. A extensão fornece vários tipos inteiros adicionais (a maioria deles não assinados). Você pode apenas extrair uint8(inteiro não assinado de 64 bits) e descartar o resto para evitar a superlotação do sistema de tipos.

    • 8
  3. Jack Douglas
    2017-12-18T07:17:06+08:002017-12-18T07:17:06+08:00

    Os valores estão entre 2^63 e 2^64, então (apenas) grande demais para caber dentro de um BIGINT.

    O intervalo de a bigint é -9223372036854775808 a +9223372036854775807 , que é -2^63 a 2^63-1 — ou 2^64 inteiros distintos. O intervalo de seus identificadores é de 2 ^ 63 inteiros distintos, então eles se encaixam perfeitamente em um bigint, desde que você não se importe com um deslocamento:

    select round(power(2::numeric,63::numeric)) "2^63"
          ,round(power(2::numeric,64::numeric)) "2^64"
          ,(9223372036854775808::numeric-9223372036854775809::numeric)::bigint "offset low val"
          ,(18446744073709551616::numeric-9223372036854775809::numeric)::bigint "offset high val";
    
                   2^63 | 2^64 | baixo valor compensado | alto valor compensado
    ------------------: | -------------------: | -------------: | ------------------:
    9223372036854775808 | 18446744073709551616 | -1 | 9223372036854775807
    

    dbfiddle aqui

    Meu exemplo usa um deslocamento de -9223372036854775809 (-2^63+1), mas você pode escolher qualquer deslocamento que não estoure o bigint.

    Portanto, você precisará usar numericao apresentar as chaves e ao aplicar o deslocamento, mas não para armazenar as chaves ou operações como classificação.

    • 3

relate perguntas

  • Quanto "Padding" coloco em meus índices?

  • Sequências Biológicas do UniProt no PostgreSQL

  • O que significa "índice" em RDBMSs? [fechado]

  • Como criar um índice condicional no MySQL?

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve