数据仓库知识点

简介

诞生背景

主要为了满足两个需要,一是历史数据积存,二是企业数据分析需要

首先是历史数据积存,线上业务系统随着企业的运行会源源不断地产生数据,这些数据就会被存储在业务数据库中,例如mysql,oracle等。但是随着线上的业务系统运行一定时间之后,积压的数据就会越来越多,这就会对业务数据库产生一定的负载,导致运行速度缓慢,而这里堆积的相当一部分数据属于冷数据,即较早的调动相对不频繁的数据,为了避免冷数据对数据库产生的影响,妨碍数据库运行,这时就需要企业定期将冷数据从业务数据库中转移,存储到专门存放历史数据的仓库中,这个仓库就称之为数据仓库

其次就是企业数据分析的需要,各个部门如果自己建立独立的数据抽取系就会导致数据不一致。例如各个部门如果直接从业务数据库抽取数据,部门A从早上九点抽取数据,而部门B从下午两点抽取,每个部门都需要给个数据库权限,数据库的权限管理有很大的风险。为了解决这个问题,企业统一建立一个数据仓库,使用专门的数据抽取系统,定期从业务数据库把数据抽取到数据仓库中。这样比如,数据仓库每天凌晨抽取数据,那么各个部门用到数据的时候是一致的,都是前一天的。再者,数据仓库可以直接开放接口,这样业务数据库和数据仓库的权限管理更具有针对性,数据仓库面向数据分析,业务数据库面向业务系统,各司其职。

什么是数据仓库

数据仓库是一个面向主题的(Subject Oriented)、集成的(Integrate)、相对稳定的(Non-Volatile)、随时间变化(Time Variant)的数据集合,它用于支持企业或组织的决策分析处理。

面向主题的:数据集合是以主题为单位进行数据汇聚,一个主题内只存储与本主题有关系的数据。

集成的:数据来源多种多样,需要ETL操作。

相对稳定的:一般只进行写入与查询操作,不进行更新与删除。

反映历史变化:关键数据隐式或显式的基于时间变化

这里可能就纳闷了,数据仓库是不让修改的,那么怎么去改变呢?
可以把最新的数据追加到系统中,然后以时间戳标记版本,修改后的数据所在的时间戳最新,之前的旧数据因为时间戳是旧的,所以在查询的时候不去查询,当然也可以定期删除旧的

传统数据库vs大数据仓库

传统数据库-单机数据库集成

传统数据库是单个关系型数据库组成MPP(大规模并行处理)集群,说人话就是多个单机数据库集群产生。

优点:由于是由关系型数据库改造,所以完全兼任原有的SQL语法
缺点:

  1. 扩展性有限,MPP集群是由一个节点把数据分发到各个节点,其实每个节点本质上还是一个数据库,他可以独立进行计算,但是涉及到数据交换,就得通过高速网络与其他节点连接,这样直接限制了 节点上限。其次,运用到了分库分表,即一张很大的表拆分成几个部分,存储到不同节点上,分库分表也有上限,因为分库分表越细,数据库处理数据时性能也越差

  2. 热点问题:还是分库分表,当一张比如说100万行的大表被拆分成多分,交由不同数据库节点存储的时候,可能会出现其中最 被频繁使用的的数据都在同一个节点中,这就导致这个节点存在的压力特别大。当然,可以用数据加盐的方式解决,即给表中的数据增加前缀,把他打乱随机分布到各个表中

大数据仓库-分布式

有极强的扩展性

大数据诞生初期,易用性差,因为大数据数据处理时有自己的数据引擎,有自己的语法,但企业的数据一般是用Sql处理的,

大数据仓库的架构是分布式文件系统,把数据库中的结构化数据看成是文件,把这些文件自动拆分成默认是按照128M拆分,拆分完后分发到各个节点,不用考虑传统的很精细的分库分表。

大数据仓库在上层处理的时候,采用元数据,把这些提取的文件还原为表结构

