大数据存储方案-每日快照数据优化(1)

拉链表实现、es、tendis

Posted by TuoX on November 8, 2021

业务场景:

1.N个关键词的搜索结果:app列表 例子: 搜索词:tuox 结果: ['app1','app2','app3'] 
2.现更新机制:每天关键词未必会推送全量,需要对不变的数据,进行继承
3.求: 1).每个app的全部、top3的关键词覆盖数(搜索结果存在该app,即为覆盖)2).每个app的覆盖列表的展示(排名变动则更新)

实现这种场景:

一般想到的就是每天一张表,第二天凌晨进行数据的快速备份,接着进行数据的更新

备份有很多种方式:

                1.程序脚本手动更新
                2.binlog实现(使用最多的方式)

但是备份会带来很多的问题: 1.备份的不稳定性 2.备份到成功之间的时差所产生的数据更新写入的失败 3.极其庞大的存储空间

方案初探:

那有没有一个完美的方案,答案是有的,那就是拉链表

什么是拉链表

    维护历史状态,以及最新状态数据的一种表,拉链表根据拉链粒度的不同,实际上相当于快照,只不过做了优化,去除了一部分不变的记录,通过拉链表可以很方便的还原出拉链时点的客户记录。

拉链表的场景

    1.数据量比较大。
    2.表中的部分字段会被更新,比如用户的地址,银行利率,订单的状态等。
    3.需要查看某一个时间点或者时间段的历史快照信息,比如,查看利率在历史某一个时间点的状态。
    4.变化的比例和频率不是很大,比如,总共有1000万的会员,每天新增和发生变化的有10万左右。
    5.如果对这边表每天都保留一份全量,那么每次全量中会保存很多不变的信息,对存储是极大的浪费;

拉链历史表,既能满足反应数据的历史状态,又可以最大程度的节省存储。

方案思路:

通过拉链表解决了历史记录备份的问题,但是我们还有面临的挑战:1.更新量大的问题 2.需要对结果的排名进行排序 3.需要对历史记录进行聚合汇总结果

技术选型:es: 存储历史记录 tendis: 采用string类型,进行每个关键词对应的每个app最新的记录

es存储的记录为: _id: '%s_%s_%s' % (word_id,app_id,start_date)   _doc: word_id、app_id、rank、start_date、end_date

tendis最新记录: key: '%s_%s' % (word_id,app_id) value: rank、start_date、end_date

很多同学肯定会认为:为什么要用tendis,目标是什么?

其实目标很简单,就是通过kv这种nosql的性能库来解决(redis内存太贵,数据量非常大),频繁在es中搜索当前最新的一条数据的过程,加快程序的计算,这样子es只会进行处理更新与插入的数据以及页面的简单查询,减少压力

接下来让我们看一下方案的逻辑图

图片

部分伪代码:

1.创建mapping

mapping = {
    'properties': {
        'app_id': {
            'type': 'integer'
        },
        'word_id': {
            'type': 'integer'
        },
        'rank': {
            'type': 'integer'
        },
        'start_date': {
            'type': 'date'
        },
        'end_date': {
            'type': 'date'
        }
    }
}

es_index = 'search_result'

2.查询语句

{
"query": {
    "bool": {
    "must": [
        {
        "range": {
            "start_date": {
            "lte": "2021-09-02T00:00:00"
            }
        }
        },
        {
        "range": {
            "end_date": {
            "gte": "2021-09-02T00:00:00"
            }
        }
        },
        {
        "term": {
            "app_id": 1
        }
        }
    ]
    }
},
"size": 10000,
"_source": [
    "word_id",
    "rank"
],
"track_total_hits": true
}

3.聚合语句

{
"query": {
    "bool": {
    "must": [
        {
        "range": {
            "start_date": {
            "lte": "2021-09-02T00:00:00"
            }
        }
        },
        {
        "range": {
            "end_date": {
            "gte": "2021-09-02T00:00:00"
            }
        }
        },
        {
        "terms": {
            "app_id": [
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10,
            11,
            12,
            13,
            14,
            15,
            16,
            17,
            18,
            19,
            10000
            ]
        }
        }
    ]
    }
},
"size": 0,
"aggs": {
    "apps": {
    "terms": {
        "field": "app_id",
        "collect_mode": "breadth_first",
        "execution_hint": "map",
        "size": 20
    },
    "aggs": {
        "count_top3": {
        "filter": {
            "range": {
            "rank": {
                "lte": 3
            }
            }
        }
        }
    }
    }
}
}

优化思路

我们通过es创建拉链表、tendis进行最新记录的判断、可以极大的提高我们消费的过程,但是es当中随着数据量越来越大也会存在查询慢的问题。

我们可以把es分为冷热数据:热数据:最近两个月的数据 冷数据:历史数据(按月切割)

我们下一篇文档会讲到如何进行冷热数据迁移的自动化操作