introdução de armazenamento
O armazenamento de objetos é amplamente utilizado em projetos atuais. É usado principalmente para armazenar recursos estáticos, como fotos, vídeos, áudio e arquivos. Basicamente, todos os fornecedores de serviços em nuvem têm armazenamento de objetos. O armazenamento de objetos geralmente é cobrado por GB por mês, como 0,098 yuan/GB/mês de Niu, 0,12 yuan/GB/mês de Ali. Por exemplo, se eu usei 30 GB no mês passado, o custo do mês passado é 30 * 0,098. Deve-se observar aqui que os 30 G usados no mês passado não significam que haverá dados de 30 G no balde no final do mês passado, mas refere-se à última média A dosagem diária é de 30G. Por exemplo, Xiao Ming carregou arquivos 1G todas as manhãs no mês passado, então o uso no mês passado foi (1+2+3+...+30)/30=15.5G, e isso leva a um novo problema. Arquivos 1G todas as manhãs, Outro arquivo 1G foi excluído à tarde, então qual foi o uso de armazenamento no mês passado? Não deve ser 0, senão não é prostituição? Para evitar que os usuários se prostituam, você pode definir o uso diário como o espaço máximo de uso do balde naquele dia, então o Xiaoming carrega 1G pela manhã e exclui 1G à tarde. O espaço máximo de armazenamento para o dia é 1G, e o uso do mês é (1+1+1...+1 )/30=1G. Se você deseja calcular com precisão o espaço máximo do dia, precisa contar o uso atual quando cada arquivo é adicionado e excluído e, em seguida, obter o valor máximo em um dia. Se os requisitos não forem altos, você também pode contar o uso em intervalos. Aqui, apresento o uso de elasticsearch para contar o uso diário de armazenamento.
Processo Básico Estatístico
Conte o uso de armazenamento atual e armazene-o no ES a cada 30 minutos. Os campos principais são os seguintes:
ID do inquilino | Tempo de estatísticas | tamanho |
---|---|---|
1 | 2023-07-10 00:00:00 | 1024 |
1 | 10/07/2023 00:00:30 | 2024 |
1 | 2023-07-10 00:00:00 | 1024 |
Criar índice ES
PUT /bucket_size
{
"settings": {
"number_of_shards": 6,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"size": {
"type": "long"
},
"tenantId": {
"type": "long"
},
"time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
Dados de teste
{
"id": "1",
"tenantId": 1,
"size": 1024,
"time": "2023-07-17 18:00:00"
}
{
"id": "2",
"tenantId": 1,
"size": 2048,
"time": "2023-07-17 19:00:00"
}
{
"id": "3",
"tenantId": 1,
"size": 1024,
"time": "2023-07-17 10:00:00"
}
{
"id": "4",
"tenantId": 2,
"size": 1024,
"time": "2023-07-17 09:00:00"
}
{
"id": "5",
"tenantId": 2,
"size": 0,
"time": "2023-07-17 10:00:00"
}
{
"id": "6",
"tenantId": 2,
"size": 1024,
"time": "2023-07-17 11:11:00"
}
Consultar o uso diário do inquilino
Consulte os requisitos, insira o ID do locatário, hora de início e hora de término e retorne o uso diário de cada locatário dentro do horário especificado.
GET /bucket_size/_search
{
"query": {
"bool": {
"must": [
{
"terms": {
"tenantId": [
1,
2
],
"boost": 1
}
},
{
"range": {
"time": {
"from": "2023-07-01",
"to": "2023-07-31",
"include_lower": true,
"include_upper": true,
"boost": 1
}
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
},
"aggregations": {
"tenantGroup": {
"terms": {
"field": "tenantId",
"size": 10,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_count": "desc"
},
{
"_key": "asc"
}
]
},
"aggregations": {
"groupDay": {
"date_histogram": {
"field": "time",
"format": "yyyy-MM-dd",
"calendar_interval": "1d",
"offset": 0,
"order": {
"_key": "asc"
},
"keyed": false,
"extended_bounds" : {
"min" : "2023-07-01",
"max" : "2023-07-31"
}
},
"aggregations": {
"maxSize": {
"max": {
"field": "size",
"missing": 0
}
}
}
}
}
}
}
}
resultado
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 6,
"successful": 6,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 6,
"relation": "eq"
},
"max_score": 2,
"hits": [
{
"_index": "bucket_size",
"_type": "_doc",
"_id": "2",
"_score": 2,
"_source": {
"id": "2",
"tenantId": 1,
"size": 2048,
"time": "2023-07-17 19:00:00"
}
}
]
},
"aggregations": {
"tenantGroup": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 1,
"doc_count": 3,
"groupDay": {
"buckets": [
{
"key_as_string": "2023-07-01",
"key": 1688169600000,
"doc_count": 0,
"maxSize": {
"value": null
}
},
{
"key_as_string": "2023-07-02",
"key": 1688256000000,
"doc_count": 0,
"maxSize": {
"value": null
}
}
]
}
},
{
"key": 2,
"doc_count": 3,
"groupDay": {
"buckets": [
{
"key_as_string": "2023-07-31",
"key": 1690761600000,
"doc_count": 0,
"maxSize": {
"value": null
}
}
]
}
}
]
}
}
}
Implementação usando código JAVA
public Map<Long, Map<String, Long>> getTenantSize(Long[] tenantIds, String mouthStartDate, String mouthEndDate) throws IOException {
Map<Long, Map<String, Long>> map = new TreeMap<>();
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
queryBuilder.must(QueryBuilders.termsQuery("tenantId", Arrays.asList(tenantIds)));
queryBuilder.must(QueryBuilders.rangeQuery("time").gte(mouthStartDate).lte(mouthEndDate));
AggregationBuilder tenantGroup = AggregationBuilders.terms("tenantGroup").field("tenantId")
.subAggregation(AggregationBuilders.dateHistogram("groupDay").field("time").calendarInterval(DateHistogramInterval.DAY)
.format(DatePattern.NORM_DATE_PATTERN).order(BucketOrder.key(true)).extendedBounds(new LongBounds(mouthStartDate,mouthEndDate))
.subAggregation(AggregationBuilders.max("maxSize").field("size"))
);
Aggregations aggregations = esClient.search(queryBuilder, tenantGroup, "bucket_size");
Map<String, Aggregation> tenantGroupMap = aggregations.asMap();
if (MapUtil.isNotEmpty(tenantGroupMap)) {
tenantGroupMap.forEach((k, v) -> {
Terms terms = (Terms) v;
List<? extends Terms.Bucket> buckets = terms.getBuckets();
if (CollUtil.isNotEmpty(buckets)) {
buckets.forEach(bucket -> {
Map<String, Long> daySizeMap = new TreeMap<>();
Map<String, Aggregation> dayGroup = bucket.getAggregations().asMap();
if (MapUtil.isNotEmpty(dayGroup)) {
dayGroup.forEach((key, value) -> {
ParsedDateHistogram daySizeTerms = (ParsedDateHistogram) value;
List<? extends Histogram.Bucket> daySizeBucket = daySizeTerms.getBuckets();
if (CollUtil.isNotEmpty(daySizeBucket)) {
daySizeBucket.forEach(daySize -> {
ParsedMax maxSize = daySize.getAggregations().get("maxSize");
Long size=maxSize.getValue()!=Double.NEGATIVE_INFINITY? Double.valueOf(maxSize.getValue()).longValue():0L;
daySizeMap.put(daySize.getKeyAsString(),size);
});
}
});
}
map.put(Long.valueOf(bucket.getKeyAsString()), daySizeMap);
});
}
});
}
return map;
}
Resumir
Este artigo apresenta principalmente o uso do armazenamento de computação elasticsearch para aprender a usar a consulta do grupo elasticsearch e usar o código JAVA para chamar a consulta do grupo elasticsearch. Existem as seguintes precauções:
- Se você consultar de 1º a 30 de julho, se não houver dados do dia no ES, também será retornado. Date_histogram é usado aqui, e extended_bounds é forçado a retornar null
- Depois que os resultados da consulta são agrupados, eles devem ser classificados por tempo
- De acordo com as agregações diárias, use max para obter o maior tamanho do dia como o uso de armazenamento do dia
- A consulta do grupo Elasticsearch consome mais memória. Ela foi agrupada em três camadas. O tempo e o número de inquilinos não devem ser muito, caso contrário, causará OOM
- No caso, o armazenamento é contabilizado a cada 30 minutos.Se fizer upload e deletar em 30 minutos, será prostituído.