正是这种粗犷的方式解决了延展性,再者也解决了热点问题,因为将数据存储到节点的时候,会备份几份,比如说存在节点1,2,3都存了,那么到时候我们要用到这个数据的时候,分布式架构的好处就在于提取数据的时候可以在1,2,3中选择一个最空闲的节点,这样就解决了热点问题。

  • 缺点:1. sql支持率问题 2.缺少事务支持,对事务支持不全 3. 数据量较小时,计算较慢。在数据量没有达到一定规模的时候,光是任务的拆分,分配,调度,合并,整个过程就会花费大量的时间。

MPP和分布式批数据架构(Hadoop架构)

MPP架构

传统数仓中用的结构,就是将单机数据库节点组成集群,从而提升整体的性能

其中每个节点都是独立计算的,有自己的独立存储和内存系统,是非共享架构。每个节点通过专业网络相连,协同计算。

设计上优先考虑C(一致性)、A(可用性)、P(分区容错性)

  • 缺点:

存储位置不透明,通过Hash(哈希)确定数据所在节点,查询任务会在所有节点均被执行

并行计算的时候,单节点瓶颈会成为整个系统短板,容错性较差。在整个节点协同计算的过程中,当一个节点运行缓慢的时候,其他节点势必等待。

分布式批数据架构

MPP不共享数据,而分布式批数据架构在集群中共享

特点:

  1. 可以单独进行局部应用
  2. 每个节点共享数据
  3. 同时又因为共享数据,所以扩展性极强

分布式批数据架构每个节点有计算和存储资源,这俩是分开的,并且每个节点的存储资源拿出来,共同组成了一个分布式的公共数据存储系统。各个节点存储资源被拿走后,剩下的就是计算资源,当任务分配到每个节点,每个节点在运算的时候,就可以共享整个公共数据存储系统,因此可以单独进行局部应用,同时也可以共享数据

  • 节点间不是通过高速网络交互,每个节点是通过局域网或者广域网相连,因此致力于减少数据移动
  • 优先考虑P(分区容错性)、A(可用性)、C(一致性)
    数据存储的时候是多个备份存储到节点,比如说保存三份,分别存储到1,2,3节点,就保证了容错性

MPP+分布式架构

数据存储用分布式架构的公告存储,提高分区容错性

上层架构采用MPP,减少运算延迟

常见的数仓产品

  • 传统型数仓
    Oracle RAC
    DB2
    Teradata
    Greenplum

  • 分布式

Hive

Hadoop分布式架构,原理:将sql转换成大数据的计算引擎MapReduce进行运算,也支持转换为spark

Spark SQL

Spark生态圈的数仓产品,诞生原因是MapReduce运行太慢了

HBase
Impala
HAWQ
TIDB

架构

基本架构(以阿里为例):ETL->ODS->CDM->ADS

在这里插入图片描述

  • ETL
    E:extract抽取,T:trasnform转换,L:load加载
    抽取原始数据,然后进行转换,比如说清洗去重等,然后加载到目的端的过程

  • ODS(操作数据层)
    这一层不做任何的修改, 目的是存储原始数据

  • CDM(公共维度模型层)
    整个层主要是为数据分析提供服务,主要分DWD(数据明细层)和DWS(数据汇总层)。
    DWD(数据明细层):处理ODS的数据,规范化
    DWS(数据汇总层):将各个表汇总成一张表(宽表),满足三范式,在对宽表进行数据建模等,模型设计

  • ADS(数据应用层)
    存储分析结果,为不同业务提供接口,减少数仓压力
    如果直接开放前面的CMD层,这层是进行数据分析的,直接开放业务查询接口会增加负担,所以我们专门建了ADS层来存储结果,并且开放接口。

ETL

E:extract抽取,T:trasnform转换,L:load加载
抽取原始数据,然后进行转换,然后加载到目的端的过程

1. 数据抽取(Extraction)

  • 抽取的数据源:结构化、半结构化、非结构化
  • 抽取数据的方式:全量或增量(抽取全部数据,或者抽取变动数据)

Q. 什么是结构化、半结构化、非结构化数据

1、结构化数据

结构化数据,简单来说就是数据库,有一定的结构性,表现为二维形式的数据

2、非结构化数据

