Produtos secos 丨 Explicação detalhada da tabela de memória DolphinDB do banco de dados de série temporal

A tabela de memória é uma parte importante do banco de dados DolphinDB. A tabela de memória pode não apenas ser usada diretamente para armazenar dados, realizar leitura e gravação de dados em alta velocidade, mas também pode armazenar em cache os resultados intermediários do mecanismo de cálculo para acelerar o processo de cálculo. Este tutorial apresenta principalmente os cenários de classificação e uso de tabelas de memória DolphinDB e as semelhanças e diferenças nas operações de dados e operações de estrutura de tabela (esquema) de várias tabelas de memória.


1. Categoria da tabela de memória

De acordo com os diferentes cenários de uso e características funcionais, as tabelas de memória DolphinDB podem ser divididas nos seguintes quatro tipos:

  • Tabela de memória convencional
  • Tabela de memória de valor-chave
  • Tabela de dados de fluxo
  • Tabela de memória MVCC


1.1 Tabela de memória convencional

A tabela de memória convencional é a estrutura de tabela mais básica no DolphinDB e suporta operações como adição, exclusão, modificação e consulta. Os resultados retornados por consultas SQL geralmente são armazenados em tabelas de memória convencionais, aguardando processamento posterior.

  • crio

Use a função de tabela para criar uma tabela de memória convencional. A função da tabela tem dois usos: o primeiro uso é baseado no esquema especificado (tipo de campo e nome do campo) e capacidade da tabela (capacidade) e o número inicial de linhas (tamanho) para gerar; o segundo uso é através dos dados existentes ( matriz, tabela, matriz e tupla) para gerar uma tabela.

A vantagem de usar o primeiro método é que a memória pode ser alocada para a tabela com antecedência. Quando o número de registros da tabela exceder a capacidade, o sistema automaticamente expandirá a capacidade da tabela. Ao expandir, o sistema irá primeiro alocar um espaço de memória maior (aumentar de 20% para 100%), em seguida, copiar a tabela antiga para a nova tabela e, finalmente, liberar a memória original. Para tabelas maiores, o custo de expansão será maior. Portanto, se podemos prever o número de linhas na tabela com antecedência, é recomendável alocar uma capacidade razoável com antecedência ao criar a tabela de memória. Se o número inicial de linhas da tabela for 0, o sistema gerará uma tabela vazia. Se o número inicial de linhas não for 0, o sistema irá gerar uma tabela com o número de linhas especificado e o valor de cada coluna da tabela é o valor padrão. Por exemplo:

//创建一个空的常规内存表
t=table(100:0,`sym`id`val,[SYMBOL,INT,INT])

//创建一个10行的常规内存表
t=table(100:10,`sym`id`val,[SYMBOL,INT,INT])
select * from t

sym id val
--- -- ---
    0  0  
    0  0  
    0  0  
    0  0  
    0  0  
    0  0  
    0  0  
    0  0  
    0  0  
    0  0  

A função de tabela também permite criar uma tabela de memória convencional a partir de dados existentes. O exemplo a seguir é criado por vários arrays.

sym=`A`B`C`D`E
id=5 4 3 2 1
val=52 64 25 48 71
t=table(sym,id,val)
  • inscrição

A tabela de memória convencional é uma das estruturas de dados mais frequentemente usadas no DolphinDB, perdendo apenas para os arrays. Os resultados da consulta de instruções SQL e os resultados intermediários de consultas distribuídas são armazenados em tabelas de memória convencionais. Quando a memória do sistema é insuficiente, a tabela não transborda automaticamente os dados para o disco, mas Out Of Memory é anormal. Portanto, quando realizamos várias consultas e cálculos, devemos estar atentos ao tamanho dos resultados intermediários e aos resultados finais. Quando alguns resultados intermediários não forem mais necessários, libere-os a tempo. Com relação aos vários usos de adição, exclusão, modificação e verificação de tabelas de memória convencionais, você pode consultar outro tutorial sobre como carregar e operar tabelas de partição de memória .


1.2 Tabela de memória de valor-chave

A tabela de memória de valor-chave é uma tabela de memória no DolphinDB que oferece suporte a chaves primárias. Ao especificar um ou mais campos na tabela como a chave primária, os registros na tabela podem ser determinados exclusivamente. A tabela de memória de valor-chave suporta operações como adição, exclusão, modificação e consulta, mas o valor da chave primária não pode ser atualizado. A tabela de memória de valor-chave registra o número da linha correspondente a cada valor-chave por meio de uma tabela de hash, portanto, tem uma eficiência muito alta para pesquisa e atualização baseada em valor-chave.

  • crio

Use a função keyedTable para criar uma tabela de memória de valor-chave. Esta função é muito semelhante à função da tabela, a única diferença é que um parâmetro é adicionado para indicar o nome da coluna chave.

//创建空的键值内存表,主键由sym和id字段组成
t=keyedTable(`sym`id,1:0,`sym`id`val,[SYMBOL,INT,INT])