非结构化数据是数据结构不规则或不完整,没有预定义的数据模型,不方便用数据库二维逻辑表来表现的数据。包括所有格式的办公文档、文本、图片、XML, HTML、各类报表、图像和音频/视频信息等等。

3、半结构化数据

和普通纯文本相比,半结构化数据具有一定的结构性,可以用一些分隔元素对记录和字段,进行分层。常见的半结构数据有XML和JSON,对于对于两个XML文件,第一个可能有

Q. 结构化、半结构化、非结构化数据如何提取?

  1. 结构化数据采用JDBC、数据库日志等方式,JDBC对数据库进行直接连接
  2. 半结构化或者非结构化,可以监听文件是否发生了变动,将变动后的数据进行抽取

2. 数据转换(Transformation)

主要是数据清洗和转换两个阶段

  • 数据清洗

对重复、二义性、不完整、违反业务或逻辑规则等问题进行统一处理

  • 数据转换

数据标准化,字段数据类型等转换

3. 数据加载(Loading)

将处理完的数据导入到对应的目标源

常见的ETL工具

  • 结构化ETL工具:

SGoop
Kettle
Datasatge
Informatica
Kafka

  • 非结构化
    Flume
    Logstash

ODS(操作数据层)

这一层不做任何的修改, 目的是存储原始数据

如果一定要修改,就新增数据,然后给他更新的日期,并且将状态变为update,删除旧数据

  • ETL导入ODS的方法
    全量和增量
    增量导入:使用外连接&全覆盖的方法,把增量数据与原有的数据进行join全外连接(两表中一个有就返回),如果有新增的数据,就直接在内存中修改,然后把ODS层覆盖

CMD(公共维度模型层)

整个层主要是为数据分析提供服务,主要分DWD(数据明细层)和DWS(数据汇总层)。

DWD(数据明细层)

主要功能:数据格式规范化&维度降维

DWD(数据明细层):主要接受ODS层的数据,由于ODS的数据是不进行修改的,所以ODS层的数据可能来源于各个系统,并且格式不统一,所以我们DWD(数据明细层)要做的就是统一格式,如清洗、标准化、异常数据清洗,对数据做统一字段编码等。

还有可能就是维度降维,比如说公司有多个分布,在北京上海等地返回用户表,这些用户表字段都一样,但是一张张独立的表,我们可以把这些表增加一个字段叫做位置,然后把这些表就可以合成同一张表

满足三范式

DWS(数据汇总层)

DWS(数据汇总层):将各个表汇总成主题表,例如都是交易相关的就汇总成交易表,不满足三范式

ADS(数据应用层)

存储分析结果,为不同业务提供接口,减少数仓压力

如果直接开放前面的CMD层,这层是进行数据分析的,直接开放业务查询接口会增加负担,所以我们专门建了ADS层来存储结果,并且开放接口。

最佳实践

维度建模

三范式

1.第一范式(确保每列保持原子性)

如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。

第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。

2.第二范式(确保表中的每列都和主键相关)
第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键,如下表所示。

3.第三范式(确保每列都和主键列直接相关,而不是间接相关)

三范式建模和维度建模

在这里插入图片描述

  • 维度建模:根据不同维度建模,一般包含分类、时间、地域等
  • 维度模型一般分为星型、雪花、星座模型

星型模型是一张事实表+一张以上的维度表组成,最高效的星型是没有维度表,维度表包含在事实表中。

雪花模型是最低效的数仓模型,以为其维度表上还有维度表,多层维度。所以在聚合过程中会产生更多的关联计算,效率很低。

星座是多个星型模型放在一起,并且这些星形模型之间的维度有重叠部分,最主要的特征是有多张事实表,并且这些事实表之间有些维度表是重叠的。

范式建模是维度建模的一种,特点是数据冗余减少,但效率变低。雪花模型就是维度建模的简单版本。

数仓中的表

维度建模中的表类型

  • 事实表
    一般指的是一个现实中存在的业务对象,比如说电商系统的用户信息表等

  • 维度表
    业务状态、代码等的解释表,例如订单状态1表示未支付,2表示已支付…

  • 事务事实表
    一旦产生表结构就不会变化,只会追加新的数据,例如交易流水,操作日志、出库入库记录等

  • 周期快照事实表
    一个周期内的度量统计,例如年、季度累计,例如表结构是
    业务id,卖家id,年累计下的金额,年累计买家数,年累计支付金额…

  • 累计快照事实表
    记录不确定周期的度量统计,比如说你下单但是没支付,这就是一条记录,然后支付了但没收货,这条记录就更新掉之前的,直到完成这个过程。

  • 拉链表
    记录每条信息的生命周期,保留数据的所有历史状态。

  • 累计快照事实表的实现方式?

  • 实现方式一
    使用日期分区,全量数据记录。即每天的数据分别存储,然后每天存储的是昨天的全量数据和今天新增的数据。缺点就是冷数据太多

  • 实现方式二
    在前面的基础上改进,设定一个数据最长生命周期,如一个月,就是默认假设这一个月内数据是会更新的,删除一个月之前的。

  • 实现方式三
    使用日期分区,但是是以业务实体的结束时间分区。也就是说两张表,一张表存储的是已经结束的数据,另一张是存储未结束的,更新未结束的数据表

ETL策略

全量同步和增量同步

任务调度

mysql

mysql配置文件

二进制日志:log-bin主从复制
错误日志:log-error默认关闭,记录警告和错误等
查询日志:log默认关闭,记录查询的sql,开启会降低整体性能
数据文件:位置/var/lib/mysql

其中.frm:存放表结构 .myd:存放表数据 .myi:存放表索引

相关配置:windows是my.ini,linux是my.cnf

逻辑架构

连接层、服务层、引擎层、存储层

存储引擎

有两种,分别是myisam和innodb
myisam:表锁,只存放索引,不支持事务
innodb:行锁,缓存索引和真实数据,支持主外键和事务

索引

定义:排好序的快速查找的一种表结构

什么时候建立索引?

1.主键自动建立唯一索引
2.频繁作为查询条件的字段
3.查询与其他表关联的字段
4.查询中排序的字段
5.查询中统计或者分组的字段

什么时候不创建索引?

1.频繁更新的
2.数据重复且分布不均匀的
3.表记录太少
4.where条件里用不到的

如何避免索引失效?

1.最佳左前缀法则
2.不要在索引列上做任何操作
3.使用!= 或者 <> 导致索引失效
4.函数、or、模糊搜索、NOT IN、NOT EXISTS导致索引失效

单值索引和组合索引

组合索引:多个字段组合生成的索引
在高并发条件下建议使用组合索引

分区、分库、分表

分区

分区并不是生成新的数据表,而是将表的数据均衡分摊到不同的硬盘,系统或是不同服务器存储介子中,实际上还是一张表。

range分区和list分区,一个是按照范围划分,例如按照年龄段分区,一个是按照某一个字段的值划分

分表

就是把一张表按一定的规则分解成N个具有独立存储空间的实体表。系统读写时需要根据定义好的规则得到对应的字表明,然后操作它。

为什么要分库,因为生产环境中mysql用到的都是一个集群,我们在读写过程中为了减清主表的压力,会有很多个从表。但在高并发情况下,写入的请求打到一个库上,这会使得库的压力大,因此需要分库,使得可以在不同的库中写数据

分库

分为垂直和水平分表,优先进行垂直分表,就是按照业务逻辑划分为不同表,若数据量还是很大(单表容量超过500w),如用户表或者订单表,这就需要进行水平拆分。

行、表锁

查询锁:show open table;
读(写)锁:lock table 表名 read(write);

表锁(myisam)偏向于读,行锁(innodb)偏向于写

读锁会阻塞写,但不阻塞读,写锁会阻塞读和写

读锁示例:

session1加表1 的读锁
session2可读但是不可写(需排队等待)
session1不可改,不可读其他表

写锁示例:
session1加表1 的写锁
session1可以读写
session2不能读