//使用向量创建键值内存表,主键由sym和id字段组成
sym=`A`B`C`D`E
id=5 4 3 2 1
val=52 64 25 48 71
t=keyedTable(`sym`id,sym,id,val)
Nota: Ao criar uma tabela de memória de valor-chave especificando a capacidade e o tamanho inicial, o tamanho inicial deve ser 0.

Também podemos usar a função keyedTable para converter tabelas de memória convencionais em tabelas de memória de valor-chave. Por exemplo:

sym=`A`B`C`D`E
id=5 4 3 2 1
val=52 64 25 48 71
tmp=table(sym, id, val)
t=keyedTable(`sym`id, tmp)
  • Recursos de inserção e atualização de dados

Ao adicionar um novo registro à tabela de memória de valor-chave, o sistema verificará automaticamente o valor da chave primária do novo registro. Se o valor da chave primária no novo registro não existir na tabela, então adicione um novo registro à tabela; se o valor da chave primária do novo registro duplicar o valor da chave primária do registro existente, o valor da chave primária correspondente em a tabela será atualizada registro de. Por favor, veja o exemplo abaixo.

Primeiro, insira um novo registro na tabela vazia de memória de valor-chave. Os valores da chave primária no novo registro são AAPL, IBM e GOOG.

t=keyedTable(`sym,1:0,`sym`datetime`price`qty,[SYMBOL,DATETIME,DOUBLE,DOUBLE]);
insert into t values(`APPL`IBM`GOOG,2018.06.08T12:30:00 2018.06.08T12:30:00 2018.06.08T12:30:00,50.3 45.6 58.0,5200 4800 7800);
t;

sym  datetime            price qty 
---- ------------------- ----- ----
APPL 2018.06.08T12:30:00 50.3  5200
IBM  2018.06.08T12:30:00 45.6  4800
GOOG 2018.06.08T12:30:00 58    7800

Insira um lote de novos registros com valores de chave primária de AAPL, IBM e GOOG na tabela novamente.

insert into t values(`APPL`IBM`GOOG,2018.06.08T12:30:01 2018.06.08T12:30:01 2018.06.08T12:30:01,65.8 45.2 78.6,5800 8700 4600);
t;

sym  datetime            price qty 
---- ------------------- ----- ----
APPL 2018.06.08T12:30:01 65.8  5800
IBM  2018.06.08T12:30:01 45.2  8700
GOOG 2018.06.08T12:30:01 78.6  4600

Como você pode ver, o número de registros na tabela não aumentou, mas os registros correspondentes à chave primária foram atualizados.

Continue a inserir um lote de novos registros na tabela, o próprio novo registro contém o valor de chave primária duplicado MSFT.

Como você pode ver, há apenas um registro cujo valor da chave primária é MSFT na tabela.

  • Cenário de aplicação

(1) A tabela de valor-chave tem eficiência muito alta para atualização e consulta de uma única linha e é uma escolha ideal para armazenamento em cache de dados. Em comparação com o redis, a tabela de memória de valor-chave no DolphinDB é compatível com todas as operações de SQL e pode concluir cálculos mais complexos, exceto atualizações e consultas de valor-chave.

(2) Como a tabela de saída do mecanismo de agregação de série temporal, os resultados da tabela de saída são atualizados em tempo real. Para mais detalhes, consulte o tutorial Usando DolphinDB para calcular K-line .

 


1.3 Tabela de dados de fluxo

A tabela de dados de streaming, como o próprio nome indica, é uma tabela de memória projetada para dados de streaming e um meio para publicação e assinatura de dados de streaming. A tabela de dados de fluxo tem uma dualidade de tabela de fluxo natural. Publicar uma mensagem é equivalente a inserir um registro na tabela de dados de fluxo e assinar uma mensagem é equivalente a enviar os dados recém-chegados na tabela de dados de fluxo para o aplicativo cliente. A consulta e o cálculo dos dados de streaming podem ser feitos por meio de instruções SQL.

  • crio

Use a função streamTable para criar uma tabela de dados de fluxo. O uso de streamTable é exatamente igual à função da tabela.

//创建空的流数据表
t=streamTable(1:0,`sym`id`val,[SYMBOL,INT,INT])