sql优化

  • 一、基础优化

  • 少使用select *,尽量使用具体的字段

  • 少使用order by排序,对于需要多个字段排序的可以使用组合索引

  • 少使用like,对于需要使用的,尽量写like “abc%”,把%尽量放在字段后面

  • 对group by语句,要先过滤后分组

  • 查询时减少使用null,对于字段有null的可以添加默认值

  • 固定字段长度

  • 使用limit 1 (明确只有1行的时候),这样查到1行就返回了

  • 在where后面少使用函数或者算数运算

  • 不要超过5个以上的表连接

  • 小表驱动大表

  • 二、 建立使用合适的索引

  • 对于高频筛选字段可以建立适当的索引

  • 不要建立会发生变动的索引,比如date(),会使得索引失效

  • 一个表的索引最好不要超过5个,多了影响插入修改

  • 不要对值是有重复的字段建立索引,如性别等

  • 使用组合索引的时候要遵守最左原则

  • 三、进阶优化

  • 使用explain检测sql查询(explain select …)

  • 使用缓存优化查询(进行多次相同的查询,结果就会被放入缓存中,后续再进行同样的查询,就直接从缓存中提取,不会到表中提取)redis

  • 注意:当查询语句中有些不确定时,例如now(),current_date()等会出现缓存失效

explain

用法:explain+sql语句
返回:每个子语句的查询顺序,每张表可能会用的索引,实际用的索引,索引的长度,是否有命中索引

  • select_type

      SIMPLE: 查询不使用UNION或子查询
      PRIMARY: 复杂查询时,最外层的SELECT
      SUBQUERY:子查询中的第一个SELECT
      DERIVED:FROM子句的子查询的SELECT,MySQL会递归执行,并将结果放到一个临时表中,称为“派生表”(子查询中派生来的)
      UNION:UNION中的第二个或后面的SELECT语句
      UNION RESULT:用来从 UNION 的匿名临时表检索结果的 SELECT
      DEPENDENT:SELECT 依赖于外层的查询
      UNCACHEABLE:SELECT 的某些特性阻止结果被缓存于一个 Item_cache 中
    
  • table

对应行正在访问的表

  • type

访问该表时的访问类型
性能从差到好的排序是:
ALL < index < range < ref < eq_ref < const,system

all 全表扫描(例外:使用了 limit 或者 Extra 列中显示了 “Using distinct/not exists”)
index 和全表扫描一样,只是是按照索引次序而不是行,优点是避免了排序,缺点是要承担按索引次序读取整个表的开销。如果在 Extra 中看到了 “Using index” 证明只扫描了索引的数据,开销小很多。
range 范围扫描,显然是查询中有 between 或者 > < ,有时候是 IN 或 OR
ref 索引访问
eq_ref 主键或者唯一索引访问,MySQL知道最多只返回一条符合条件的记录
const,system MYSQL对查询的某部分进行优化并将其转化为一个常量时的访问类型,比如 where 子句中放入了主键
NULL 意味着MYSQL能在优化阶段分解查询语句,不用再访问表或者索引

Redis

什么情况下使用redis

针对热点数据进行缓存
对于特定限时数据的存放
针对带热点权值数据的排序list
分布式锁

数据结构

基本数据类型:

  • 字符串
  • 链表
  • 哈希表
  • 集合
  • 有序集合

字符串

  • 为什么不使用C++已有的字符串类型?
    因为C++的字符串获得长度,时间复杂度为O(n),且难以处理特殊字符,并存在扩容问题

总结:Redis字符串本质上是C语言的字符数组,加上了一点别的标识属性的结构体,优点在于:1.字符串获取长度从O(n)->O(1),减少因为字符串扩容引起的数据搬运次数

链表

Redis链表的底层实现是双向链表,结构有两个节点,前置和位置节点
节点定义如下:

typedef struct listNode{
	struct listNode*prev; //前置节点
	struct listNode*next; //位置节点
	void *value; //节点数值
}listNode;

链表常见的API函数

  • lpush:向链表左边增加元素
  • rpush:向链表右边增加元素
  • lpop:弹出左侧第一个元素
  • rpop:弹出右侧第一个元素
  • llen:获得链表长度
  • lrange:按索引范围获得值

哈希表

键和值一一对应,一个key对应一个value,可以通过键key在O(1)时间复杂度内获得对应的value值。由于C语言没有内置哈希表,所以resdis自己实现了这一结构。

redis采用拉链法作为哈希表的实现。

持久化

什么叫持久化?就是将内存的数据搬运到硬盘上,保证在电脑断电等突发情况下数据能从硬盘中恢复

因为Redis是内存数据库,它将自己的数据库状态储存在内存里面,所以如果不想办法将储存在内存中的数据库状态保存到磁盘里面,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见

redis是内存型数据库,数据均存储在内存中,这个优点就是读取速度快,但是数据有易失性。有两种解决方法:RDB(redis data base)和AOF(append only file),前者相当于把整个redis内存数据存储到硬盘中,AOF是把数据修改的命令存储到硬盘。

RDB

把目前Redis内存中的数据,生成一个.rdb形式的文件,保存到硬盘中。

如果发生事故,Redis可以通过RDB文件,进行读取,将数据重新载入到内存中取

全量备份,有手动和自动触发两种,手动有save和bgsave,前者是主线程后者是子线程生成rdb文件,自动c触发一般是bgsave,即有个子线程去生成rdb文件

RDB文件结构

数据部分,有过期时间标量常识,过期时间,类型,键,值

eg.字符串:压缩标识,字符串被压缩后长度,原始长度,字符串
列表:列表长度,第1项长度,第1项,…
集合:集合长度,第1项长度,第1项,…
有序集合:集合长度,第1项长度,第1项,第1项score长度,第1项score…
哈希表:哈希表长度,第1项key长度,第1项key,第1项value长度,第1项value,…

触发条件
  • 手动触发:
  1. save:执行该指令后,主线程执行rdbsave,SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求
  2. bgsave:和SAVE命令直接阻塞服务器进程的做法不同,BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求
  • 自动触发:
  • 在配置文件中写入 save m n,即当m秒内发生n次变化的时候,自动执行bgsave

AOF

记录之后所有对Redis数据进行修改的操作

如果发生事故,redis可以通过AOF文件,将文件中的数据修改命令全部执行一遍,从而恢复数据。

AOF的重写与恢复

由于AOF会以追加的方式记录每一条redis的写命令,因此随着Redis处理的写命令增多,AOF文件也会变得越来越大,命令回放的时间也会增多,为了解决这个问题,Redis引入了AOF rewrite机制(下文称之为AOFRW)。

AOFRW会移除AOF中冗余的写命令,以等效的方式重写、生成一个新的AOF文件,来达到减少AOF文件大小的目的。

也就是说,到了一定程度,根据redis数据状态重新生成一个新的AOF文件,来代替原有的AOF文件。

AOF触发条件
  • 手动触发:bgrewrite
  • 自动触发:配置文件中设置appendonly yes开启
    当然自动触发的时候,仅仅只是设置appendonly yes开启是不够的,还需要明确下写入策略
  1. Always:每个命令执行完后,就将这个命令加载到磁盘文件中
  2. Everysec:每秒写入,也就是说每个命令执行完后,过1s将缓存区命令写到磁盘的AOF文件中
  3. No:这个No不是说不执行AOF,而是将操作命令什么时候存储到磁盘中,交由操作系统决定

Redis默认策略是Everysec

缓存相关

缓存淘汰

怎么淘汰缓存数据?

  • 先进先出–FIFO算法
  • 最近最少使用–LRU算法

把数据按照最近被访问的时间排序,把排序中末端节点删除,新增新数据。

实现思路:双向链表+哈希

  • 最不经常使用–LFU算法
    增加一个数据属性,为被访问次数,然后按照访问次数进行筛选,淘汰被访问次数最少的节点

redis中使用的是LRU算法,即淘汰最近最少被使用的

过期删除

怎么保证几分钟后自己删除?有两种方法,主动删除和惰性删除和定期删除