//使用向量创建流数据表
sym=`A`B`C`D`E
id=5 4 3 2 1
val=52 64 25 48 71
t=streamTable(sym,id,val)

Também podemos usar a função streamTable para converter uma tabela de memória convencional em uma tabela de fluxo de dados. Por exemplo:

sym=`A`B`C`D`E
id=5 4 3 2 1
val=52 64 25 48 71
tmp=table(sym, id, val)
t=streamTable(tmp)

A tabela de dados de fluxo também suporta a criação de uma única coluna de valor-chave, que pode ser criada pela função keyedStreamTable. Mas, diferente do propósito de design da tabela codificada, o propósito da tabela keyedstream é evitar mensagens duplicadas em cenários de alta disponibilidade (vários editores gravam ao mesmo tempo). Normalmente, a chave é o ID da mensagem.

  • Características de operação de dados

Como os dados do fluxo têm a característica de não mudar depois de gerados, a tabela de dados do fluxo não oferece suporte à atualização e exclusão de registros, mas apenas à consulta e adição de registros. O streaming de dados geralmente é contínuo, mas a memória é limitada. Para resolver essa contradição, a tabela de dados de streaming apresenta um mecanismo de persistência para manter a parte mais recente dos dados na memória, e os dados mais antigos são persistidos no disco. Quando o usuário assina os dados antigos, eles são lidos diretamente do disco. Para habilitar a persistência, use a função enableTableShareAndPersistence, consulte o tutorial de streaming de dados para obter detalhes .

  • Cenário de aplicação

A tabela de dados de streaming compartilhada publica dados na computação de streaming. O assinante usa a função subscribeTable para assinar e consumir dados de streaming.

 

1.4 Tabela de memória MVCC

A tabela de memória MVCC armazena várias versões de dados.Quando vários usuários leem e gravam a tabela de memória MVCC ao mesmo tempo, eles não se bloqueiam. O isolamento de dados da tabela de memória MVCC adota o modelo de isolamento de instantâneo. O que o usuário lê são os dados que já existem antes de lê-los. Mesmo que os dados sejam modificados ou excluídos durante o processo de leitura, isso também afetará o usuário que é lendo antes. Sem efeito. Essa abordagem de várias versões pode oferecer suporte ao acesso de usuários simultâneos às tabelas de memória. Deve-se notar que a implementação atual da tabela de memória MVCC é relativamente simples, toda a tabela é bloqueada ao atualizar e excluir dados e a tecnologia copy-on-write é usada para copiar uma cópia dos dados, portanto a eficiência de as operações de exclusão e atualização de dados não são altas. Em versões subsequentes, implementaremos tabelas de memória MVCC em nível de linha.

  • crio

Use a função mvccTable para criar a tabela de memória MVCC. Por exemplo:

//创建空的流数据表
t=mvccTable(1:0,`sym`id`val,[SYMBOL,INT,INT])

//使用向量创建流数据表
sym=`A`B`C`D`E
id=5 4 3 2 1
val=52 64 25 48 71
t=mvccTable(sym,id,val)

Podemos manter os dados da tabela de memória MVCC no disco, basta especificar o diretório persistente e o nome da tabela ao criá-lo. Por exemplo,

t=mvccTable(1:0,`sym`id`val,[SYMBOL,INT,INT],"/home/user1/DolphinDB/mvcc","test")

Após a reinicialização do sistema, podemos carregar os dados do disco na memória por meio da função loadMvccTable.

loadMvccTable("/home/user1/DolphinDB/mvcc","test")

Também podemos usar a função mvccTable para converter tabelas de memória convencionais em tabelas de memória MVCC.

sym=`A`B`C`D`E
id=5 4 3 2 1
val=52 64 25 48 71
tmp=table(sym, id, val)
t=mvccTable(tmp)
  • Cenário de aplicação

A tabela de memória MVCC atual é adequada para cenários em que há mais leituras e menos gravações e a persistência é necessária. Por exemplo, um sistema de configuração dinâmica requer itens de configuração persistentes, e os itens de configuração não são alterados com frequência.Eles foram adicionados recentemente e principalmente pesquisados, o que é muito adequado para tabelas MVCC.

 

2. Tabela de memória compartilhada

A tabela de memória no DolphinDB é usada apenas na sessão que criou a tabela de memória por padrão.Ela não suporta operações simultâneas de vários usuários e sessões múltiplas e, claro, não é visível para outras sessões. Se quiser criar uma tabela de memória que possa ser usada por outros usuários para garantir a segurança de operações simultâneas multiusuário, você deve compartilhar a tabela de memória. Todos os 4 tipos de tabelas de memória podem ser compartilhados. No DolphinDB, usamos o comando share para compartilhar tabelas de memória.

t=table(1..10 as id,rand(100,10) as val)
share t as st
//或者share(t,`st)

O código acima compartilha a tabela t com a tabela st.

Use a função undef para excluir a tabela compartilhada.

undef(`st,SHARED)

2.1 Certifique-se de que está visível para todas as sessões

A tabela de memória só é visível na sessão atual, não em outras sessões. Após o compartilhamento, outras sessões podem acessar a tabela de memória acessando variáveis ​​compartilhadas. Por exemplo, compartilhamos a mesa t como a mesa st na sessão atual.

t=table(1..10 as id,rand(100,10) as val)
share t as st

Podemos acessar a variável st em outras sessões. Por exemplo, insira um dado na tabela compartilhada st.

insert into st values(11,200)
select * from st

id val
-- ---
1  1  
2  53 
3  13 
4  40 
5  61 
6  92 
7  36 
8  33 
9  46 
10 26 
11 200

Mudando para a sessão original, podemos descobrir que um registro também foi adicionado à tabela t.

select * from t

id val
-- ---
1  1  
2  53 
3  13 
4  40 
5  61 
6  92 
7  36 
8  33 
9  46 
10 26 
11 200

 

2.2 Garantir a segurança da rosca

No caso de multithreading, os dados na tabela de memória são facilmente destruídos. O compartilhamento fornece um mecanismo de proteção para garantir a segurança dos dados, mas também afeta o desempenho do sistema.

Tabelas de memória convencionais, tabelas de fluxo de dados e tabelas de memória MVCC suportam modelos de várias versões, permitindo várias leituras e uma gravação. Especificamente, ler e escrever não se bloqueiam, você pode ler enquanto escreve e pode escrever quando lê. Não há bloqueio ao ler dados, vários threads têm permissão para ler dados ao mesmo tempo e o isolamento de instantâneo é usado ao ler dados. Um bloqueio deve ser adicionado ao gravar dados, e apenas um thread tem permissão para modificar a tabela de memória. As operações de gravação incluem adição, exclusão ou atualização. Os registros de adição são sempre anexados ao final da tabela de memória, tanto o uso da memória quanto o uso da CPU são muito eficientes. As tabelas de memória convencionais e as tabelas de memória MVCC suportam atualizações e exclusões e usam tecnologia copy-on-write, o que significa que uma cópia dos dados é primeiro copiada (para formar uma nova versão) e depois excluída e modificada na nova versão. Pode-se ver que tanto o consumo de memória quanto de CPU nas operações de exclusão e atualização são relativamente altos. Quando as operações de exclusão e atualização são frequentes e as operações de leitura demoradas (a versão antiga não pode ser lançada rapidamente), é fácil causar exceções OOM.

A tabela de memória de valor-chave precisa manter um índice interno durante a gravação e obter dados com base no índice durante a leitura. Portanto, o compartilhamento da tabela de memória de valor-chave adota métodos diferentes e tanto a leitura quanto a gravação devem ser bloqueadas. Thread de gravação e thread de leitor, vários threads de gravação, vários threads de leitor são mutuamente exclusivos. Tente evitar consultas ou cálculos demorados para tabelas de memória de valor-chave, caso contrário, outros threads ficarão esperando por um longo tempo.

 

3. Tabela de partição de memória

Quando a quantidade de dados na tabela de memória é grande, podemos particionar a tabela de memória. Após o particionamento, uma grande tabela consiste em várias subtabelas (tablets). A grande tabela não usa bloqueios globais. Os bloqueios são gerenciados de forma independente por cada subtabela, o que pode aumentar muito a simultaneidade de leitura e gravação. DolphinDB suporta particionamento de valor, particionamento de intervalo, particionamento de hash e particionamento de lista para tabelas de memória.Ele não suporta particionamento combinado. No DolphinDB, usamos a função createPartitionedTable para criar uma tabela de partição de memória.

  • Criar tabela de memória convencional de partição
t=table(1:0,`id`val,[INT,INT]) 
db=database("",RANGE,0 101 201 301) 
pt=db.createPartitionedTable(t,`pt,`id)
  • Criar tabela de memória de chave de partição
kt=keyedTable(1:0,`id`val,[INT,INT]) 
db=database("",RANGE,0 101 201 301) 
pkt=db.createPartitionedTable(t,`pkt,`id)
  • Crie uma tabela de dados de fluxo particionado

Ao criar uma tabela de dados de fluxo particionada, você precisa passar várias tabelas de dados de fluxo como modelos e cada tabela de dados de fluxo corresponde a uma partição. Ao gravar dados, grave diretamente nessas tabelas de fluxo; ao consultar dados, você precisa consultar a tabela de partição.

st1=streamTable(1:0,`id`val,[INT,INT]) 
st2=streamTable(1:0,`id`val,[INT,INT]) 
st3=streamTable(1:0,`id`val,[INT,INT]) 
db=database("",RANGE,1 101 201 301) pst=db.createPartitionedTable([st1,st2,st3],`pst,`id)  
st1.append!(table(1..100 as id,rand(100,100) as val)) 
st2.append!(table(101..200 as id,rand(100,100) as val)) 
st3.append!(table(201..300 as id,rand(100,100) as val))  
select * from pst
  • Criar tabela de memória de partição MVCC

Assim como criar uma tabela de dados de fluxo particionado, para criar uma tabela de memória MVCC particionada, você precisa passar várias tabelas de memória MVCC como modelos. Cada tabela corresponde a uma partição. Ao gravar dados, grave diretamente nessas tabelas; ao consultar dados, você precisa consultar a tabela de partição.

mt1=mvccTable(1:0,`id`val,[INT,INT])
mt2=mvccTable(1:0,`id`val,[INT,INT])
mt3=mvccTable(1:0,`id`val,[INT,INT])
db=database("",RANGE,1 101 201 301)
pmt=db.createPartitionedTable([mt1,mt2,mt3],`pst,`id)

mt1.append!(table(1..100 as id,rand(100,100) as val))
mt2.append!(table(101..200 as id,rand(100,100) as val))
mt3.append!(table(201..300 as id,rand(100,100) as val))

select * from pmt

Como a tabela de partição de memória não usa bloqueios globais, as subtabelas não podem ser adicionadas ou excluídas dinamicamente após a criação.


3.1 Aumentar a simultaneidade de consultas

Existem três significados para aumentar a simultaneidade da consulta por tabela de partição: (1) A tabela de valor-chave também precisa ser bloqueada durante a consulta. A tabela de partição é gerenciada de forma independente pela tabela filho, o que equivale a restringir a granularidade do bloquear, para que possa aumentar a simultaneidade de leitura.; (2) A tabela de partição pode processar cada subtabela em paralelo durante o cálculo em lote; (3) Se o campo de partição for especificado no filtro da consulta SQL, o intervalo de partição pode ser reduzido para evitar a varredura completa da tabela.

Tomando a tabela de memória de valor-chave como exemplo, comparamos o desempenho de consultas simultâneas com e sem particionamento. Primeiro, crie um conjunto de dados de simulação, que contém um total de 5 milhões de linhas de dados.

n=5000000
id=shuffle(1..n)
qty=rand(1000,n)
price=rand(1000.0,n)
kt=keyedTable(`id,id,qty,price)
share kt as skt

id_range=cutPoints(1..n,20)
db=database("",RANGE,id_range)
pkt=db.createPartitionedTable(kt,`pkt,`id).append!(kt)
share pkt as spkt

Simulamos 10 clientes em outro servidor para consultar simultaneamente a tabela de memória de valor-chave. Cada cliente consulta 100.000 vezes e cada vez que um dado é consultado, o tempo total consumido para cada consulta do cliente 100.000 vezes é calculado.

def queryKeyedTable(tableName,id){
	for(i in id){
		select * from objByName(tableName) where id=i
	}
}
conn=xdb("192.168.1.135",18102,"admin","123456")
n=5000000

jobid1=array(STRING,0)
for(i in 1..10){
	rid=rand(1..n,100000)
	s=conn(submitJob,"evalQueryUnPartitionTimer"+string(i),"",evalTimer,queryKeyedTable{`skt,rid})
	jobid1.append!(s)
}
time1=array(DOUBLE,0)
for(j in jobid1){
	time1.append!(conn(getJobReturn,j,true))
}

jobid2=array(STRING,0)
for(i in 1..10){
	rid=rand(1..n,100000)
	s=conn(submitJob,"evalQueryPartitionTimer"+string(i),"",evalTimer,queryKeyedTable{`spkt,rid})
	jobid2.append!(s)
}
time2=array(DOUBLE,0)
for(j in jobid2){
	time2.append!(conn(getJobReturn,j,true))
}

time1 é o tempo gasto para 10 clientes consultarem a tabela de memória chave não particionada e time2 é o tempo gasto para 10 clientes consultarem a tabela de memória chave particionada, em milissegundos.

time1
[6719.266848,7160.349678,7271.465094,7346.452625,7371.821485,7363.87979,7357.024299,7332.747157,7298.920972,7255.876976]

time2
[2382.154581,2456.586709,2560.380315,2577.602019,2599.724927,2611.944367,2590.131679,2587.706832,2564.305815,2498.027042]

Pode ser visto que o tempo consumido por cada cliente para consultar a tabela de memória de valor de chave particionada é menor do que para consultar a tabela de memória não particionada.

Consulte tabelas de memória não particionadas para garantir o isolamento de instantâneo. Mas consultar uma tabela de partição de memória não garante mais o isolamento do instantâneo. Como mencionado acima, a leitura e gravação da tabela de memória particionada não usa um bloqueio global. Quando um thread está fazendo uma consulta, outro thread pode estar gravando e envolvendo várias subtabelas, para que parte dos dados gravados possam ser lidos.


3.2 Aumentar a simultaneidade de escrita

Tomando a tabela de memória convencional particionada como exemplo, podemos gravar dados em diferentes partições ao mesmo tempo.

t=table(1:0,`id`val,[INT,INT])
db=database("",RANGE,1 101 201 301)
pt=db.createPartitionedTable(t,`pt,`id)

def writeData(mutable t,id,batchSize,n){
	for(i in 1..n){
		idv=take(id,batchSize)
		valv=rand(100,batchSize)
		tmp=table(idv,valv)
		t.append!(tmp)
	}
}

job1=submitJob("write1","",writeData,pt,1..100,1000,1000)
job2=submitJob("write2","",writeData,pt,101..200,1000,1000)
job3=submitJob("write3","",writeData,pt,201..300,1000,1000)

No código acima, existem 3 threads gravando simultaneamente em 3 partições diferentes de pt. Deve-se observar que devemos evitar gravar na mesma partição ao mesmo tempo. Por exemplo, o código a seguir pode causar o travamento do sistema.

job1=submitJob("write1","",writeData,pt,1..300,1000,1000)
job2=submitJob("write2","",writeData,pt,1..300,1000,1000)

O código acima define dois threads de gravação e gravação na mesma partição, o que destruirá a memória. A fim de garantir a segurança e consistência dos dados em cada partição, podemos compartilhar a tabela de memória da partição. Desta forma, vários threads podem ser definidos para se dividirem na mesma partição ao mesmo tempo.

share pt as spt
job1=submitJob("write1","",writeData,spt,1..300,1000,1000)
job2=submitJob("write2","",writeData,spt,1..300,1000,1000)


4. Comparação de operação de dados


4.1 Adicionar, excluir, modificar

A tabela a seguir resume as operações de adição, exclusão, modificação e verificação suportadas pelos 4 tipos de tabelas de memória no caso de compartilhada / particionada.

Descrição:

  • As tabelas de memória convencionais, as tabelas de memória de valor-chave e as tabelas de memória MVCC oferecem suporte a operações de adição, exclusão, modificação e consulta. As tabelas de dados de fluxo oferecem suporte apenas à adição e consulta de dados, mas não à exclusão e atualização de operações.
  • Para tabelas de memória de valor-chave, se a chave primária for incluída na condição de filtro de consulta, o desempenho da consulta será significativamente melhorado.
  • Para tabelas de memória particionada, se as condições do filtro de consulta incluírem colunas de partição, o sistema pode restringir o intervalo de partições a serem verificadas, melhorando assim o desempenho da consulta.


4.2 Simultaneidade

Sem escrever, todas as tabelas de memória permitem que vários threads consultem ao mesmo tempo. No caso da escrita, a simultaneidade das quatro tabelas de memória é diferente. A tabela a seguir resume as condições simultâneas de leitura e gravação suportadas pelos 4 tipos de tabelas de memória no caso compartilhado / particionado.

Descrição:

  • A tabela compartilhada permite leituras e gravações simultâneas.
  • Para tabelas de partição que não são compartilhadas, vários threads não têm permissão para gravar na mesma partição ao mesmo tempo.


4.3 Resistência

  • As tabelas de memória convencionais e as tabelas de memória de valor-chave não oferecem suporte à persistência de dados. Assim que o nó for reiniciado, todos os dados na memória serão perdidos.
  • Apenas tabelas de dados de streaming vazias suportam persistência de dados. Para manter a tabela de dados de fluxo, primeiro configure o diretório persistenceDir para os dados de fluxo e, em seguida, use enableTableShareAndPersistence para compartilhar a tabela de dados de fluxo e persisti-la no disco. Por exemplo, a tabela de dados de streaming t é compartilhada e mantida no disco.
t=streamTable(1:0,`id`val,[INT,INT])
enableTableShareAndPersistence(t,`st)

Depois que a tabela de dados de fluxo for habilitada para persistência, alguns dos registros mais recentes na tabela de dados de fluxo ainda serão mantidos na memória. Por padrão, a memória reterá os últimos 100.000 registros. Também podemos ajustar esse valor conforme necessário.

A persistência da tabela de dados de fluxo pode ser configurada para adotar métodos assíncronos / síncronos, compactados / descompactados. Normalmente, o modo assíncrono pode atingir um rendimento mais alto.

Depois que o sistema for reiniciado, execute a função enableTableShareAndPersistence novamente para carregar todos os dados do disco na memória.

  • A tabela de memória MVCC suporta persistência. Ao criar a tabela de memória MVCC, podemos especificar o caminho de persistência. Por exemplo, crie uma tabela de memória MVCC persistente.
t=mvccTable(1:0,`id`val,[INT,INT],"/home/user/DolphinDB/mvccTable")
t.append!(table(1..10 as id,rand(100,10) as val))

Após a reinicialização do sistema, podemos usar a função loadMvccTable para carregar os dados do disco para a memória. Por exemplo:

t=loadMvccTable("/home/user/DolphinDB/mvccTable","t")

5. Comparação da operação da estrutura da tabela

As operações de estrutura da tabela de memória incluem adicionar colunas, deletar colunas, modificar colunas (conteúdo e tipo de dados) e ajustar a ordem das colunas. A tabela a seguir resume as operações de estrutura suportadas pelos 4 tipos de tabelas de memória no caso de compartilhada / particionada.

Descrição:

  • Tabelas de partição e tabelas de memória MVCC não podem adicionar colunas por meio da função addColumn.
  • A tabela de partição pode adicionar colunas por meio da instrução de atualização, mas a tabela de dados de fluxo não pode ser modificada, portanto, a tabela de dados de fluxo não pode adicionar colunas por meio da instrução de atualização.


6. Resumo

O DolphinDB suporta 4 tipos de tabelas de memória e também introduz o conceito de compartilhamento e particionamento, que pode basicamente atender às várias necessidades de computação de memória e computação de fluxo.

Acho que você gosta

Origin blog.csdn.net/qq_41996852/article/details/112858160
Recomendado
Clasificación