redis采用的是定期删除策略

  • 主动删除
    设置删除间隔,到了时间点自己运行程序删除工作
    优点:易于理解,设置合理间隔后不会使内存占用超标
    缺点:当redis比较忙时,设置删除时间碰巧到期了要删除的话,主动删除会增加redis负担

  • 惰性删除
    程序取值时查看该数据是否已经过期,如果过期了就删除,没过期就返回
    缺点:容易造成某些数据长期霸占在内存中

  • 定期删除
    定期删除=主动删除+惰性删除
    即每隔一段时间,跑主动删除,不跑主动删除的时候,就执行惰性删除策略

缓存一致

指的是数据库和redis数据库要同步,可能出现数据库中数据被修改,但是还没更新到redis中,如何保持缓存一致?

删除缓存有两种方式:

1.先删除缓存,再更新数据库。解决方案是使用**延迟双删**。

2.先更新数据库,再删除缓存。解决方案是**消息队列或者其他binlog同步**,引入消息队列会带来更多的问题,并不推荐直接使用。

延时双删–先删除缓存,再更新数据库

为了避免更新数据库的时候,其他线程从缓存中读取不到数据,就在更新完数据库之后,再sleep一段时间,然后再次删除缓存。

  • 具体操作:先删除redis中的数据,然后更新数据库,更新完后再删除redis缓存,再同步

为什么要这么做?

  1. 时间点1,要更新数据库,所以我们把redis缓存删掉,然后更新数据库,但是更新数据库过程很长,有可能还没更新完就有人向redis读取数据。

  2. 比如说这时候是时间点2,但此时redis中没数据,所以就得向数据库访问,数据库因为还没更新完数据,所以返回的是旧的值,然后把旧值写入缓存。

  3. 这时候redis多了旧的值,所以需要更新完后,再把redis缓存值删掉,重新加载更新后的数据

消息队列–先更新数据库,再删除缓存

先更新数据库,成功后往消息队列发消息,收到消息后再删除缓存,借助消息队列的重试机制来实现,达到最终一致性的效果。

消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,

为什么是删除,而不是更新缓存?

我们以先更新数据库,再删除缓存来举例。

如果是更新的话,那就是先更新数据库,再更新缓存。

举个例子:如果数据库1小时内更新了1000次,那么缓存也要更新1000次,但是这个缓存可能在1小时内只被读取了1次,那么这1000次的更新有必要吗?

反过来,如果是删除的话,就算数据库更新了1000次,那么也只是做了1次缓存删除,只有当缓存真正被读取的时候才去数据库加载。

缓存穿透

查询某个数据,缓存中没有,数据库中也没有,所以叫“穿透”,两层都没有

  • 解决方法:

      1.拦截非法请求。即在数据库操作访问前进行校验,对不合法请求直接返回。
      2.缓存空对象。即对于经常被访问的,并且数据库没有的键,缓存层记录键=null。
      3.布隆过滤器。
    

布隆过滤器

主要用于判断一个元素是否在一个集合中。通过将元素转化成哈希函数再经过一系列的计算,最终得出多个下标,然后在长度为n的数组中该下标的值修改为1。

当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组(Bit array)中的 K 个点,把它们置为 1

布隆过滤器的原理介绍

  • 当一个元素加入布隆过滤器中的时候,会进行如下操作:

    使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。根据得到的哈希值,得到的哈希值就是在数组的位置,把这个位置中的0改写成1。

  • 当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行如下操作:

    对给定元素再次进行相同的哈希计算;得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。

主要用来比较给定的元素,是不是原始元素集合中已经存在
在这里插入图片描述

布隆过滤器使用场景

  1. 判断给定数据是否存在:比如判断一个数字是否存在于包含大量数字的数字集中(数字集很大,5 亿以上!)、 防止缓存穿透(判断请求的数据是否有效避免直接绕过缓存请求数据库)等等、邮箱的垃圾邮件过滤、黑名单功能等等。
  2. 去重:比如爬给定网址的时候对已经爬取过的 URL 去重。

缓存击穿

查询某个数据的值,该值在缓存中无,在数据库中有,所以叫“击穿”,一层有一层没有,还没有被完全穿透

  • 解决方法

      1.设置热点数据永远不过期。
      2.对并发读数据设置并发锁,降低并发性
    

利用分布式锁,只允许一个请求线程去访问database数据库,其他请求阻塞,这样就避免了很多请求打到数据库上。

热点发现

人为预测

就是人工标记,预测这个商品会成为热点,打个标记。web应用根据这个标记把此商品保存到本地缓存中

这个方案,是根据运营人员的经验进行预测,太不靠谱了。

系统推算

这个方案是根据实实在在的数据访问量进行推算形成,网上也介绍了用访问日志的什么算法,推算哪些是热点数据。 这里分享一个比较简单的方式,就是利用redis4.x自身特性,LFU机制发现热点数据。实现很简单,只要把redis内存淘汰机制设置为allkeys-lfu或者volatile-lfu方式,再执行

./redis-cli --hotkeys

会返回访问频率高的key,并从高到底的排序

也就是说,把淘汰机制设为LFU,最不经常使用–LFU算法,然后返回最经常使用的key,从高到低排序就是我们要选择的热点

缓存雪崩

缓存雪崩指缓存中一大批数据到过期时间,而从缓存中删除。但该批数据查询数据量巨大,查询全部走数据库,造成数据库压力过大。

与缓存击穿不同的是,缓存击穿是查询一条数据,缓存中没有所以得到数据库查找,缓存雪崩是多条数据,同时删除,查询走到数据库这边了

  • 缓存雪崩的解决方法

    1.缓存数据设置随机过期时间,防止同一时间大量数据过期。
    2.设置热点数据永远不过期。
    3.对于集群部署的情况,将热点数据均与分布在不同缓存中。
    4.缓存预热,即在上线前,根据当天的情况分析,将热点数据直接加载到缓存系统中

主从、哨兵、集群

主从复制

有一个主库,有一堆从库,主库用来写入修改数据,而从库用来被别人读取数据,主库对应的数据修改命令也会告诉从库,从库跟着执行从而保证主从库之间数据的一致性。读取的需求由从库执行,不干扰主库 ,减少数据库的负担。

主库发生修改后,会写一个日志到binlog中,然后从库拷贝这个binlog到他的中继日志中去,sql根据这个日志进行查询复制操作。

mysql的复制是异步且串行化的。

哨兵

从库断网问题不大,主库断网问题很大。所以设置了很多哨兵监督主库,如果有哨兵发现主库连不上了,就会喊其他哨兵去连,诶要是好多都连不上,可能主库真的出现了问题,那么就会让哨兵头头在从库中挑选一个当主库。选从库的话,选尽可能网络好,最接近库的。

集群–Cluster

Cluster:redis提供的分布式数据库解决方案,自动将数据切片分给多个节点储存,即使这些节点中有一部分宕机,也可以继续执行数据操作。

大小表关联

MapJoin通常用于一个很小的表和一个大表进行join的场景

1.大小表join(MapJoin)
说明 : 当大表小表关联时,可以将小表读取到内存,在Map端进行数据关联
小表在左在右都会触发 Mapjoin

MapJoin简单说就是在Map阶段将小表数据从 HDFS 上读取到内存中的哈希表中,读完后将内存中的哈希表序列化为哈希表文件,在下一阶段,当 MapReduce 任务启动时,会将这个哈希表文件上传到 Hadoop 分布式缓存中,该缓存会将这些文件发送到每个 Mapper 的本地磁盘上。因此,所有 Mapper 都可以将此持久化的哈希表文件加载回内存,并像之前一样进行 Join。顺序扫描大表完成Join。减少昂贵的shuffle操作及reduce操作

数据倾斜

数据仓库面试题整理(一)

数仓工程师面经:https://blog.csdn.net/qq_41106844/article/details/108337780

2022百度大数据开发工程师实习面试经历
https://blog.csdn.net/weixin_48077303/article/details/123233937?spm=1001.2014.3001.5501

2022字节跳动数据仓库实习面经:

携程 数据仓库工程师一面面经:https://ac.nowcoder.com/discuss/495327?type=0&order=0&page=1&channel=-1

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值