面试总结文档

Q:简单描述一下风控建模的流程?

前期准备工作:不同的模型针对不同的业务场景,在建模项目开始前需要对业务的逻辑和需求有清晰的理解,明确好模型的作用,项目周期时间和安排进度,以及模型效果的要求。
模型设计:包括模型的选择(评分卡还是集成模型),单个模型还是做模型的细分,是否需要做拒绝推论,观察期,表现期的定义,好坏用户的定义,数据的获取途径等都要确定好。
数据拉取及清洗:根据观察期和表现期的定义从数据池中取数,并进行前期的数据清洗和稳定性验证工作,数据清洗包括用户唯一性检查,缺失值检查,异常值检查等。稳定性验证主要考察变量在时间序列上的稳定性,衡量的指标有PSI,平均值/方差,IV等。
特征工程:主要做特征的预处理和筛选,如果是评分卡,需要对特征进行离散化,归一化等处理,再对特征进行降维,降维的方法有IV筛选,相关性筛选,显著性筛选等。另外会基于对业务的深入理解做特征构造工作,包括特征交叉,特征转换,对特征进行四则运算等。
模型建立和评估:选择合适的模型,像评分卡用逻辑回归,只需要做出二分类预测可以选择xgboost等集成模型,模型建好后需要做模型评估,计算AUC,KS,并对模型做交叉验证来评估泛化能力及模型的稳定性。
模型上线部署:在风控后台上配置模型规则,对于一些复杂的模型还得需要将模型文件进行转换,并封装成一个类,用Java等其他形式来调用。
模型监控:前期主要监控模型整体及变量的稳定性,衡量标准主要是PSI,并每日观察模型规则的拒绝率与线下的差异。后期积累一定线上用户后可评估线上模型的AUC,KS,与线下进行比较,衡量模型的线上的实际效果。

Q:评分卡,集成模型在线上是如何部署的?

评分卡的部署较为简单,因为评分卡将变量映射到了一个个区间及得分,所以在普通的风控决策引擎上就可配置。
像一些比较复杂的模型,例如xgboost和lightgbm,一般是将模型文件转换为pmml格式,并封装pmml,在风控后台上上传pmml文件和变量参数文件,并配置好模型的阈值。python模型和R模型都可以用这种方式来部署。

Q:对于金融场景,稳定胜于一切,那在建模过程中如何保证模型的稳定性?

在数据预处理阶段可以验证变量在时间序列上的稳定性,通过这个方法筛掉稳定性不好的变量,也能达到降维的目的。筛选的手段主要有:计算月IV的差异,观察变量覆盖率的变化,两个时间点的PSI差异等。
异常值的检查,剔除噪声,尤其对于逻辑回归这种对于噪声比较敏感的模型。
在变量筛选阶段剔除与业务理解相悖的变量,如果是评分卡,可以剔除区分度过强的变量,这种变量一般不适合放入模型中,否则会造成整个模型被这个变量所左右,造成模型的稳定性下降,过拟合的风险也会增加。
做交叉验证,一种是时间序列上的交叉验证,考察模型在时间上的稳定性,另一种是K折随机交叉验证,考察模型的随机稳定性。
选择稳定性较好的模型,例如随机森林或xgboost这类泛化能力较好的模型。

Q:为什么要做拒绝推断?常用的拒绝推断方法有哪些?

拒绝推断的目的:

如果只用好坏用户建模,则忽略了那些授信被拒的用户,加入拒绝用户是为了让建模样本更接近总体的分布,防止样本有偏,同时也能增加样本数量。
公司内部策略的变动,导致当前的申请者已不能代表建模时点的申请者,所以过去被拒的用户不一定现在也会被拒绝,因此,只使用审批通过的用户可能会造成误判。
做拒绝推断可以找出之前被拒的好用户,挖掘这些用户,改善风控流程,增加公司收益。
拒绝推断的适用范围:

高核准率不适合用拒绝推断,因为高核准率下好坏用户已接近于整体的申请用户。中低核准率适用用拒绝推断。

拒绝推断的常用方法:

硬性截断法:先用好坏用户建立初始模型,然后用这个初始模型对拒绝用户进行打分,设定一个阈值分数(根据对拒绝用户的风险容忍度),低于这个阈值的为坏用户,高于这个阈值的为好用户。再将已标记好的拒绝用户放入样本中,重新建立模型。
分配法:此方法适用于评分卡,先用好坏用户建立初始评分卡模型,再将样本跟据评分高低进行分组,计算各分组的违约率。然后对拒绝用户进行打分并按此前的步骤进行分组,以各分组的违约率为抽样比例,随机抽取改分组下的违约用户,指定其为坏用户,剩下的则是好用户。最后将已标记的拒绝用户加入样本中,重新建立模型。
平常工作中主要用到以上两种方法,个人建议做申请模型最好做一下拒绝推断,这样模型上线后的得分分布和拒绝率和线下才不会有很大的差异。

Q:模型转化为规则后决策点(cutoff点)怎么设定?

规则只是判断用户好坏,而不会像模型会输出违约概率,所以设定决策点时需要考虑到规则的评估指标(精准率,查全率,误伤率,拒绝率),一般模型开发前会设定一个预期的拒绝率,在这个拒绝率下再考量精确率,查全率和误伤率的取舍,找到最佳的平衡点。
好的模型能接受更多的好用户,拒绝掉更多的坏用户,也就是提高好坏件比例,所以可事先设定一个预期目标的好坏件比例来选择最佳的决策点。

Q:怎么做风控模型的冷启动?

风控模型的冷启动是指产品刚上线时,没有积累的用户数据,或者用户还没有表现出好坏,此时需要做模型就是一个棘手的问题,常用的方法如下:

不做模型,只做规则。凭借自己的业务经验,做一些硬性规则,比如设定用户的准入门槛,考量用户的信用历史和多头风险,而且可以接入第三方提供的反欺诈服务和数据产品的规则。另外可以结合人审来对用户的申请资料做风险评估。
借助相同模式产品的数据来建模。如果两个产品的获客渠道,风控逻辑,用户特征都差不多的话,可以选择之前已上线那个产品所积累的用户来建模,不过在模型上线后需要比较线上用户的特征是否与建模用户有较大的差异,如果差异较大,需要对模型对一些调整。
无监督模型+评分卡。这种方法适用于产品上线一段时间后,表现出好坏的用户比较少,但需要做一个模型出来,此时可用线上的申请用户做无监督模型,找出一部分坏样本和好样本,用这些数据来做评分卡模型,当然这种模型准确性是存疑的,需要后续对模型不断迭代优化。

Q:模型上线后是怎么监控的?

前期监控(模型上线后一个月内):

模型最后设定cutoff点后可以得出模型的拒绝率(线下拒绝率), 上线后需要比较模型每日的拒绝率与线下拒绝率。如果两者差异较大,说明线上的用户与建模的用户分布有很大差异,原因可能是没做拒绝推断,或者用户属性随着时间发生了偏移。
监控模型整体的稳定性,通常用PSI来衡量两个时间点的差异程度。模型的稳定性是一个需要长期观察的指标,可绘制月/周PSI变化趋势图来分析稳定性的变化,从中可以发现用户是否随着时间推移属性发生了变化,以便及时对模型做出合理的调整。
变量稳定度分析,目的是如果模型的稳定性不好,可利用变量稳定度分析来了解是哪些变量造成的。对于不稳定的变量要分析其原因,并对模型做出调整,弃用不稳定的变量或者找其他变量来替换。
后期监控(用户表现出了好坏程度):

此时已积累了一些线上的好坏用户,可做模型的线上效果的评估,评估的指标有AUC, KS, 基尼系数,如果模型的线下效果好,但线上效果却不理想,这个模型是要做优化的。
好坏用户的评分分布。绘制线上好坏用户的评分分布图,如果符合期望(高分段好用户占比多,低分段坏用户占比多),则说明模型的线上的区隔能力较好。
变量鉴别力分析。用线上的好坏用户来计算变量的IV值,评价变量的预测能力,预测能力不好的变量可以考虑弃用。

Q:怎么设计反欺诈模型?

本人之前没做过风控的反欺诈模型,因为公司的反欺诈检测都是外包给第三方的,所以对于如何设计反欺诈模型只能给出自己的一些见解:

反欺诈模型不太适合用二分类监督模型来做,一是诈骗的类型很多,是一个多分类的问题,不可能只做单一类型的反欺诈模型。二是大部分数据都是没有标签的,各种监督学习模型几乎无用武之地,而且区分噪声和异常点难度很大,甚至需要一点点想象力和直觉。三是欺诈类型不断在变化,如果用历史数据建的模型来识别之前从未出现过的欺诈类型,几乎是做不到的。
个人觉得做模型之前需要确定两个问题,一是如何发现欺诈点,二是如何验证欺诈点。第一个问题可以用数据可视化的手段,做一下时序分析,或者用无监督学习来识别异常点。第二个问题可以用统计学的方法来验证异常点与总体是有显著性差异的,但有差异不一定就是欺诈,所以需要与领域专家进行讨论,也可以直接叫来领域专家来分析用户哪些行为是欺诈的。
反欺诈规则+机器学习模型来检测欺诈用户,不能单纯依靠机器学习模型来检测欺诈。规则和模型用到的变量一般有:用户的设备,位置信息,关系网络,异常操作记录和第三方黑名单等。总之反欺诈模型难度很高,有相关经验的小伙伴可以互相交流一下。

Q:当模型上线后发现稳定性不佳,或者线上的区分效果不好,你是怎么对模型作调整的?

模型稳定性不佳先检查当初建模时有没有考量过特征的稳定性,在模型前期监控一般会做变量的稳定性分析,如果发现稳定性不佳的变量,考虑弃用或用其他变量替代。另外可以分析下线上用户和建模用户的分布差异,考虑在建模时增加拒绝推断的步骤,让建模样本的分布更加接近于实际整体的申请用户。
线上的效果不好可以从变量角度分析,做一下变量鉴别度分析,剔除掉效果不好的变量,挖掘新的变量入模。如果一个模型已上线较长的时间,用户的属性也慢慢发生偏移,建议重新取数做一个新的模型替代旧模型。

Q:如何衡量一个风控模型的效果?

本人在工作中写过一个评分卡模型的评估方法,这里贴出来可以做个参考:

1.评分卡建模之前的评估:

主要评估建模样本的稳定性,根据评分卡的目的不同,比较对象为总体或者近段时间的样本。

2.分箱过程的评估

变量分箱的同时会计算WOE,这里是对WOE进行可解释性上的评估,包括变化趋势,箱体之间WOE差异,WOE绝对值大小等。

3.对逻辑回归模型的评估

将数据集随机划分为训练集和测试集,计算AUC, KS及Gini系数
通过交叉验证的方法,评估模型的泛化能力,评判指标选择AUC。
绘制学习曲线,评估模型是否有过拟合的风险,评判指标为准确率(Accuracy)。
4.转化评分之后的评估

对score进行可解释上的评估,评估原则与WOE评估大致相同。
绘制评分分布图,观察分布的形状及好坏用户分布的重叠程度。
绘制提升图和洛伦兹曲线,评估评分卡的可解释性和好坏用户区分效果。
评估准确性,根据对精确率和查全率的重视程度绘制PR曲线,并根据业务目标设定cutoff点。
5.评分卡上线后的评估

绘制评分分布表和评分分布图,计算评分的PSI,评估其稳定性。
评估每个入模变量的稳定性。

Q:在实际应用中,如何权衡模型的性能,可解释性和部署的难易程度?

首先要考虑到部署的难易程度,评分卡可以像普通规则一样在风控后台部署,但是像xgboost等比较复杂的模型需要考虑平台支不支持,一般来说能用评分卡解决的就最好用评分卡,部署简单而且可解释性好。然后关于可解释性和模型效果的权衡,个人认为模型的效果达到要求的情况下再去考虑可解释性,如果用评分卡做出来的效果不好,则可以考虑用集成模型或者神经网络等复杂的机器学习模型,只要你的风控后台支持部署。

Q:对于高维稀疏特征,或者是弱特征,你是怎么处理的?

对于高维稀疏特征,逻辑回归的效果要比GBDT好。这是由于逻辑回归的正则项是对特征权重的惩罚,以至于特征的权重不至于过大,而树模型的惩罚项主要是深度和叶子节点数目,而对于高维稀疏特征,10000个样本可能9990个值是0,那只需要一个节点就可以划分9990和剩下的10个样本,可见惩罚项之小,所以GBDT对于高维稀疏特征很容易过拟合。平时工作中如果用的是逻辑回归评分卡,则可以对稀疏特征进行离散化,离散成值为0或不为0,再用woe进行编码。而如果使用xgboost等集成模型,最好还是不要用高维的稀疏特征。
弱特征指的是与目标变量关系不大的特征,或者是区分能力较弱的特征。在大数据风控中弱特征的种类很多,包括社交,通话,位置等信息,而且建模时弱特征会多达数百个。如果是用评分卡建模,弱特征一般会被舍弃掉,因为评分卡的入模特征数不宜过多,一般在15个以下,所以要找寻比较强的特征。而对于xgboost等模型,本身对数据的要求不是很高,并且精度好,一些弱特征进行交叉组合或许能给模型带来不错的效果。

Q:对于成千上万维的特征你是怎么做特征筛选的,如何保证其模型的可解释性和稳定性?

可先做特征的粗筛选,例如缺失率高,方差为0,非常稀疏的特征可以先剔除。
根据变量的稳定性再次进行粗筛,衡量指标有月IV差异,两个时间点的PSI差异等。
根据IV值的高低筛选变量,或者直接用集成模型的特征重要性进行筛选。
为了保证模型的可解释性,需要将共线性的特征剔除。
最后考察各个特征与目标变量的关系,要求在业务上有良好的可解释能力,并且特征与目标变量的关系最好是呈单调线性变化的,这样也能保证模型的稳定性。

Q:如何根据风险因素对用户分层,构建客群差异化的模型?

做客群差异化模型之前最好做一下用户画像,在风控领域中做用户画像的目的是:

系统性的梳理用户群体,找到异同点对用户进行划分群体,分类的维度很多,可以是静态属性,购买偏好,也可以是褥羊毛党等风险属性。
便于更深刻的理解业务,理解用户需求,风控离不开业务,只有深刻理解业务后,才能发现更多潜在的风险。
便于后续的数据挖掘,了解坏用户的行为特征,并且根据用户特征做关联规则分析。
对不同类型的用户,做针对性的风控规则和风控模型。
平常工作中的做法:

对用户做静态属性的划分,比如按性别,年龄,收入,职业等。例如刚毕业工作的年轻人和收入比较稳定的中年人,他们的借款需求,风险程度就不一样,可以先对用户群体做这样的划分,再对每个群体单独建立模型。
根据用户风险属性做差异化模型,例如对手机分期业务做一个套现风险模型,挖掘套现风险属性,目标变量变成是否为套现用户。

Q:额度,利率的风险定价模型你是如何设计的?

首先做风险定价模型需要熟悉产品的属性和特点,像小额现金贷和大额分期贷两种产品的额度定价逻辑就不同。另外也要了解产品的盈利模式和预期的利润,这点需要与业务部门做好沟通,通常关于额度,利率也是业务或者产品制定的。
风险定价模型一般采用评分卡模型,最后设定cutoff点后对通过的用户进行风险等级划分,对于风险高的用户给的额度较低,或者利率较高。一般来说中低额度的用户占大部分,高额度用户占小部分,最后可以得出一个平均额度或利率,这个值事先可以根据预期的利润/资损来计算。

Q:风控流程中不同环节的评分卡是怎么设计的?

申请评分A卡用在贷前审核阶段,主要的作用是决定用户是否准入和对用户进行风险定价(确定额度和利率),用到的数据是用户以往的信用历史,多头借贷,消费记录等信息,并且做A卡一般需要做拒绝推断。A卡一般预测用户的首笔借款是否逾期,或者预测一段时间内是否会逾期,设计的方式也多种多样,有风险差异化评分卡,群体差异化评分卡,或者做交叉评分卡等。
行为B卡主要用在借贷周期较长的产品上,例如手机分期。作用一是防控贷中风险,二是对用户的额度做一个调整。用到的数据主要是用户在本平台的登录,浏览,消费行为数据,还有借还款,逾期等借贷表现数据。
催收C卡主要是对逾期用户做一个画像分析,通过深度挖掘用户特征,对逾期用户进行分群,做智能催收策略等。

一.算法

  • 逻辑回归
  • 决策树
  • 集成学习(随机森林,Adaboost,GBDT,XGBOOST,LightGbm)

二.特征工程

三.模型评估与优化


一. 算法

1.逻辑回归

Q : 逻辑回归的优缺点,在金融领域相比其他算法有什么优势,局限性在哪?

1)优点:

  • 实现简单,速度快,占用内存小,可在短时间内迭代多个版本的模型。
  • 模型的可解释性非常好,可以直接看到各个特征对模型结果的影响,可解释性在金融领域非常重要,所以在目前业界大部分使用的仍是逻辑回归模型。
  • 模型客群变化的敏感度不如其他高复杂度模型,因此稳健更好,鲁棒性更强。
  • 特征工程做得好,模型的效果不会太差,并且特征工程可以并行开发,大大加快开发的速度。
  • 模型的结果可以很方便的转化为策略规则,且线上部署简单。

2)缺点和局限性:

  • 容易欠拟合,相比集成模型,准确度不是很高。
  • 对数据的要求比较高,逻辑回归对缺失值,异常值,共线性都比较敏感,且不能直接处理非线性的特征。所以在数据清洗和特征工程上会花去很大部分的时间。
  • 在金融领域对场景的适应能力有局限性,例如数据不平衡问题,高维特征,大量多类特征,逻辑回归在这方面不如决策树适应能力强。

Q : 逻辑回归是线性模型吗?逻辑回归和线性回归的区别?

  • 逻辑回归是一种广义线性模型,它引入了Sigmod函数,是非线性模型,但本质上还是一个线性回归模型,因为除去Sigmod函数映射关系,其他的算法原理,步骤都是线性回归的。
  • 逻辑回归和线性回归首先都是广义的线性回归,在本质上没多大区别,区别在于逻辑回归多了个Sigmod函数,使样本映射到[0,1]之间的数值,从而来处理分类问题。另外逻辑回归是假设变量服从伯努利分布,线性回归假设变量服从高斯分布。逻辑回归输出的是离散型变量,用于分类,线性回归输出的是连续性的,用于预测。逻辑回归是用最大似然法去计算预测函数中的最优参数值,而线性回归是用最小二乘法去对自变量因变量关系进行拟合。

Q:逻辑回归做分类的样本应该满足什么分布?

应该满足伯努利分布,逻辑回归的分类标签是基于样本特征通过伯努利分布产生的,分类器要做的就是估计这个分布。

Q:逻辑回归解决过拟合的方法有哪些?

  • 减少特征数量,在实际使用中会用很多方法进行特征筛选,例如基于IV值的大小,变量的稳定性,变量之间的相关性等。
  • 正则化,常用的有L1正则化和L2正则化。

Q:什么是特征的离散化和特征交叉?逻辑回归为什么要对特征进行离散化?

  • 特征离散化是将数值型特征(一般是连续型的)转变为离散特征,例如评分卡中的woe转化,就是将特征进行分箱,再将每个分箱映射到woe值上,就转换为了离散特征。特征交叉也叫作特征组合,是将单独的特征进行组合,使用相乘/相除/笛卡尔积等形成合成特征,有助于表示非线性关系。比如使用One-Hot向量的方式进行特征交叉。这种方式一般适用于离散的情况,我们可以把它看做基于业务理解的逻辑和操作,例如经度和纬度的交叉,年龄和性别的交叉等。
  • 实际工作中很少直接将连续型变量带入逻辑回归模型中,而是将特征进行离散化后再加入模型,例如评分卡的分箱和woe转化。这样做的优势有以下几个:1)特征离散化之后,起到了简化模型的作用,使模型变得更稳定,降低了模型过拟合的风险。2)离散化之后的特征对异常数据有很强的鲁棒性,实际工作中的哪些很难解释的异常数据一般不会做删除处理,如果特征不做离散化,这个异常数据带入模型,会给模型带来很大的干扰。3)离散特征的增加和减少都很容易,且稀疏向量的内积乘法运算速度快,易于模型的快速迭代。4)逻辑回归属于广义线性模型,表达能力有限,特征离散化之后,每个离散变量都有单独的权重,相当于给模型引入了非线性,能够提高模型的表达能力。5)离散化后的特征可进行特征交叉,进一步引入非线性,提高模型的表达能力。

Q:在逻辑回归中,为什么要常常做特征组合(特征交叉)?

逻辑回归模型属于线性模型,线性模型不能很好处理非线性特征,特征组合可以引入非线性特征,提升模型的表达能力。另外,基本特征可以认为是全局建模,组合特征更加精细,是个性化建模,但对全局建模会对部分样本有偏,对每一个样本建模又会导致数据爆炸,过拟合,所以基本特征+特征组合兼顾了全局和个性化。

Q:做评分卡中为什么要进行WOE化?

  • 更好的解释性,变量离散化之后可将每个箱体映射到woe值,而不是通常做one-hot转换。
  • woe化之后可以计算每个变量的IV值,可用来筛选变量。
  • 对离散型变量,woe可以观察各个level间的跳转对odds的提升是否呈线性。
  • 对连续型变量,woe和IV值为分箱的合理性提供了一定的依据,也可分析变量在业务上的可解释性。
  • 用woe编码可以处理缺失值问题。

Q:高度相关的特征带入逻辑回归到底有什么影响?为什么逻辑回归要将高度相关特征剔除?

  • 在损失函数最终收敛的情况下,就算有很多相关度很高的特征,也不会影响模型的效果。假设一个特征将它重复100次,生成100个高度相关的特征。那么模型训练完之后,这100个特征和原来那一个特征扮演的效果一样,每一个特征的权重都是原来特征的1/100,只是可能中间很多特征的系数正负相互抵消了,比如做评分卡,如果引入了高度相关的特征,那么最后逻辑回归的系数符号可能就会不一致。
  • 虽然高度相关特征对模型结果没什么大的影响,但还是要剔除相关性高的特征,原因是一个可以减少特征数量,提高模型的训练速度,减少过拟合的风险。二是去掉高相关特征可以让模型的可解释性更好。尤其在做评分卡时,为了使最后每个特征的系数符号一致,必须做特征相关性筛选。

Q:逻辑回归的特征系数的绝对值可以认为是特征的重要性吗?

首先特征系数的绝对值越大,对分类效果的影响越显著,但不能表示系数更大的特征重要性更高。因为改变变量的尺度就会改变系数的绝对值,而且如果特征是线性相关的,则系数可以从一个特征转移到另一个特征,特征间相关性越高,用系数解释变量的重要性就越不可靠。

Q:逻辑回归为什么要用极大似然函数作为损失函数?

  • 数据归一到0和1,这样的话梯度下降会收敛的更快,相比不归一化,不会出现扁平的情况。
  • 数据归一化之后可以提高结果的精度,尤其在与正则化同时使用时,数据归一化可以避免由于特征取值范围差距过大,对取值较小特征的参数影响更大的问题。

2.决策树

Q:决策树模型的优缺点及适用性?

优点:

  • 易于理解,决策树可以生成IF…TEHN逻辑表达的树结构,可解释性很好。
  • 相比逻辑回归对数据的处理较简单,不太需要做例如数据离散化,归一化等操作。
  • 决策树是目前已知的对于处理非线性交互的最好的算法。
  • 模型的效果比较好,例如随机森林,xgboost都是基于决策树构建的。

缺点:

  • 很容易在训练过程中生成过于复杂的树结构,造成过拟合。
  • 不适合处理高维数据,当属性数量过大时,部分决策树就不适用了。
  • 泛化能力能力比较差,对于没有出现过的值几乎没有办法。

Q:简述一下决策树的原理以及树的构建过程。

决策树时基于树的结构进行决策的,学习过程包括特征选择,决策树的生成和剪枝过程。决策树的学习过程通常是递归地选择最优特征,并用最优特征对数据集进行分割。开始时,构建根节点,选择最优特征,该特征有几种值就划分为多少子集,每个子集递归调用此方法,返回结点,返回的结点就是上一层的子节点,直到所有特征都已经用完,或者数据集只有一维特征为止。

Q:简述一下ID3,C4.5,CART三类决策树的原理和异同点。

  • ID3选择最佳分割点是基于信息增益的,信息增益越大,表明使用这个属性来划分所获得的“纯度提升”越大。C4.5对ID3进行了改进,因为ID3使用的信息增益对数据划分时,可能出现每个结点只包含一个样本,这些子节点的纯度已经达到最大,但是,这样的决策树并不具有泛化能力,无法对新样本进行预测。且ID3不能处理连续型变量和缺失值。而C4.5使用信息增益率来选择属性,克服了信息增益选择属性时偏向选择值多的属性的不足。且可以处理连续型变量和缺失值。
  • C4.5是基于ID3的改进版,只能用于分类。而CART树既可以做分类,也可以做回归。CART的本质是对特征空间进行二元划分,所以CART生成的是一颗二叉树,且可以对类别型变量和数值型变量进行分裂。对分类型变量进行划分时,分为等于该属性和不等于该属性,在对连续型变量进行划分时,分为大于和小于,在做分类是使用的是GINI系数作为划分标准,在做回归时使用的是均方误差。

Q:分类树和回归树的区别在哪里?

  • 分类树以C4.5为例,在对一个特征进行划分时,是穷举这个特征的每一个阈值,找到使得特征<=阈值和特征>阈值分成的两个分支的熵的最大值,按照该标准分支得到两个新的节点,用同样的方法继续分支,直到得到种类唯一的叶子节点,或者达到预设的终止条件为止。
  • 回归树的流程是类似分类树的,区别在于划分时的标准不再是最大熵,而是最小化均差,如果节点的预测值错的越离谱,均方差越大,通过最小化均差能够找到最可靠的分支依据。

Q:决策树对缺失值是如何处理的?

决策树处理缺失要考虑以下三个问题:

  1. 当开始选择哪个属性来划分数据集时,样本在某几个属性上有缺失怎么处理:
  • 忽略这些缺失的样本。
  • 填充缺失值,例如给属性A填充一个均值或者用其他方法将缺失值补全。
  • 计算信息增益率时根据缺失率的大小对信息增益率进行打折,例如计算属性A的信息增益率,若属性A的缺失率为0.9,则将信息增益率乘以0.9作为最终的信息增益率。

\2. 一个属性已经被选择,那么在决定分割点时,有些样本在这个属性上有缺失怎么处理?

  • 忽略这些缺失的样本。
  • 填充缺失值,例如填充一个均值或者用其他方法将缺失值补全。
  • 把缺失的样本,按照无缺失的样本被划分的子集样本个数的相对比率,分配到各个子集上去,至于那些缺失样本分到子集1,哪些样本分配到子集2,这个没有一定准则,可以随机而动。
  • 把缺失的样本分配给所有的子集,也就是每个子集都有缺失的样本。
  • 单独将缺失的样本归为一个分支。

3.决策树模型构建好后,测试集上的某些属性是缺失的,这些属性该怎么处理?

  • 如果有单独的缺失值分支,依据此分支。
  • 把待分类的样本的属性A分配一个最常出现的值,然后进行分支预测。
  • 待分类的样本在到达属性A结点时就终止分类,然后根据此时A结点所覆盖的叶子节点类别状况为其分配一个发生概率最高的类。

Q:为什么决策树不需要对数据做归一化等预处理?

决策树是一种概率模型,所以不需要做归一化,因为它不关心变量的值,而是关心变量的分布和变量之间的条件概率,所以归一化这种数值缩放,不影响分裂结点位置。

Q:如何解决决策树的过拟合问题?

  • 预剪枝的方法:通过提前停止树的构建而对树剪枝,是目前解决过拟合的主要方法。常用的剪枝条件包括限制树的深度,限制叶节点最小样本数,限制叶节点的最小样本权重,限制叶节点的信息增益值的阈值等。
  • 后剪枝的方法:首先构造完整的决策树,允许树过度拟合数据,然后应单个结点代替子树,节点的分类采用子树的主要分类。剪枝方法有错误率降低剪枝,悲观错误剪枝,代价复杂度剪枝

3.集成学习

Q:什么是集成学习?集成学习有哪些框架?简单介绍各个框架的常用算法。

  • 集成学习是一种优化手段和策略,通常是结合多个简单的弱分类器来集成模型组,去做更可靠的决策。一般的弱分类器可以是决策树,SVM,kNN等构成,其中的模型可以单独来训练,并且这些弱分类器以某种方式结合在一起去做出一个总体预测。集成学习就是找出哪些弱分类器可以结合在一起,以及如何结合的方法。目前集成学习主要有bagging,boosting,stacking三种:
  • bagging:对训练集进行随机子抽样,对每个子训练集构建基模型,对所有的基模型的预测结果进行综合产生最后的预测结果。如果是分类算法,则用多数投票法确定最终类别,如果是回归算法,则将各个回归结果做算术平均作为最终的预测值。常用的bagging算法:随机森林
  • boosting:训练过程为阶梯状,基模型按照次序进行训练(实际上可以做到并行处理),先给定一个初始训练数据,训练出第一个基模型,根据基模型的表现对样本进行调整,在之前基模型预测错误的样本上投入更多的关注,然后用调整后的样本训练下一个基模型,重复上述过程N次,将N个基模型进行加权结合,输出最后的结果。常用的算法有GBDT,XGBOOST等。
  • stacking:是一种组合分类器的方法,以两层为例,第一层由多个基学习器组成,其输入为原始训练集,第二层的模型则是以第一层基学习器的输出作为训练集进行再训练(一般用LR进行回归组合),从而得到完整的stacking模型。要得到stacking模型,关键在于如何构造第二层的特征,构造第二层特征的原则是尽可能的避免信息泄露,因此对原始训练集常常采用类似于K折交叉验证的划分方法。各个基模型要采用相同的Kfold,这样得到的第二层特征的每一折(对应于之前的K折划分)都将不会泄露进该折数据的目标值信息 ,从而尽可能的降低过拟合的风险。

Q : 简单描述一下模型的偏差和方差?bagging和boosting主要关注哪个?

  • 偏差描述的是预测值与真实值的差距,偏差越大,越偏离真实数据。
  • 方差描述的是预测值的变化范围,离散程度,方差越大,数据分布越分散。
  • bagging主要关注的是降低方差,boosting主要关注降低偏差。

随机森林

Q:简述一下随机森林的原理,随机森林的构造过程。

随机森林是bagging算法的代表,使用了CART树作为弱分类器,将多个不同的决策树进行组合,利用这种组合来降低单棵决策树的可能带来的片面性和判断不准确性。对于普通的决策树,是在所有样本特征中找一个最优特征来做决策树的左右子树划分,而随机森林会先通过自助采样的方法(bootstrap)得到N个训练集,然后在单个训练集上会随机选择一部分特征,来选择一个最优特征来做决策树的左右子树划分,最后得到N棵决策树,对于分类问题,按多数投票的准则确定最终结果,对于回归问题,由多棵决策树的预测值的平均数作为最终结果。随机森林的随机性体现在两方面,一个是选取样本的随机性,一个是选取特征的随机性,这样进一步增强了模型的泛化能力。

Q:随机森林的优缺点?

优点:

  • 训练可以高度并行化,训练速度快,效率高。
  • 两个随机性的引入,使得随机森林不容易过拟合,具有很好的抗噪声能力。
  • 由于每次不再考虑全部的特征属性,二是特征的一个子集,所以相对于bagging计算开销更小,效率更高。
  • 对于数据的适应能力强,可以处理连续型和离散型的变量,数据无需规范化。
  • 可以输出变量的重要程度,被认为是一种不错的降维方法。

缺点:

  • 在某些噪声较大的分类问题和或回归问题上容易过拟合。
  • 模型的可解释性比较差,无法控制模型内部的运行。
  • 对于小数据或者低维数据,效果可能会不太好。

Q:随机森林为什么不容易过拟合?

随机森林由很多棵树组合在一起,单看每一棵树可以是过拟合的,但是既然是过拟合,就会拟合到非常小的细节,随机森林通过引入随机性,让每一棵树过拟合的细节不同,再将这些树组合在一起,过拟合的部分就会抵消掉,不过随机森林还是可能会出现过拟合的现象,只是出现的概率相对较低。

Q:随机森林输出特征重要性的原理?

  • 随机森林对于特征重要性的评估思想:判断每个特征在随机森林中的每颗树上做了多大的贡献,然后取个平均值,最后比一比特征之间的贡献大小。其中关于贡献的计算方式可以是基尼指数或袋外数据错误率。
  • 基于基尼系数:如果特征X出现在决策树J中的结点M,则计算节点M分枝前后的Gini指数变化量,假设随机森林由N棵树,则计算N次的Gini系数,最后将所有的Gini系数做一个归一化处理就得到了该特征的重要性。
  • 基于袋外数据错误率:袋外数据指的是每次随机抽取未被抽取达到的数据,假设袋外的样本数为O,将这O个数据作为测试集,代入已生成好的随机森林分类器,得到预测的分类结果,其中预测错误的样本数为X,则袋外数据误差为X/O,这个袋外数据误差记为errOOB1,下一步对袋外数据的特征A加入噪声干扰,再次计算袋外误差errOOB2,假设随机森林由N个分类器,则特征A的重要性为:sum(errOOB2-errOOB1)/N,其依据就是,如果一个特征很重要,那么其变动后会非常影响测试误差,如果测试误差没有怎么改变,则说明特征A不重要。

Adaboost

Q:简单描述一下Adaboost的算法原理和流程。

  • Adaboost基于分类器的错误率分配不同的权重系数,最后得到累加加权的的预测结果。

算法流程:

  • 给数据中每一个样本一个权重,若有N个样本,则每个样本的权重为1/N.
  • 训练数据的每一个样本,得到第一个分类器。
  • 计算该分类器的错误率,根据错误率计算给分类器分配的权重。
  • 将第一个分类器分错的样本权重增加,分对的样本权重减少,然后再用新的样本权重训练数据,得到新的分类器。
  • 迭代这个训练步骤直到分类器错误为0或达到迭代次数。
  • 将所有的弱分类器加权求和,得到分类结果(分类器权重),错误率低的分类器获得更高的决定系数,从而在数据进行预测起关键作用。

Q:Adaboost的优点和缺点?

优点:

  • 分类精度高,构造简单,结果可理解。
  • 可以使用各种回归分类模型来构建弱学习器,非常灵活。
  • 不容易过拟合。

缺点:

  • 训练时会过于偏向分类困难的数据,导致Adaboost容易受噪声数据干扰。
  • 依赖于弱分类器,训练时间可能比较长。

GBDT

Q:简单说一下GBDT的原理。

  • GBDT是boosting的一种方法,主要思想是每一次建立单个分类器时,是在之前建立的模型的损失函数的梯度下降方向。损失函数越大,说明模型越容易出错,如果我们的模型能让损失函数持续的下降,则说明我们的模型在持续不断的改进,而最好的方式就是让损失函数在其梯度的方向上下降。
  • GBDT的核心在于每一棵树学的是之前所有树结论和的残差,残差就是真实值与预测值的差值,所以为了得到残差,GBDT中的树全部是回归树,之所以不用分类树,是因为分类的结果相减是没有意义的。
  • Shrinkage(缩减)是 GBDT 的一个重要演进分支,Shrinkage的思想在于每次走一小步来逼近真实的结果,要比直接迈一大步的方式更好,这样做可以有效减少过拟合的风险。它认为每棵树只学到了一小部分,累加的时候只累加这一小部分,通过多学习几棵树来弥补不足。这累加的一小部分(步长*残差)来逐步逼近目标,所以各个树的残差是渐变的而不是陡变的。
  • GBDT可以用于回归问题(线性和非线性),也可用于分类问题。

Q:为什么对于高维稀疏特征不太适合用GBDT?

  • GBDT在每一次分割时需要比较大量的特征,特征太多,模型训练很耗费时间。
  • 树的分割往往只考虑了少部分特征,大部分的特征都用不到,所有的高维稀疏的特征会造成大量的特征浪费。

Q:GBDT和随机森林的异同点?

相同点:

  • 都是由多棵树构成,最终的结果也是由多棵树决定。

不同点:

  • 随机森林可以由分类树和回归树组成,GBDT只能由回归树组成。
  • 随机森林的树可以并行生成,而GBDT只能串行生成,所以随机森林的训练速度相对较快。
  • 随机森林关注减小模型的方差,GBDT关注减小模型的偏差。
  • 随机森林对异常值不敏感,GBDT对异常值非常敏感。
  • 随机森林最终的结果是多数投票或简单平均,而GBDT是加权累计起来。

Q:GBDT的优缺点?

优点:

  • GBDT每一次的残差计算都增大了分错样本的权重,而分对的权重都趋近于0,因此泛化性能比较好。
  • 可以灵活的处理各种类型的数据。

缺点:

  • 对异常值比较敏感。
  • 由于分类器之间存在依赖关系,所以很难进行并行计算。

XGBOOST

Q:XGBOOST和GBDT的区别在哪里?

  • 传统的GBDT是以CART树作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题),线性分类器的速度是比较快的,这时候xgboost的速度优势就体现了出来。
  • 传统的GBDT在优化时只使用一阶导数,而xgboost对损失函数做了二阶泰勒展开,同时用到了一阶和二阶导数,并且xgboost支持使用自定义损失函数,只要损失函数可一阶,二阶求导。
  • xgboost在损失函数里加入了正则项,用来减小模型的方差,防止过拟合,正则项里包含了树的叶节点的个数, 每个叶子节点上输出的score的L2模的平方和。
  • xgboost里有一个参数叫学习速率(learning_rate), xgboost在进行完一次迭代后,会将叶子节点的权重乘上学习速率,主要是为了削弱每棵树的影响,让后面有更大的学习空间。实际应用中,一般把learing_rate设置得小一点,然后迭代次数(n_estimators)设置得大一点。
  • xgboost借鉴了随机森林的原理,支持行抽样(subsample)和列抽样(colsample_bytree,colsample_bylevel), 行抽样指的是随机森林里对数据集进行有放回抽样,列抽样指的是对特征进行随机选择,不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。

Q:为什么XGBOOST要用泰勒展开,优势在哪里?

xgboost使用了一阶和二阶偏导,二阶导数有利于梯度下降的更快更准,使用泰勒展开取得函数做自变量的二阶导数形式,可以在不选定损失函数具体形式的情况下,仅仅依靠输入数据的值就可以进行叶子分裂优化计算,本质上也就把损失函数的选取和模型算法的优化分开来了,这种去耦合增加了xgboost的适用性,使得它按需选取损失函数,既可以用于分类,也可以用于回归。

Q:XGBOOST是如何寻找最优特征的?

xgboost在训练过程中给出各个特征的增益评分,最大增益的特征会被选出来作为分裂依据,从而记忆了每个特征在模型训练时的重要性,从根到叶子中间节点涉及某特征的次数作为该特征重要性排序。

Q:XGBOOST是如何处理缺失值的?

xgboost为缺失值设定了默认的分裂方向,xgboost在树的构建过程中选择能够最小化训练误差的方向作为默认的分裂方向,即在训练时将缺失值划入左子树计算训练误差,再划入右子树计算训练误差,然后将缺失值划入误差小的方向。

Q:XGBOOST的并行化是如何实现的?

  • xgboost的并行不是在tree粒度上的并行,xgboost也是一次迭代完才能进行下一次迭代(第t次迭代的损失函数包含了第t-1次迭代的预测值),它的并行处理是在特征粒度上的,在决策树的学习中首先要对特征的值进行排序,然后找出最佳的分割点,xgboost在训练之前,就预先对数据做了排序, 然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。
  • 可并行的近似直方图算法。树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以xgboost还提出了一种可并行的近似直方图算法,用于高效地生成候选的分割点。

Q:XGBOOST采样时有放回的还是无放回的?

xgboost属于boosting方法的一种,所以采样时样本是不放回的,因而每轮计算样本不重复,另外,xgboost支持子采样,每轮计算可以不使用全部的样本,以减少过拟合。另外一点是xgboost还支持列采样,每轮计算按百分比随机抽取一部分特征进行训练,既可以提高速度又能减少过拟合。

Q:XGBOOST的调参步骤是怎样的?

PS:这里使用Gridsearch cv来穷举检索最佳的参数,如果时间允许,可以通过设置步数先粗调,再细调。

  • 保持learning rate和其他booster相关的参数不变,调节和estimators的参数。learing_rate可设为0.1, max_depth设为4-6之间,min_child_weight设为1,subsample和colsample_bytree设为0.8 ,其他的参数都设为默认值即可。
  • 调节max_depth 和 min_child_weight参数,首先,我们先大范围地粗调参数,然后再小范围地微调。
  • gamma参数调优
  • subsample和colsample_bytree 调优
  • 正则化参数调优,选择L1正则化或者L2正则化
  • 缩小learning rate,得到最佳的learning rate值

Q:XGBOOST特征重要性的输出原理?

xgboost是用get_score方法输出特征重要性的,其中importance_type参数支持三种特征重要性的计算方法:

  • importance_type*=*weight(默认值),使用特征在所有树中作为划分属性的次数。
  • importance_type*=*gain,使用特征在作为划分属性时loss平均的降低量。
  • importance_type*=*cover,使用特征在作为划分属性时对样本的覆盖度。

LightGbm

Q:LightGBM相比XGBOOST在原理和性能上的差异?

1.速度和内存上的优化:

  • xgboost用的是预排序(pre-sorted)的方法, 空间消耗大。这样的算法需要保存数据的特征值,还保存了特征排序的结果(例如排序后的索引,为了后续快速的计算分割点),这里需要消耗训练数据两倍的内存。 其次,时间上也有较大的开销,在遍历每一个分割点的时候,都需要进行分裂增益的计算,消耗的代价大。
  • LightGBM用的是直方图(Histogram)的决策树算法,直方图算法的基本思想是先把连续的浮点特征值离散化成k个整数,同时构造一个宽度为k的直方图。在遍历数据的时候,根据离散化后的值作为索引在直方图中累积统计量,当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点。

2.准确率上的优化:

  • xgboost 通过level(depth)-wise策略生长树, Level-wise过一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度,不容易过拟合。但实际上Level-wise是一种低效的算法,因为它不加区分的对待同一层的叶子,带来了很多没必要的开销,因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。
  • LightGBM通过leaf-wise(best-first)策略来生长树, Leaf-wise则是一种更为高效的策略,每次从当前所有叶子中,找到分裂增益最大的一个叶子,然后分裂,如此循环。因此同Level-wise相比,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。Leaf-wise的缺点是可能会长出比较深的决策树,产生过拟合。因此LightGBM在Leaf-wise之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。

3.对类别型特征的处理**:**

  • xgboost不支持直接导入类别型变量,需要预先对类别型变量作亚编码等处理。如果类别型特征较多,会导致哑变量处理后衍生后的特征过多,学习树会生长的非常不平衡,并且需要非常深的深度才能来达到较好的准确率。
  • LightGBM可以支持直接导入类别型变量(导入前需要将字符型转为整数型,并且需要声明类别型特征的字段名),它没有对类别型特征进行独热编码,因此速度比独热编码快得多。LightGBM使用了一个特殊的算法来确定属性特征的分割值。基本思想是对类别按照与目标标签的相关性进行重排序,具体一点是对于保存了类别特征的直方图根据其累计值(sum_gradient/sum_hessian)重排序,在排序好的直方图上选取最佳切分位置。

二. 特征工程

Q:什么是特征工程?为什么特征工程对机器学习很重要?

  • 特征工程指的是使用专业知识和技巧来处理数据,使得特征在机器学习算法上发挥更好的作用的过程。这个过程包含了数据预处理,特征构建,特征筛选等。特征工程的目的就是筛选出好的特征,得到更好的训练数据,使模型达到更好的效果。
  • 从数据中提取出来的特征好坏会直接影响到模型的效果,有的时候,如果特征工程做得好,仅使用一些简单的机器学习算法,也能达到很好的效果。由此可见特征工程在实际的机器学习中的重要性。

Q:特征工程的一般步骤是什么?什么是特征工程的迭代?

特征工程常规步骤:

  • 数据获取,数据的可用性评估(覆盖率,准确率,获取难度)
  • 探索性数据分析,对数据和特征有一个大致的了解,同时进行数据的质量检验,包括缺失值,异常值,重复值,一致性,正确性等。
  • 特征处理,包括数据预处理和特征转换两部分,数据预处理主要做清洗工作(缺失值,异常值,错误值,数据格式),特征转换即对连续特征,离散特征,时间序列特征进行转换,便于入模。
  • 特征构建,特征构建的目的是找寻与目标变量相关且区分度较好的特征。常用的方法有特征交叉,四则运算,基于业务理解进行头脑风暴构建特征等。
  • 特征筛选,大量的特征中选择少量的有用特征,也叫作特征降维,常用的方法有过滤法,包装法,嵌入法。

特征工程的迭代:

  • 选择特征:具体问题具体分析,通过查看大量的数据和基于对业务的理解,从数据中查找可以提出出数据的关键。
  • 设计特征:可以自动进行特征提取工作,也可以手工进行特征的构建。
  • 选择特征:使用不同的特征构造方法,从多个角度来评判这个特征是否适合放入模型中。
  • 计算模型:计算模型在该特征上所提升的准确率。
  • 上线测试:通过在线测试的效果来评估特征是否有效。

Q:常用的特征工程方法有哪些?

  • 特征处理:数据的预处理包括异常值和缺失值,要根据实际的情况来处理。特征转换主要有标准化,归一化,区间缩放,二值化等,根据特征类型的不同选择合适的转换方法。
  • 特征构建:特征之间的四则运算(有业务含义),基于业务理解构造特征,分解类别特征,特征交叉组合等。
  • 特征筛选:过滤法,封装法,嵌入法。

Q:在实际的风控建模中怎么做好特征工程?

本人工作中的一些经验总结:

  • 因为做风控模型大部分的数据源来自第三方,所以第三方数据的可用性评估非常重要,一方面需要了解这些特征底层的衍生逻辑,判断是否与目标变量相关。另一方面考察数据的覆盖率和真实性,覆盖率较低和真实性存疑的特征都不能使用在模型中。
  • 基于金融的数据特点,在特征筛选这个步骤上考量的因素主要有:一个是时间序列上的稳定性,衡量的指标可以是PSI,方差或者IV。一个是特征在样本上覆盖率,也就是特征的缺失率不能太高。另外就是特征的可解释性,特征与目标变量的关系要在业务上要解释的通。
  • 如果第三方返回有用户的原始底层数据,例如社保的缴纳记录,运营商的通话/短信记录,则需要在特征衍生上多下功夫,基于自身对数据的敏感性和业务的理解,构建具有金融,风险属性的特征,也可以与业务部门进行沟通找寻与业务相关的特征。

Q:实际项目中原始数据通常有哪些问题?你是如何解决的?

  • 一些特征的底层逻辑不清晰,字面上的意思可能与实际的衍生逻辑相悖,这个需要与第三方数据供应商进行沟通,了解清楚特征的衍生逻辑。
  • 数据的真实性可能存在问题。比如一个特征是历史总计,但第三方只是爬取了用户近2年的数据,这样的特征就不符合用户的真实情况。所以对数据的真实性校验显得非常重要。
  • 有缺失的特征占的比例较高。在进行缺失值处理前先分析缺失的原因,而不是盲目的进行填充,删除等工作。另外也要分析缺失是否有风险属性,例如芝麻分缺失的用户相对来说风险会较高,那么缺失可以当做一个类别来处理。
  • 大量多类特征如何使用。例如位置信息,设备信息这些特征类别数较多,如果做亚编码处理会造成维度灾难,目前常用的方法一个是降基处理,减少类别数,另一个是用xgboost来对类别数做重要性排序,筛选重要性较高的类别再做亚编码处理。

Q:在做评分卡或其他模型中,怎么衡量特征(数据)的有用性?

  • 特征具有金融风险属性,且与目标变量的关系在业务上有良好的可解释性。
  • 特征与目标变量是高度相关的,衡量的指标主要是IV。
  • 特征的准确率,这个需要了解特征的衍生逻辑,并与实际一般的情况相比较是否有异常。
  • 特征的覆盖率,一般来说覆盖率要达到70%以上。
  • 特征的稳定性,特征的覆盖率,分布,区分效果在时间序列上的表现比较稳定。
  • 特征的及时性,最好是能代表用户最近的信用风险情况。

Q:为什么探索性数据分析(EDA)在机器学习中非常重要?

  • EDA不单是看看数据的分布,而是对数据整体有一个大概的了解。通过作图、制表、方程拟合、计算特征量等手段探索数据的结构和规律。从中发现关键性的价值信息,这些信息对于后续建模及对模型的正确理解有很重要的意义。
  • 通过EDA可以发现数据的异常,可以分析每个特征与目标变量之间的关系,特征与特征之间的关系,为特征构建和特征筛选提供有价值的信息。
  • EDA分析可以验证数据是不是你认为的那样,实际情况中由于数据和特征量比较大,往往忽视这些数据是如何生成的,数据突出的问题或模型的实施中的错误会被长时间忽视,这可能会导致基于错误信息做出决策。

Q:缺失值的处理方式有哪些?风控建模中该如何合理的处理缺失?

  • 首先要了解缺失产生的原因,因数据获取导致的缺失建议用填充的方式(缺失率比较低的情况下),因用户本身没有这个属性导致的缺失建议把缺失当做一个类别。另外可以分析缺失是否有风险属性,有的话最好当做一个类别来处理。
  • 风控模型对于缺失率的要求比较高,尤其是评分卡。个人认为,缺失率在30%以上的特征建议不要用,缺失率在10%以下的变量可用中位数或随机森林来填充,10%-30%的缺失率建议当做一个类别。对于xgboost和lightgbm这类可以自动处理缺失值的模型可以不做处理。

Q:如何发现数据中的异常值?对异常值是怎么处理的?

  • 一种是基于统计的异常点检测算法例如极差,四分位数间距,均差,标准差等,这种方法适合于挖掘单变量的数值型数据。另一种主要通过距离方法来检测异常点,将数据集中与大多数点之间距离大于某个阈值的点视为异常点,检测的标准有欧式距离,绝对距离。
  • 对于异常值先检查下是不是数据错误导致的,数据错误的异常作删除即可。如果无法判别异常的原因,要根据实际情况而定,像评分卡会做WOE转换,所以异常值的影响不大,可以不做处理。若异常值的数量较多,建议将异常值归为一类,数量较少作删除也可以。

Q:对于时间序列特征,连续特征,离散特征这三类是怎么做特征转换的?

  • 时间序列特征:将时间变量的维度进行分离(年/月/日/时/分/秒),或者与位置变量进行结合衍生成新的特征。
  • 连续型特征:标准化,归一化,区间缩放,离散化。在评分卡中主要用的是离散化,离散化常用的方法有卡房分箱,决策树分箱,等频和等深分箱。
  • 离散型特征:如果类别数不是很多,适合做亚编码处理,对于无序离散变量用独热编码,有序离散变量用顺序编码。如果类别数较多,可用平均数编码的方法。

Q:如何处理样本不平衡的问题?

  • 在风控建模中出现样本不平衡主要是坏样本的数量太少,碰到这个问题不要急着试各种抽样方法,先看一下坏用户的定义是否过于严格,过于严格会导致坏样本数量偏少,中间样本偏多。坏用户的定义一般基于滚动率分析的结果,不过实际业务场景复杂多样,还是得根据情况而定。
  • 确定好坏用户定义是比较合理的之后,先尝试能不能扩大数据集,比如一开始取得是三个月的用户数据,试着将时间线延长来增加数据。因为机器学习是使用现在的数据在整个数据分布上进行估计,因此更多的数据往往能够得到更多的分布信息,以及更好的分布估计。
  • 对数据集进行抽样,一种是进行欠采样,通过减少大类的数据样本来降低数据的不平衡,另一种是进行过采样,通过增加小类数据的样本来降低不平衡,实际工作中常用SMOTE方法来实现过采样。
  • 尝试使用xgboost和lightgbm等对不平衡数据处理效果较好的模型。
  • 尝试从新的角度来理解问题,可以把那些小类样本当做异常点,因此该分类问题转化为异常检测问题或变化趋势检测问题,这种方法笔者很少用到,就不详细说明了。

Q:特征衍生的方法有哪些?说说你平时工作中是怎么做特征衍生的?

常规的特征衍生方法:

  • 基于对业务的深入理解,进行头脑风暴,构造特征。
  • 特征交叉,例如对类别特征进行交叉相乘。
  • 分解类别特征,例如对于有缺失的特征可以分解成是否有这个类别的二值化特征,或者将缺失作为一个类别,再进行亚编码等处理。
  • 重构数值量(单位转换,整数小数拆分,构造阶段性特征)
  • 特征的四则运算,例如取平均/最大/最小,或者特征之间的相乘相除。

平时工作特征衍生的做法:

  • 因为风控模型通常需要好的解释能力,所以在特征衍生时也会考虑到衍生出来的特征是否与目标变量相关。例如拿到运营商的通话记录数据,可以衍生一个"在敏感时间段(深夜)的通话次数占比",如果占比较高,用户的风险也较大。
  • 平常会将大量的时间和精力花在底层数据的衍生上,这个不仅需要对业务的理解,也需要一定的想象力进行头脑风暴,即使衍生出来的特征90%都效果不佳,但只要剩下的10%是好的特征,那对于模型效果的提升是很显著的。
  • 对于评分卡来说,特征需要好的解释能力,所以一些复杂的衍生方法,像特征交叉,log转换基本不会用到。但如果是xgboost等复杂模型,进行特征交叉等方法或许有比较好的效果。

Q:特征筛选的作用和目的?筛选的特征需要满足什么要求?

作用和目的:

  • 简化模型,增加模型的可解释性, 降低模型过拟合的风险。
  • 缩短模型的训练时间。
  • 避免维度灾难。

筛选特征满足的要求:

  • 具有良好的区分能力。
  • 可解释性好,与目标变量的关系在业务上能解释的通。
  • 在时间序列上有比较好的稳定性。
  • 特征的用户覆盖率符合要求。

Q:特征筛选的方法有哪些?每种方法的优缺点?实际工作中用到了哪些方法?

Filter(过滤法):按照发散性或者相关性对各个特征进行评分,设定阈值或者待选择阈值的个数,选择特征。

  • 相关系数,方差(适用于连续型变量),卡方检验(适用于类别型变量),信息熵,IV。实际工作中主要基于IV和相关性系数(皮尔逊系数)。
  • 优点:算法的通用性强;省去了分类器的训练步骤,算法复杂性低,因而适用于大规模数据集;可以快速去除大量不相关的特征,作为特征的预筛选器非常合适。
  • 缺点:由于算法的评价标准独立于特定的学习算法,所选的特征子集在分类准确率方面通常低于Wrapper方法。

Wrapper(封装法):封装式特征选择是利用学习算法的性能评价特征子集的优劣。因此,对于一个待评价的特征子集,Wrapper方法需要训练一个分类器,根据分类器的性能对该特征子集进行评价。

  • 方法有完全搜索(递归消除法),启发式搜索(前向/后向选择法,逐步选择法),随机搜索(训练不同的特征子集)。实际工作中主要用到启发式搜索,例如评分卡的逐步逻辑回归。
  • 优点:相对于Filter方法,Wrapper方法找到的特征子集分类性能通常更好。
  • 缺点:Wrapper方法选出的特征通用性不强,当改变学习算法时,需要针对该学习算法重新进行特征选择;由于每次对子集的评价都要进行分类器的训练和测试,所以算法计算复杂度很高,尤其对于大规模数据集来说,算法的执行时间很长。

Embedded(嵌入法):先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。类似于Filter方法,但是是通过训练来确定特征的优劣。

  • 一种是基于惩罚项,例如岭回归,lasso回归,L1/L2正则化。另一种是基于树模型输出的特征重要性,在实际工作中较为常用,可选择的模型有随机森林,xgboost,lightgbm。
  • 优点:效果最好速度最快,模式单调,快速并且效果明显。
  • 缺点:如何参数设置, 需要对模型的算法原理有较好的理解。

三.模型评估和优化

Q:简单介绍一下风控模型常用的评估指标。

  • 混淆矩阵指标:精准率,查全率,假正率。当模型最后转化为规则时,一般用这三个指标来衡量规则的有效性。要么注重精准率,要么注重查全率,两者不可兼而得之。
  • ROC曲线和AUC值,ROC曲线是一种对于查全率和假正率的权衡,具体方法是在不同阈值下以查全率作为纵轴,假正率作为横轴绘制出一条曲线。曲线越靠近左上角,意味着越多的正例优先于负例,模型的整体表现也就越好。AUC是ROC曲线下面的面积,AUC可以解读为从所有正例中随机选取一个样本A,再从所有负例中随机选取一个样本B,分类器将A判为正例的概率比将B判为正例的概率大的可能性。在对角线(随机线)左边的点上TPR总大于FPR,意为正例被判为正例的概率大于负例被判为正例的概率。从另一个角度看,由于画ROC曲线时都是先将所有样本按分类器的预测概率排序,所以AUC反映的是分类器对样本的排序能力。AUC越大,自然排序能力越好,即分类器将越多的正例排在负例之前。
  • KS:用于区分预测正负样本分隔程度的评价指标,KS越大,表示模型能将好坏样本区分开的程度越大。KS的绘制方法是先将每个样本的预测结果化为概率或者分数,将最低分到最高分(分数越低,坏的概率越大)进行排序做样本划分,横轴就是样本的累计占比,纵轴则是好坏用户的累计占比分布曲线,KS值为两个分布的最大差值(绝对值)。KS值仅能代表模型的区隔能力,KS不是越高越好,KS如果过高,说明好坏样本分的过于开了,这样整体分数(概率)就是比较极端化的分布状态,这样的结果基本不能用。
  • 基尼系数:其横轴是根据分数(概率)由高到低累计的好用户占总的好用户的比例,纵轴是分数(概率)从高到低坏用户占总的坏用户的比例。由于分数高者为低风险用户,所以累计坏用户比例的增长速度会低于累计好用户比例,因此,基尼曲线会呈现向下弯曲的形式,向下突出的半月形的面积除以下方三角形的面积即是基尼系数。基尼系数越大,表示模型对于好坏用户的区分能力越好。

Q:为什么ROC适合不平衡数据的评价?

  • ROC曲线的纵轴是TPR= \frac{TP}{TP+FN} ,横轴是FPR= \frac{FP}{FP+TN} ,TPR聚焦于正例,FPR聚焦于与负例,所以ROC兼顾了正样本和负样本的权衡,使其成为一个比较均衡的评估方法。
  • 因为TPR用到的TP和FN都是正样本,FPR用到的FP和TN都是负样本,所以说正样本或负样本发生了改变,TPR和FPR也不会相互影响,因此即使类别分布发生了改变,数据变得不平衡了,ROC曲线也不会产生大的变化。ROC曲线的优点,即具有鲁棒性,在类别分布发生明显改变的情况下依然能客观地识别出较好的分类器。

Q:AUC和KS的关系是什么?

img img

  • 左图是KS曲线,红色的是TPR曲线(累计正样本占比),蓝色的是FPR曲线(累计负样本占比)。由于按照正样本预测概率降序排列,所以排在前面的样本为正的概率更大,但为正的概率是递减的;相反排在前面的样本为负的概率更小,但为负的概率递增。所以KS图中,TPR曲线在FPR曲线上方,并且TPR曲线的导数递减,FPR曲线的导数递增,而KS曲线先上升到达峰值P点(导数为0)后下降,P点对应的C值就是KS值。ROC图中,ROC曲线的导数是递减的,且刚开始导数大于1,逐渐递减到导数为1的T点(T点对应P点),然后导数继续降低。另外,A值对应X值,B值对应Y值,且C=B-A=Y-X
  • 在用KS评估模型时,除了看P点对应的KS值C,还要看P点的横坐标F值的大小,F值表示的是将分数从低到高排序后的累计样本占比,F值越小,说明模型对正样本的预测越精确,也就是说在识别出正样本的同时也能保证对负样本更小的误杀率。
  • 假设F值不变,C值增大,即P点沿着垂直方向向上移动,那么A值应该减小,B值应该增大;对应地,X值减小,Y值增大,T点会向左上角移动;所以ROC曲线下方的面积会增大,也就是AUC值增大。
  • 假设C值不变,F值减小,即P点沿着水平方向向左移动,因为C=B-A,所以A和B减小相同的幅度,也是就说X和Y减小相同的幅度,即T点沿着斜率为1的切线方向向下移动,此时ROC曲线下方的面积也会增大,即AUC值增大。
  • 所以P点的位置决定了T点的位置,C值和F值均会影响AUC值。AUC值看上去更像一个综合评估指标,但缺乏对模型细节的评估。而KS值结合F值,可以评估每一段评分的效果,还可以找出评分切分的阈值等。

Q:什么是模型的欠拟合和过拟合?

  • 欠拟合指的是模型没有很好的捕捉到数据特征,不能很好的拟合数据。
  • 过拟合指的是模型把数据学习的太彻底,以至于把噪声数据学习进去了,这样模型在预测未知数据时,就不能正确的分类,模型的泛化能力太差。

Q:如何判断模型是否存在过拟合或欠拟合?对应的解决方法有哪些?

  • 判断模型是否存在过拟合/欠拟合主要用学习曲线,学习曲线指的是通过画出不同训练集大小时训练集和交叉验证的准确率,可以看到模型在新数据上的表现,进而来判断模型是否方差偏高(过拟合)或偏差过高(欠拟合)。当训练集和测试集的误差收敛但却很高时,即为欠拟合,当训练集和测试集的误差之间有大的差距时,为过拟合。
  • 解决欠拟合的方法:增加效果好的特征,添加多项式特征,减小正则化参数等。
  • 解决过拟合的方法:使用更多的数据,选择更加合适的模型,加入正则项等。

Q:什么是正则化?什么是L1正则化和L2正则化?

  • 正则化是在模型的loss function的基础上,加上了一些正则化项或者称为模型复杂度惩罚项,它会向学习算法略微做些修正,从而让模型能更好地泛化。这样反过来能提高模型在不可见数据上的性能。
  • L1正则化就是在loss function后边所加正则项为L1范数,加上L1范数容易得到稀疏解,所以L1正则化会趋向于产生少量的特征。
  • L2正则化就是loss function后边所加正则项为L2范数的平方,加上L2正则相比于L1正则来说,得到的解比较平滑(不是稀疏),所以L2正则化会使特征的解趋近于0,但不会为0。

Q:正则化为什么可以防止过拟合?

最简单的解释是正则化对模型参数添加了先验,在数据少的时候,先验知识可以防止过拟合。举个例子:抛一枚硬币5次,得到的全是正面,则得出结论:正面朝上的概率为1,这类似于模型的过拟合,如果加上硬币朝上的概率是0.5的先验,结果就不会这么离谱,这就是正则。

Q:什么是交叉验证?交叉验证的目的是什么?有哪些优点?

交叉验证概念:

交叉验证,就是重复的使用数据,把得到的样本数据进行切分,组合为不同的训练集和测试集,用训练集来训练模型,用测试集来评估模型预测的好坏。在此基础上可以得到多组不同的训练集和测试集,某次训练集中的某样本在下次可能成为测试集中的样本,即所谓"交叉"。

交叉验证的目的:

评估给定算法在特定数据集上训练后的泛化性能,比单次划分训练集和测试集的方法更加稳定,全面。

交叉验证的优点:

  • 如果只是对数据随机划分为训练集和测试集,假如很幸运地将难以分类的样本划分进训练集中,则在测试集会得出一个很高的分数,但如果不够幸运地将难以分类的样本划分进测试集中,则会得到一个很低的分数。所以得出的结果随机性太大,不够具有代表性。而交叉验证中每个样本都会出现在训练集和测试集中各一次,因此,模型需要对所有样本的泛化能力都很好,才能使其最后交叉验证得分,及其平均值都很高,这样的结果更加稳定,全面,具有说服力。
  • 对数据集多次划分后,还可以通过每个样本的得分比较,来反映模型对于训练集选择的敏感性信息。
  • 对数据的使用更加高效,可以得到更为精确的模型。

Q:交叉验证常用的方法有哪些?

  • 标准K折交叉验证:K是自定义的数字,通常取5或10,如果设为5折,则会训练5个模型,得到5个精度值。
  • 分层K折交叉验证:如果一个数据集经过标准K折划分后,在测试集上只有一种类别,则无法给出分类器整体性能的信息,这种情况用标准K折是不合理的。而在分层K折交叉验证中,每个折中的类别比例与整个数据集类别比例相同,这样能对泛化性能做出更可靠的估计。
  • 留一法交叉验证:每次划分时,把单个数据点作为测试集,如果数据量小,能得到更好的估计结果,数据量很大时则不适用。
  • 打乱划分交叉验证:每次划分数据时为训练集取样train_size个点,为测试集取样test_size个点,将这一划分划分方法重复n_splits次。这种方法还允许每次迭代中使用部分数据,可通过设置train_size和test_size之和不为0来实现,用这种方法对数据进行二次采样可能对大型数据上的试验很用用。另外也有分层划分的形式( StratifiedShuffleSplit),为分类任务提供更可靠的结果。
  • 分组交叉验证:适用于数据中的分组高度相关时,以group数组作为参数,group数组表示数据中的分组,在创建训练集和测试集的时候不应该将其分开,也不应该与类别标签弄混。

风控模型指标详解

1.P-R曲线

首先,明确两个概念,精确率§和召回率®。下表中,1代表正例,0代表负例。

在这里插入图片描述

P = T P P = T P T P + F P P=\frac{TP}{P}=\frac{TP}{TP+FP}P=PTP=T**P+FPT**P
R = T P R = T P T P + F N R=\frac{TP}{R}=\frac{TP}{TP+FN}R=RTP​=T**P+FNT**P

精确率是指分类正确的正样本个数占分类器判定为正样本个数的比例。它表示的是预测为正的样本中有多少是真正的正样本。
召回率是指分类正确的样本个数占真正的正样本个数的比例,它表示的是样本中的正例有多少被预测正确了。这里可以参看西瓜书,里面好瓜坏瓜的例子,比较容易理解。

Precision和Recall是既矛盾又统一的两个指标,为了提高Precision,分类器需要尽量在“更有把握”时才把样本预测为正样本,但此时往往会因为过于保守而漏掉很多“没有把握”的正样本,导致Recall很低。

接下来说回P-R曲线,横轴是召回率R,纵轴是精确率P。对于P-R曲线上的一点,表示在某一阈值下,模型将大于该阈值的结果判定为正样本,小于该阈值的结果判定为负样本,此时返回结果对应的P和R。一般的P-R曲线如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tOhuUphD-1575456885844)(./image/PR曲线.jpg)]
注意,只用某个点对应的精确率和召回率无法全面衡量模型性能。

2.ROC,AUC

介绍ROC之前,先看几个指标,假阳率(FPR)和真阳率(TPR)。

F P R = F P N = F P F P + T N FPR=\frac{FP}{N}=\frac{FP}{FP+TN}FPR=NFP=F**P+TNF**P
T P R = T P P = T P T P + F N TPR=\frac{TP}{P}=\frac{TP}{TP+FN}TPR=PTP​=T**P+FNT**P

P是真实的正样本数量,N是真实的负样本数量。TP是P个正样本中被分类器预测为正样本的个数,FP是N个负样本中被分类器预测为正样本的个数。
  可以这样记这两个公式,以TPR为例,分子就是TP,因为T意味着P(正)分类正确,所以分母为P(正类)。正类有两种,一种是预测为正类且预测正确(TP),一种是预测为负类但是预测错误(FN)。FPR同理,只是分母为N而已。
ROC曲线通过不断移动分类器的“截断点”来生成曲线上的一组关键点。
具体介绍见https://zhuanlan.zhihu.com/p/60218684(不想写了。。。)

P-R曲线和ROC曲线的区别

当正负样本的分布发生变化时,ROC曲线形状基本保持不变,P-R曲线会发生剧烈变化。但是,在正负样本分布极不均匀的情况下,P-R曲线比ROC曲线更能有效反应分类的好坏。

3.WOE.IV值

WOE和IV主要用来判断变量的预测强度,比如判断用户收入对用户是否会发生逾期的预测强度。因此,两个值的使用主要是在有监督的分类问题中,具体可以细化到如下方面:

指导变量离散化。在建模过程中,时常需要对连续变量进行离散化处理,如将年龄进行分段。但是变量不同的离散化结果(如:年龄分为[0-20]还是[0-15])会对模型产生不同影响。因此,可以根据指标所反应的预测强度,调整变量离散化结果。(对一些取值很多的分类变量,在需要时也可以对其进行再分组,实现降维。)

变量筛选。我们需要选取比较重要的变量加入模型,预测强度可以作为我们判断变量是否重要的一个依据。

WOE的全称是“Weight of Evidence”,即证据权重。WOE是对原始自变量的一种编码形式。要对一个变量进行WOE编码,需要首先把这个变量进行分组处理(也叫离散化、分箱等等,说的都是一个意思)。下面以german credit数据来解释WOE及后面的IV值,选取savings这个分类字段进行相应解释,此字段共有5个取值,每个取值代表一个分组,每个分组中好坏样本数如下,其中0代表未违约,1代表违约:

0(未违约)1(违约)总计
A61386217603
A626934103
A63521163
A6442648
A6515132183
总计7003001000

WOE公式如下:
W O E i = l n P ( y i ) P ( n i ) = l n y i y n i n WOE_i=ln\frac{P(y_i)}{P(n_i)}=ln\frac{\frac{y_i}{y}}{\frac{n_i}{n}}WOE**i​=lnP(n**i​)P(y**i​)​=lnnni​​yyi​​​
P(yi)代表第i组中,违约样本占所有违约样本的比例;
P(ni)代表第i组中,未违约样本占所有未违约样本比例;
y:所有违约样本数;
n:所有未违约样本数。
  经过变换,上述式子可以变为
W O E i = l n y i n i y n WOE_i=ln\frac{\frac{y_i}{n_i}}{\frac{y}{n}}WOE**i​=lnn**yn**iy**i​​​

0(未违约)1(违约)总计WOE
A613862176030.271
A6269341030.14
A63521163-0.706
A6442648-1.099
A6515132183-0.704
总计7003001000

以变量取A61时为例,对应的woe=ln((217/300)/(386/700))。 即每一组中坏样本比例除以好样本比例。在大于0部分(说明坏样本比例大于好样本比例),WOE越大,说明坏样本比例比好样本比例大得越多,即分组中存在坏样本的可能性越大;小于0部分(坏样本比例小于好样本比例),WOE越小,说明坏样本比例比好样本比例小得越多,即分组中存在好样本的可能性越大)。总结下来,WOE越小好样本可能性越大。

但是,WOE没有考虑分组中样本占整体样本的比例,如果一个分组的WOE值很高,但是样本数占整体样本数很低,则对变量整体预测的能力会下降。因此,我们还需要计算IV值。
  IV值考虑了分组中样本占整体样本的比例,相当于WOE的加权求和。具体计算公式如下:

I V i = ( P ( y i ) − P ( n i ) ) ∗ W O E i = ( y i y − n i n ) l n y i y n i n IV_i=(P(y_i)-P(n_i))WOE_i=(\frac{y_i}{y}-\frac{n_i}{n})ln\frac{\frac{y_i}{y}}{\frac{n_i}{n}}IVi=(P(y**i)−P*(n**i))∗WOE**i=(yyinni)lnnniyy**i

0(未违约)1(违约)总计WOEIV
A613862176030.2710.047
A6269341030.140.002
A63521163-0.7060.027
A6442648-1.0990.044
A6515132183-0.7040.077
总计70030010000.197

有了一个变量各分组的IV值,我们就可以计算整个变量的IV值,方法很简单,就是把各分组的IV相加:

I V = ∑ i = 1 n I V i IV=\sum_{i=1}^nIV_iI**V=i=1∑nIV**i

其中,n为变量分组个数。

IV值可以用来衡量自变量的预测能力。类似的指标还有信息增益、基尼系数等等。

4.K-S值

KS曲线是用来衡量分类型模型准确度的工具。KS曲线与ROC曲线非常的类似。KS曲线是两条线,其横轴是阈值,纵轴是TPR与FPR。两条曲线之间之间相距最远的地方对应的阈值,就是最能划分模型的阈值。

KS的计算步骤如下:

  1. 计算每个评分区间的好坏账户数。
  2. 计算每个评分区间的累计好账户数占总好账户数比率(good%)和累计坏账户数占总坏账户数比率(bad%)。
  3. 计算每个评分区间累计坏账户占比与累计好账户占比差的绝对值(累计good%-累计bad%),然后对这些绝对值取最大值即得此评分卡的K-S值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8nWcFOAe-1575456885845)(./image/K-S值.png)]
  K-S指标衡量的是好坏样本累计分部之间的差值。
好坏样本累计差异越大,KS指标越大,那么模型的风险区分能力越强。

KS曲线与ROC曲线的区别

KS曲线就是把ROC曲线由原先的一条曲线拆解成了两条曲线。原先ROC的横轴与纵轴都在KS中变成了纵轴,而横轴变成了不同的阈值。

5.PSI,CSI

PSI

PSI又叫作群体稳定性指标,常用来筛选特征变量、评估模型稳定性。公式如下:
P S I = ∑ i = 1 n ( A i − E i ) ∗ l n ( A i / E i ) PSI=\sum_{i=1}^n(A_i-E_i)ln(A_i/E_i)PSI=i=1∑n​(A**i​−Ei​)∗ln*(A**i​/E**i​)

A i A_iA**i表示实际占比,E i E_iE**i表示预期占比。

通常,以训练样本作为预期分布占比,验证样本作为实际分布占比。

ScoreA%E%A-EA/EPSI
0-20020%10%10%0.8750.0013
200-30030%20%10%0.80000.0045
300+50%70%-20%1.11110.00111
总计0.0069

PSI数值越小,两个分布之间的差异就越小,代表越稳定。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i2lb7dzw-1575456885846)(./image/PSI.jpg)]
  模型部署上线后,通常用PSI曲线报表观察模型的稳定性。通过保证入模变量稳定性来进行变量监控,保证模型分数稳定性来进行模型监控。

根据建模经验,给出一些建议:

  1. 实际评估需要分不同粒度:时间粒度(按月、按样本集)、订单层次(放贷层、申请层)、人群(若没有分群建模,可忽略)。
  2. 先在放贷样本上计算PSI,剔除不稳定的特征;再对申请样本抽样(可能数据太大),计算PSI再次筛选。之前犯的错误就是只在放贷样本上评估,后来在全量申请订单上评估时发现并不稳定,导致返工。
  3. 时间窗尽可能至今为止,有可能建模时间窗稳定,但近期时间窗出现
  4. PSI只是一个宏观的指标,建议先看变量数据分布(EDD),看分位数跨时间变化来检验数据质量。我们无法得知PSI上升时,数据分布是左偏还是右偏。因此,建议把PSI计算细节也予以保留,便于在模型不稳定时,第一时间排查问题。
    转自https://zhuanlan.zhihu.com/p/79682292

CSI

CSI又叫作特征稳定性指标,能帮助理解入模特征变量对模型分数波动的影响,以及背后的客群分布偏移原因。这对风控模型不稳定时追溯定位原因具有重要意义。
C S I = ∑ i = 1 n ( D i s t r _ A i − D i s t r _ E i ) ∗ p a r t i a l _ S c o r e i CSI=\sum_{i=1}^n(Distr_A_i-Distr_E_i)partial_Score_iCSI*=i=1∑n​(DistrA**i​−DistrE**i​)∗partial_Score**i

D i s t r _ A i Distr_A_iDistrA**i表示实际占比,D i s t r _ E i Distr_E_iDistrE**i表示预期占比。
关于CSI,注意以下几点:

  1. 符号为正,说明当前样本相对于开发样本往高分段偏移;符号为负,说明说明当前样本相对于开发样本往低分段偏移。
  2. CSI绝对值数值越大,特征稳定性越差。
  3. 不同变量间的CSI没有可比性。

PSI和CSI的区别

  1. 群体稳定性报告(Population Stability Report)是用模型分数层(score)来评估当前样本与开发样本之间的分布差异。目前业内有可以统一参考来判断稳定性的阈值标准。
  2. 特征稳定性报告(Characteristic Stability Report)是从入模特征层(characteristic)来分析当前样本与开发样本之间的分数差异,以及对最终模型分数的影响。目前没有统一参考来判断稳定性的阈值标准。

风控模型不稳定时的排查方向
  当通过PSI指标发现模型不稳定时,我们该如何去排查原因?引起模型不稳定的因素是多种多样的,主要包括:

申贷客群变化:获客渠道一般决定了客群质量,我们只是从客群的有限特征维度来大致判断是否变化,但这只是有偏判断,因为无法完全获知用户画像。当然,在获客阶段也会做前置风控,预先筛选流量,以及保证客群的稳定。
数据源不稳定:先从CSI指标观察入模特征的分数漂移,对于影响较大和偏移较大的变量予以重点关注。再从数据源上确认采集是否可靠,比如数据服务商是否正常提供、接口是否正常工作、网关数据传输过程是否正常等。
特征逻辑有误:在模型上线时,特征逻辑可能没有确认清楚,导致上线后出现意想不到的问题。因此,需要将入模特征的逻辑再次予以Review。
其他相关原因:模型监控报表是否正确计算?线上依赖于离线T+1产出的数据是否正常调度?特征缺失值处理逻辑?
转自https://zhuanlan.zhihu.com/p/86559671

6.Lift曲线

Lift是评估一个预测模型是否有效的一个度量;它衡量的是一个模型(或规则)对目标中“响应”的预测能力优于随机选择的倍数,以1为界线,大于1的Lift表示该模型或规则比随机选择捕捉了更多的“响应”,等于1的Lift表示该模型的表现独立于随机选择,小于1则表示该模型或规则比随机选择捕捉了更少的“响应”。Lift计算公式:
L i f t = T P T P + F P T P + F N T P + F P + T N + F N = 查 准 率 正 例 占 比 Lift=\frac{\frac{TP}{TP+FP}}{\frac{TP+FN}{TP+FP+TN+FN}}=\frac{查准率}{正例占比}Lif**t=T**P+F**P+T**N+FNT**P+F**NT**P+FPT**P​​=正例占比查准率​

Lift指标可以这样理解:在不使用模型的情况下,我们用先验概率估计正例的比例,即上式分母部分,以此作为正例的命中率;利用模型后,我们不需要从整个样本中来挑选正例,只需要从我们预测为正例的那个样本的子集{TP+FP}中挑选正例,这时正例的命中率为查准率 ,后者除以前者即可得提升值Lift。
下表是一个提升表(Lift Table)的示例:
在这里插入图片描述
  Decile表示分数段,Obs表示不使用评分卡,用随机选择方法覆盖到的坏客户占比,等价于该组观测数占总观测数的比例(分子分母同时乘以样本整体的坏账率)。Bad表示使用评分卡得到的坏客户占比。
以分数段为横轴,以提升度为纵轴,可绘制出累计提升图,示例如下:

在这里插入图片描述

Lift曲线的右半部分应该尽量陡峭,因为越陡峭说明低分段中的Bad占比越大,模型的区分能力越好。

世上最全的模型评价指标解析

分类模型评价指标

评价指标:

准确率 (Accuracy),混淆矩阵 (Confusion Matrix),精确率(Precision)即查准率,召回率(Recall)即查全率,灵敏度(sensitivity),特异度(specificity),TPR,, FPR,PR曲线,平均正确率(AP),mean Average Precision(mAP),交除并(IoU),ROC + AUC,非极大值抑制(NMS),F度量值(F-measure)即F-score,

1. 准确率 (Accuracy)

分对的样本数除以所有的样本数 ,即:准确(分类)率 = 正确预测的正反例数总数 / 所有样本总数。

准确率一般用来评估模型的全局准确程度,不能包含太多信息,无法全面评价一个模型性能。

2. 混淆矩阵 (Confusion Matrix

横轴模型预测的类别统计,纵轴是样本数据的真实标签的类别统计

TP,TN,FP,FN都是从模型预测的结果的角度来说,T即True,指的是预测类别与真实label一致,F即False,模型预测的类别与真实label不一致,请特别谨记这点。
在这里插入图片描述

对角线上TP,TN表示模型预测label和数据标签一致的数量,所以对角线之和除以测试集总数就是准确率。对角线上数字越大越好

True Positives: 真的正样本,正样本被模型预测为正样本

True Negatives:真的负样本,负样本被模型预测为负样本

False Positives:假的正样本,负样本被模型错误预测为正样本

False Negatives:假的负样本,正样本被模型错误预测为负样本

**TPR(图中tp rate): 它是所有实际正样本中,正确识别的正样本的比例,它和召回率的表达式没有区别, Recall(召回率)=sensitivity(灵敏性)=TPR=TP/(TP+FN),**真正率,可理解为正确的被判断为正确的

FPR(图中fp rate):它是所有实际负样本中,错误的识别为正样本的比例, FPR=FP/(FP+TN),假真率。

TNR 即specificity(特异性):它是所有实际负样本中,正确识别为负样本的比例,,specificity(特异性)= TN/(FP+TN)=1-FPR

3. 精确率(Precision)(即查准率)与召回率(Recall)即(查全率)

上图的具体我们通过韦恩图来解释更易理解,原有数据样本是整个长方形,左边长方形是正样本,右边是负样本。模型对样本预测后会出现四种结果,即图中四种颜色,圆区域是模型预测的正类,其他区域(即false negatives, true negatives)是模型预测的负类。里面绿色的半圆就是TP(True Positives), 红色的半圆就是FP(False Positives), 左边的灰色长方形(不包括绿色半圆),就是FN(False Negatives)。右边的 浅灰色长方形(不包括红色半圆),就是TN(True Negatives)。
在这里插入图片描述
精确率(Precision)的定义在上图可以看出,是绿色半圆除以红色绿色组成的圆。

精确率是针对我们预测结果而言的,它表示的是预测为正的样本中有多少是真正的正样本,指的是找的对。那么预测为正就有两种可能了,一种就是把正类预测为正类(TP),另一种就是把负类预测为正类(FP),也就是
在这里插入图片描述

召回率(Recall)的定义也在图上能看出,是绿色半圆除以左边的长方形。

召回率是针对我们原来的样本而言的,它表示的是样本中的正例有多少被预测正确了,指的是找的全。那也有两种可能,一种是把原来的正类预测成正类(TP),另一种就是把原来的正类预测为负类(FN)。
在这里插入图片描述

4. ROC曲线和AUC值

以TPR也即召回率recall为y轴,以FPR为x轴,我们就直接得到了ROC曲线
在这里插入图片描述

  • 横坐标:假正率(False positive rate, FPR),FPR = FP / [ FP + TN] ,代表所有负样本中错误预测为正样本的概率,假警报率;
  • 纵坐标:真正率(True positive rate, TPR),TPR = TP / [ TP + FN] ,代表所有正样本中预测正确的概率,命中率。即召回率。

当一个样本被分类器判为正例,若其本身是正例,则TPR增加;若其本身是负例,则FPR增加,因此ROC曲线可以看作是随着阈值的不断移动,所有样本中正例与负例之间的“对抗”。曲线越靠近左上角,意味着越多的正例优先于负例,模型的整体表现也就越好。

从FPR和TPR的定义可以理解,TPR越高,FPR越小,我们的模型和算法就越高效,从图上能看出,曲线越接近左上角,分类器的性能越好。从几何的角度讲,ROC曲线下方的面积越大越大,则模型越优。所以有时候我们用ROC曲线下的面积,即AUC值来作为算法和模型好坏的标准。

ROC曲线有个很好的特性:当测试集中的正负样本的分布变化的时候,ROC曲线基本不会产生大的变化(原因是TPR用到的TP和FN同属P列,FPR用到的FP和TN同属N列,所以即使P或N的整体数量发生了改变,也不会影响到另一列。也就是说,即使正例与负例的比例发生了很大变化,ROC曲线也不会产生大的变化,而像Precision使用的TP和FP就分属两列,则易受类别分布改变的影响。

ROC曲线绘制:

(1)根据每个测试样本属于正样本的概率值从大到小排序;

(2)从高到低,依次将每个样本的预测概率作为阈值threshold,即依次将每个样本划分为正例,当测试样本属于正样本的概率大于或等于这个threshold时,我们认为它为正样本 则TP+1,否则为负样本 则FP+1;

(3)每次选取一个不同的threshold,我们就可以得到一组FPR和TPR,即ROC曲线上的一点。

当我们将threshold设置为1 (即把所有样本均预测为反例)和0(即把所有样本均预测为正例)时,分别可以得到ROC曲线上的(0,0)和(1,1)两个点。将这些(FPR,TPR)对 连起来,就得到了ROC曲线。当threshold取值越多,ROC曲线越平滑。

AUC(Area Under Curve)即为ROC曲线下的面积。AUC越接近于1,分类器性能越好。

**物理意义:**首先AUC值是一个概率值,当你从所有正样本中随机挑选一个正样本以及再从所有负样本中随机选一个负样本,当前的分类算法根据计算得到的置信度Score值将这个正样本排在负样本前面的概率就是AUC值。当然,AUC值越大,当前的分类算法越有可能将正样本排在负样本前面,即能够更好的分类。由于画ROC曲线时都是先将所有样本按分类器的预测概率排序,所以AUC反映的是分类器对样本的排序能力

计算公式:就是求曲线下矩形面积。
在这里插入图片描述
AUC主要考察模型对正样本以及负样本的覆盖能力(即“找的全”)

5. IOU

IoU这一值,可以理解为系统预测出来的框与原来图片中标记的框的重合程度。 计算方法即检测结果Detection Result与 Ground Truth 的交集比上它们的并集,即为检测的准确率。
在这里插入图片描述
6. PR曲线

以精确率为y轴,以召回率为x轴,我们就得到了PR曲线。仍然从精确率和召回率的定义可以理解,精确率越高,召回率越高,我们的模型和算法就越高效。也就是画出来的PR曲线越靠近右上越好。
在这里插入图片描述

  • 横坐标:召回率recall ( FPR)即查全率,TPR = TP / [ TP + FN] ,代表所有正样本中预测正确的概率。

  • 纵坐标:精确率precision即查准率,P = TP / [ TP + FP] ,代表预测为正的样本中有多少是真正的正样本。

    PR曲线绘制

    这里我们用一张图片作为例子,多张图片道理一样。假设一张图片有M个需要检测的目标,分别是object1,object2,object3共分为三类,使用检测器得到了N个Bounding Box(BB),每个BB里包含BB所在的位置以及object1,object2,object3对应的分数confidence。

    (1),对每一类object i 进行如下操作:

    对模型检测出的N个bounding box中的每个box,计算其与真实M个ground truth(GT)的IOU值,且取其中最大值MaxIOU。设定一个阈值thresh,一般设置thresh为0.5。当MaxIoU < thresh的时候,记录其类别i的分数confidencei以及FPi = 1(表示假的正样本1个),当MaxIoU>=thresh分为以下俩种情况:

    当MaxIoU对应的GT类别为 i 的时候,记录其类别i的分数以及TPi = 1(表示真的正样本1个)。
    当MaxIoU对应的GT类别不为 i 的时候,记录其类别i的分数以及FPi = 1。

    (2),由步骤1我们可以得到N个分数与TP/FP的元组,形如(confidencei, TPi或者FPi),对这N个元组按照confidence进行排序(从大到小)。

    (3),按照顺序1,2,3,4。。。Ni截取(意思是当模型预测1个box,2个box,,,M个box,分别计算对应的recall和precision),计算每次截取所对应获得的recall和precision ,recall = TP总数/M, M是目标实例数量,precision = TP总数/(TP总数+FP总数) ,这里(TP总数+FP总数)即Ni的值,预测框数量,TP总数是当预测框为Ni时,预测的正样本数量。
    这样得到M个recall和precision点,便画出PR曲线了。

AP就是PR 曲线下面的面积,通常来说一个越好的分类器,AP值越高。

计算AP值,代码中实际上是利用微积分定义法(多个小矩形求和),对PR曲线与x轴求面积
​ 假设检测出了N个样本实例中有M个正例,那么我们会得到M个recall值(1/M, 2/M, …, M/M),对于每个recall值r,我们可以计算出对应(r’ > r)的最大precision,然后对这M个precision值取平均即得到最后的AP值。

假设从测试集中共检测出20个box 实例,而测试集中共有6个正例,则PR表如下:
在这里插入图片描述
mAP是多个类别AP的平均值。这个mean的意思是对每个类的AP再求平均,得到的就是mAP的值,mAP的大小一定在[0,1]区间,越大越好。该指标是目标检测算法中最重要的一个。

MAP主要考察模型对正样本的覆盖能力以及识别能力(即对正样本的“找的全”和“找的对”)

7、PR曲线和ROC曲线比较

PR曲线与ROC曲线的相同点是都采用了TPR (Recall)

ROC曲线特点:

(1)优点:当测试集中的正负样本的分布变化的时候,ROC曲线能够保持不变。因为TPR聚焦于正例,FPR聚焦于负例,使其成为一个比较均衡的评估方法。

在实际的数据集中经常会出现类不平衡(class imbalance)现象,即负样本比正样本多很多(或者相反),而且测试数据中的正负样本的分布也可能随着时间变化。

(2)缺点:上文提到ROC曲线的优点是不会随着类别分布的改变而改变,但这在某种程度上也是其缺点。因为负例N增加了很多,而曲线却没变,这等于产生了大量FP。像信息检索中如果主要关心正例的预测准确性的话,这就不可接受了。在类别不平衡的背景下,负例的数目众多致使FPR的增长不明显,导致ROC曲线呈现一个过分乐观的效果估计。ROC曲线的横轴采用FPR,根据FPR ,当负例N的数量远超正例P时,FP的大幅增长只能换来FPR的微小改变。结果是虽然大量负例被错判成正例,在ROC曲线上却无法直观地看出来。(当然也可以只分析ROC曲线左边一小段)

PR曲线:

(1)PR曲线使用了Precision,因此PR曲线的两个指标都聚焦于正例类别不平衡问题中由于主要关心正例,所以在此情况下PR曲线被广泛认为优于ROC曲线。

使用场景:

  1. ROC曲线由于兼顾正例与负例,所以适用于评估分类器的整体性能,相比而言PR曲线完全聚焦于正例。
  2. 如果有多份数据且存在不同的类别分布,比如信用卡欺诈问题中每个月正例和负例的比例可能都不相同,这时候如果只想单纯地比较分类器的性能且剔除类别分布改变的影响,则ROC曲线比较适合,因为类别分布改变可能使得PR曲线发生变化时好时坏,这种时候难以进行模型比较;反之,如果想测试不同类别分布下对分类器的性能的影响,则PR曲线比较适合。
  3. 如果想要评估在相同的类别分布下正例的预测情况,则宜选PR曲线。
  4. 类别不平衡问题中,ROC曲线通常会给出一个乐观的效果估计,所以大部分时候还是PR曲线更好。
  5. 最后可以根据具体的应用,在曲线上找到最优的点,得到相对应的precision,recall,f1 score等指标,去调整模型的阈值,从而得到一个符合具体应用的模型。

8、非极大值抑制(NMS)

Non-Maximum Suppression就是需要根据置信度score和预测box的坐标信息,从中找到置信度比较高的bounding box。对于有重叠在一起的预测框,只保留得分最高的那个。

(1)NMS计算出每一个bounding box的面积,然后根据score进行排序,把score最大的bounding box作为队列中首个要比较的对象;

(2)计算其余bounding box与当前最大score与box的IoU,去除IoU大于设定的阈值的bounding box,保留小的IoU得预测框;

(3)然后重复上面的过程,直至候选bounding box为空。

最终,检测了bounding box的过程中有两个阈值,一个就是IoU,另一个是在过程之后,从候选的bounding box中剔除score小于阈值的bounding box。需要注意的是:Non-Maximum Suppression一次处理一个类别,如果有N个类别,Non-Maximum Suppression就需要执行N次。

9、F-Measure

很多情况下,不管是查准率Precision还是查全率Recall均不能比较全面地对我们模型所进行评估。因此,人们提出了F度量值(F-measure)查全率查准率在非负权重β下的加权调和平均值(Weighted Harmonic Mean)
在这里插入图片描述

RFM模型原理介绍

为什么要使用RFM模型

假设你开了一家卖早餐的小店铺。有一个月你在做财务的时候,突然发现该月收入大幅度的下降。经过分析你发现,重要的部分用户被竞争对手给挖走了。(市场经验:公司收入的80%来自顶端的20%的用户)但当你在此时采取措施的时候,已经太晚了。
从上面的小栗子我们可以看出,对于流失用户的预测和了解用户行为,对不同类型的用户采取用户细分(user segmentation)是多么的重要!那么今天我们一起来学习一下【用户细分】的简单方法之一——RFM分析方法(模型)

RMF模型原理介绍

RFM是由三个英文单词的首字母组成的,即Recency(最近一次消费时间间隔)、Frequency(一段时间内的消费频率)和Monetary(一段时间内的消费金额)(RFM分析模型介绍)
(1) 最近一次消费时间间隔(R),上一次消费离得越近,R值越小,用户价值越高。
(2) 消费频率(F),购买的频率越高,F值越大,用户价值越高
(3) 消费金额(M),消费金额越高,M值越大,用户价值越高。
每一个指标都有高-低两个值,222之后,我们得到了以下八类用户。
RFM模型图
矩阵大家看着有点头疼,那我们来看看表格的!通俗易懂。
用户分类规则
注意:这里的高不是指值的大小,指的是用户的价值。容易搞错的就是“R”,这里的“高”不是指其最近一次消费的时间间隔长,而是指其用户价值高(最近一次消费的时间间隔低
简单分析一下分类:
(1) 根据消费金额的高低,分为“重要用户”和“一般用户”(果真“有钱”就是不一样)
(2) 如果R高(最近一次消费时间间隔低)、F高(一段时间内消费频率高)则是“价值用户
(3) 如果R高(最近一次消费时间间隔低)、F低(一段时间内消费频率低)则是“发展用户
(4) 如果R低(最近一次消费时间间隔大)、F高(一段时间内消费频率高)则是“保持用户
(5) 如果R低(最近一次消费时间间隔大)、F低(一段时间内消费频率低)则是“挽留用户
以上就是RFM模型的介绍,那么我们该如何将RFM模型运用到我们的运营中呢~

RFM模型用户细分

从刚开始的案例我们可知,RFM模型的目标是进行用户细分,之后对用户采取精细化的运营。那么我们粗略的看一下不同类别的用户,我们该采取什么样的运营方法吧!(为啥是粗略的?因为各个行业不同的业务采取的手段不一样啊)
(1) 重要价值用户,RFM三个值都很高,需要提供VIP服务
(2) 重要发展用户,消费频率低,但是其他两个值比较高,那么需要想办法提高他的消费频率
(3) 重要保持用户,最近消费距离现在时间比较远(F值低),但是消费频次和消费金额高。这种用户一般是一段时间没来的忠实用户,我们需要主动与他保持联系,提高复购率
(4) 重要挽留用户,最近消费时间距离现在比较远,消费的频率也比较低,但是消费的金额比较多。这种用户,即将流失,需要主动联系,调查清楚哪里出现了问题,想办法挽留。
我们用以下的表格图来清晰的展示一下精细化运用的小思路!
用户精细化运营

用户行为分析模型——RFM模型

1. RFM模型

​ RFM模型根据客户活跃程度和交易金额的贡献,进行客户价值细分的一种方法。

  • R(Recency)——最近一次交易时间间隔。基于最近一次交易日期计算的得分,距离当前日期越近,得分越高。如5分制。反映客户交易活跃度。
  • F(Frequency)——客户在最近一段时间内交易次数。基于交易频率计算的得分,交易频率越高,得分越高。如5分制。反映客户交易活跃度。
  • M(Monetray)——客户最近一段时间内交易金额。基于交易金额计算的得分,交易金额越高,得分越高。如5分制。反映客户价值。
    RFM总分值:R F M = R S ∗ 100 + F S ∗ 10 + M S ∗ 1 RFM=RS100+FS10+MS1RFM*=R**S∗100+F**S∗10+M**S∗1

RFM分析的主要作用:

  • 识别优质客户
  • 可以制定个性化的沟通和营销服务,为更多的营销决策提供有力支持。
  • 能够衡量客户价值和客户利润创收能力。

RFM的假设前提:
​ 假设交易的可能性:

  • 最近交易过的客户 > 最近没有交易过的
  • 交易频率高的客户 > 交易频率低的
  • 交易金额大的客户 > 交易金额小的

2. RFM模型分析应用

​ RFM分析应用为客户分组,即将三个指标分别分为“高”和“低”两种,高于均值的为“高”,低于均值的为“低”。因此有三件事要做:计算出各个指标得分的平均值;将各个变量高于平均分的定义为“高”,低于平均分的定义为“低”;根据三个变量“高”“低”的组合来定义客户类型;如“高”“高”“高”为高价值客户。部分结果如下图:

在这里插入图片描述
通过RFM方法,根据用户的属性数据分析,对用户进行了归类。在推送、转化等很多过程中,可以更加精准化,不至于出现用户反感的情景,更重要的是,对产品转化等商业价值也有很大的帮助。(当然也可以对RFM进行再聚类)
企业用R、F的变化,可以推测客户消费的异动状况,根据客户流失的可能性,列出客户,再从M(消费金额)的角度来分析,就可以把重点放在贡献度高且流失机会也高的客户上,重点拜访或联系,以最有效的方式挽回更多的商机。

应用
比如对圈用户群发短信转化只有不到1%时,你可以用RFM做个分析,只选取R值高的用户(最近2周到最近一个月内消费的用户),转化率可以由1%提升到10%。

这也意味着,以往6元/订单将下降到0.6元/订单。掌柜们是愿意花600元给10000个用户发短信,得到100个订单,还是愿意花48元给800人发短信得到80个订单,相信大家一定会选后者。

而整体的RFM区分,则能够帮掌柜们针对不同的用户发不同的短信,短信的开头是用“好久不见”、还是用“恭喜你成为VIP”,就得看时重要保持客户还是重要价值用户了。只有能区分用户,才能走向精细化运营。

作用:

A:RFM模型较为动态地层示了一个会员的全部轮廓,这对个性化的沟通和服务提供了依据。

B:如果与该会员打交道的时间足够长,也能够较为精确地判断该会员的长期价值(甚至是终身价值),通过改善三项指标的状况,从而为更多的营销决策提供支持。

二.RFM模型客户细分方法

细分步骤:

①将所有客戶按照Recency的值,由小排列到大;前20%的客戶给5分,次20%的客戶给4分,以此类推,最后的20%给1分。

②再将所有客戶按照Frequency的值,由大排列到小;以20%为一群,依序给予5,4,3,2,1分。

③最后将所有客戶按照Monetary的值,由大排列到小;以20%为一群,依序给予5,4,3,2,1分。

RFM的整合:
5-5-5:最好的顾客;

1-1-1:遗弃的客户;

5-1-1:需要挖掘的潜在客户;

1-1-5:运营人员重点维护的高价值客户。

将RFM分别分为5个等级会得到125种组合,计算RFM时,应该根据客户数据特点灵活运用。如果客户人数少,可以减少分级。

三.RFM模型的应用意义

在众多的客户关系管理(CRM)的分析模式中,RFM模型是被广泛提到的。RFM模型是衡量客户价值和客户创利能力的重要工具和手段。该模型通过一个客户的近期购买行为、购买的总体频率以及花了多少钱三项指标来描述该客户的价值状况。

RFM模型较为动态地层示了一个客户的全部轮廓,这对个性化的沟通和服务提供了依据,同时,如果与该客户打交道的时间足够长,也能够较为精确地判断该客户的长期价值(甚至是终身价值),通过改善三项指标的状况,从而为更多的营销决策提供支持。

RFM非常适用于生产多种商品的卖家,而且这些商品单价相对不高,如消费品、化妆品、服装、零食等;它也适合在一个企业内只有少数耐久商品,但是该商品中有一部分属于消耗品,如面膜、尿不湿、零食等消耗品

RFM可以用来提高客户的交易次数。业界常用的EDM和短信,常常批量滥发,不仅费钱而且效果很差。根据统计(以一般邮购日用品而言),如果将所有R(Resency)的客户分为五级,最好的第五级转换率是第四级的三倍,因为这些客户刚完成交易不久,所以会更注意店铺促销信息。如果用M(Monetary)来把客户分为五级,最好与次好的平均转化率,几乎没有显著差异。

有些人会用客户绝对贡献金额来分析客户是否流失,但是绝对金额有时会曲解客户行为。因为每个商品价格可能不同,对不同产品的促销有不同的折扣,所以采用相对的分级(例如R、F、M都各分为五级)来比较消费者在级别区间的变动,则更可以显现出相对行为。企业用R、F的变化,可以推测客户消费的异动状况,根据客户流失的可能性,列出客户,再从M(消费金额)的角度来分析,就可以把重点放在贡献度高且流失机会也高的客户上,重点拜访或联系,以最有效的方式挽回更多的商机。

RFM也不可以用过头,而造成高交易的客户不断收到短信。每一家店铺应该设计一个客户接触频率规则,如购买三天或一周内应该发出一个感谢的电话或Email,并主动关心消费者是否有使用方面的问题,一个月后发出使用是否满意的询问,而三个月后则提供交叉销售的建议,并开始注意客户的流失可能性,不断地创造主动接触客户的机会。这样一来,客户再购买的机会也会大幅提高。

企业在推行CRM时,就要根据RFM模型的原理,了解客户差异,并以此为主轴进行企业流程重建,才能创新业绩与利润。否则,将无法在新世纪的市场立足。

*一、* *什么是用户画像*

​ 用户画像是指根据用户的属性、用户偏好、生活习惯、用户行为等信息而抽象出来的标签化用户模型。通俗说就是给用户打标签,而标签是通过对用户信息分析而来的高度精炼的特征标识。通过打标签可以利用一些高度概括、容易理解的特征来描述用户,可以让人更容易理解用户,并且可以方便计算机处理。

img

用户画像是对现实世界中用户的建模,用户画像应该包含目标,方式,组织,标准,验证这5个方面。

**目标:**指的是描述人,认识人,了解人,理解人。

**方式:**又分为非形式化手段,如使用文字、语言、图像、视频等方式描述人;形式化手段,即使用数据的方式来刻画人物的画像。

**组织:**指的是结构化、非结构化的组织形式。

**标准:**指的是使用常识、共识、知识体系的渐进过程来刻画人物,认识了解用户。

**验证:**依据侧重说明了用户画像应该来源事实、经得起推理和检验。

img

​ 在产品早期和发展期,会较多地借助用户画像,帮助产品人员理解用户的需求,想象用户使用的场景,产品设计从为所有人做产品变成为三四个人做产品,间接的降低复杂度。

*二、* *用户画像的作用*

在互联网、电商领域用户画像常用来作为精准营销、推荐系统的基础性工作,其作用总体包括:

(1)精准营销:根据历史用户特征,分析产品的潜在用户和用户的潜在需求,针对特定群体,利用短信、邮件等方式进行营销。

(2)用户统计:根据用户的属性、行为特征对用户进行分类后,统计不同特征下的用户数量、分布;分析不同用户画像群体的分布特征。

(3)数据挖掘:以用户画像为基础构建推荐系统、搜索引擎、广告投放系统,提升服务精准度。

(4)服务产品:对产品进行用户画像,对产品进行受众分析,更透彻地理解用户使用产品的心理动机和行为习惯,完善产品运营,提升服务质量。

(5)行业报告&用户研究:通过用户画像分析可以了解行业动态,比如人群消费习惯、消费偏好分析、不同地域品类消费差异分析

​ 根据用户画像的作用可以看出,用户画像的使用场景较多,用户画像可以用来挖掘用户兴趣、偏好、人口统计学特征,主要目的是提升营销精准度、推荐匹配度,终极目的是提升产品服务,起到提升企业利润。用户画像适合于各个产品周期:从新用户的引流到潜在用户的挖掘、从老用户的培养到流失用户的回流等。

img

​ 总结来说,用户画像必须从实际业务场景出发,解决实际的业务问题,之所以进行用户画像,要么是获取新用户,要么是提升用户体验、或者挽回流失用户等具有明确的业务目标。

imgimgimg

​ 另外关于用户画像数据维度的问题,并不是说数据维度越丰富越好,总之,画像维度的设计同样需要紧跟业务实际情况进行开展。

*三、* *用户画像的分类*

​ 从画像方法来说,可以分为定性画像、定性+定量画像、定量画像

img

​ 从应用角度来看,可以分为行为画像、健康画像、企业信用画像、个人信用画像、静态产品画像、旋转设备画像、社会画像和经济画像等。

*四、* *用户画像需要用到哪些数据*

​ 一般来说,根据具体的业务内容,会有不同的数据,不同的业务目标,也会使用不同的数据。在互联网领域,用户画像数据可以包括以下内容:

(1)人口属性:包括性别、年龄等人的基本信息

(2)兴趣特征:浏览内容、收藏内容、阅读咨询、购买物品偏好等

(3)消费特征:与消费相关的特征

(4)位置特征:用户所处城市、所处居住区域、用户移动轨迹等

(5)设备属性:使用的终端特征等

(6)行为数据:访问时间、浏览路径等用户在网站的行为日志数据

(7)社交数据:用户社交相关数据

img

​ 用户画像数据来源广泛,这些数据是全方位了解用户的基础,这里以Qunar的画像为例,其画像数据主要维度如下所示,包括用户RFM信息、航线信息等。

img

​ Qunar的画像数据仓库构建都是基于Qunar基础数据仓库构建,然后按照维度进行划分。

img

*五、* *用户画像主要应用场景*

a)用户属性

b)用户标签画像

c)用户偏好画像

d)用户流失

e)用户行为

f)产品设计

g) 个性化推荐、广告系统、活动营销、内容推荐、兴趣偏好

img

*六、* *用户画像使用的技术方法*

*七、* *用户画像标签体系的建立*

1、什么是标签体系

​ 用户画像是对现实用户做的一个数学模型,在整个数学模型中,核心是怎么描述业务知识体系,而这个业务知识体系就是本体论,本体论很复杂,我们找到一个特别朴素的实现,就是标签。

​ 标签是某一种用户特征的符号表示。是一种内容组织方式,是一种关联性很强的关键字,能方便的帮助我们找到合适的内容及内容分类。(注:简单说,就是你把用户分到多少个类别里面去,这些类是什么,彼此之间有什么关系,就构成了标签体系)

​ 标签解决的是描述(或命名)问题,但在实际应用中,还需要解决数据之间的关联,所以通常将标签作为一个体系来设计,以解决数据之间的关联问题。

​ 一般来说,将能关联到具体用户数据的标签,称为叶子标签。对叶子标签进行分类汇总的标签,称为父标签。父标签和叶子标签共同构成标签体系,但两者是相对概念。例如:下表中,地市、型号在标签体系中相对于省份、品牌,是叶子标签。

一级标签二级标签三级标签四级标签
移动属性用户所在地省份地市
手机品牌品牌型号
业务属性用户等级普通
音乐普通会员
音乐高级会员
音乐VIP会员

​ 用户画像标签体系创建后一般要包含以下几个方面的内容

img

(1)标签分类

​ 用户画像标签可以分为基础属性标签和行为属性标签。

img

​ 由于基于一个目标的画像,其标签是在动态扩展的,所以其标签体系也没有统一的模板,在大分类上,与自身的业务特征有很大的关联,在整体思路上可以从横纵两个维度展开思考:横向是产品内数据和产品外数据,纵向是线上数据和线下数据。而正中间则是永恒不变的“人物基础属性”。

​ 如果说其他的分类因企业特征而定,那么只有人物特征属性(至于名字叫什么不重要,关键是内涵)是各家企业不能缺失的板块。

​ 所谓人物基础属性指的是:用户客观的属性而非用户自我表达的属性,也就是描述用户真实人口属性的标签。所谓非“自我表达”,举例来说,某产品内个人信息有性别一项,用户填写为“女”,而通过用户上传的身份证号,以及用户照片,用户购买的产品,甚至用户打来的客服电话,都发现该用户性别是“男性”。那么在人物基础属性中的性别,应该标识的是“男性”,但是用户信息标签部分,自我描述的性别则可能标注为女性。

(2)标签级别(标签的体系结构)

​ 分级有两个层面的含义,其一是:指标到最低层级的涵盖的层级;其二是指:指标的运算层级。其一非常好理解,这里重点说运算层级。

​ 标签从运算层级角度可以分为三层:事实标签、模型标签、预测标签。

​ 事实标签:是通过对于原始数据库的数据进行统计分析而来的,比如用户投诉次数,是基于用户一段时间内实际投诉的行为做的统计。

​ 模型标签:模型标签是以事实标签为基础,通过构建事实标签与业务问题之间的模型,进行模型分析得到。比如,结合用户实际投诉次数、用户购买品类、用户支付的金额等,进行用户投诉倾向类型的识别,方便客服进行分类处理。

​ 预测标签:则是在模型的基础上做预测,比如针对投诉倾向类型结构的变化,预测平台舆情风险指数。

img

(3)标签命名&赋值

​ 我们用一张图来说明一下命名和赋值的差别,只要在构建用户标签的过程种,有意识的区别标签命名和赋值足矣,不再赘述。

img

(4)标签属性

​ 标签属性可以理解为针对标签进行的再标注,这一环节的工作主要目的是帮助内部理解标签赋值的来源,进而理解指标的含义。如图所示,可以总结为5种来源:

1、固有属性:是指这些指标的赋值体现的是用户生而有之或者事实存在的,不以外界条件或者自身认知的改变而改变的属性。比如:性别、年龄、是否生育等。

2、推导属性:由其他属性推导而来的属性,比如星座,我们可以通过用户的生日推导,比如用户的品类偏好,则可以通过日常购买来推导。

3、行为属性:产品内外实际发生的行为被记录后形成的赋值,比如用户的登陆时间,页面停留时长等。

4、态度属性:用户自我表达的态度和意愿。比如说我们通过一份问卷向用户询问一些问题,并形成标签,如询问用户:是否愿意结婚,是否喜欢某个品牌等。当然在大数据的需求背景下,利用问卷收集用户标签的方法效率显得过低,更多的是利用产品中相关的模块做了用户态度信息收集。

5、测试属性:测试属性是指来自用户的态度表达,但并不是用户直接表达的内容,而是通过分析用户的表达,结构化处理后,得出的测试结论。比如,用户填答了一系列的态度问卷,推导出用户的价值观类型等。

img

​ 值得注意的是,一种标签的属性可以是多重的,比如:个人星座这个标签,既是固有属性,也是推导属性,它首先不以个人的意志为转移,同时可以通过身份证号推导而来。

​ 即便你成功了建立用户画像的标签体系,也不意味着你就开启了用户画像的成功之路,因为有很大的可能是这些标签根本无法获得,或者说无法赋值。

​ 标签无法赋值的原因有:数据无法采集(没有有效的渠道和方法采集到准确的数据,比如用户身份证号)、数据库不能打通、建模失败(预测指标无法获得赋值)等等。

2、标签体系结构

​ 标签体系可以归纳出如下的层级结构。

img

(1)原始输入层

​ 主要指用户的历史数据信息,如会员信息、消费信息、网络行为信息。经过数据的清洗,从而达到用户标签体系的事实层。

(2)事实层

​ 事实层是用户信息的准确描述层,其最重要的特点是,可以从用户身上得到确定与肯定的验证。如用户的人口属性、性别、年龄、籍贯、会员信息等。

(3)模型预测层

​ 通过利用统计建模,数据挖掘、机器学习的思想,对事实层的数据进行分析利用,从而得到描述用户更为深刻的信息。如通过建模分析,可以对用户的性别偏好进行预测,从而能对没有收集到性别数据的新用户进行预测。还可以通过建模与数据挖掘,使用聚类、关联思想,发现人群的聚集特征。

(4)营销模型预测

​ 利用模型预测层结果,对不同用户群体,相同需求的客户,通过打标签,建立营销模型,从而分析用户的活跃度、忠诚度、流失度、影响力等可以用来进行营销的数据。

(5)业务层

​ 业务层可以是展现层。它是业务逻辑的直接体现,如图中所表示的,有车一族、有房一族等。

3、标签体系结构分类

​ 一般来说,设计一个标签体系有3种思路,分别是:(1)结构化标签体系;(2)半结构化标签体系;(3)非结构化标签体系。

(1)结构化标签体系

​ 简单地说,就是标签组织成比较规整的树或森林,有明确的层级划分和父子关系。结构化标签体系看起来整洁,又比较好解释,在面向品牌广告井喷时比较好用。性别、年龄这类人口属性标签,是最典型的结构化体系。下图就是Yahoo!受众定向广告平台采用的结构化标签体系。

img

(2)半结构化标签体系

​ 在用于效果广告时,标签设计的灵活性大大提高了。标签体系是不是规整,就不那么重要了,只要有效果就行。在这种思路下,用户标签往往是在行业上呈现出一定的并列体系,而各行业内的标签设计则以“逮住老鼠就是好猫”为最高指导原则,切不可拘泥于形式。下图是Bluekai聚合多家数据形成的半结构化标签体系。

img

(3)非结构化标签体系

​ 非结构化,就是各个标签就事论事,各自反应各自的用户兴趣,彼此之间并无层级关系,也很难组织成规整的树状结构。非结构化标签的典型例子,是搜索广告里用的关键词。还有Facebook用的用户兴趣词。

*4、**用户画像标签层级的建模方法*

​ 用户画像的核心是标签的建立,用户画像标签建立的各个阶段使用的模型和算法如下图所示。

img

img

原始数据层。对原始数据,我们主要使用文本挖掘的算法进行分析如常见的TF-IDF、TopicModel主题模型、LDA 等算法,主要是对原始数据的预处理和清洗,对用户数据的匹配和标识。

事实标签层。通过文本挖掘的方法,我们从数据中尽可能多的提取事实数据信息,如人口属性信息,用户行为信息,消费信息等。其主要使用的算法是分类和聚类。分类主要用于预测新用户,信息不全的用户的信息,对用户进行预测分类。聚类主要用于分析挖掘出具有相同特征的群体信息,进行受众细分,市场细分。对于文本的特征数据,其主要使用相似度计算,如余弦夹角,欧式距离等。

模型标签层。使用机器学习的方法,结合推荐算法。模型标签层完成对用户的标签建模与用户标识。其主要可以采用的算法有回归,决策树,支持向量机等。通过建模分析,我们可以进一步挖掘出用户的群体特征和个性权重特征,从而完善用户的价值衡量,服务满意度衡量等。

预测层。也是标签体系中的营销模型预测层。这一层级利用预测算法,如机器学习中的监督学习,计量经济学中的回归预测,数学中的线性规划等方法。实习对用户的流失预测,忠实度预测,兴趣程度预测等等,从而实现精准营销,个性化和定制化服务。

不同的标签层级会考虑使用对其适用的建模方法,对一些具体的问题,有专门的文章对其进行研究。

*八、* *用户画像基本步骤*[F2]

​ 根据具体业务规则确定用户画像方向后,开展用户画像分析,总体来说,一个用户画像流程包括以下三步。(1)用户画像的基本方向;(2)用户数据收集;(3)用户标签建模。

img

​ 另外,需要注意的是用户画像的时效性,构建画像的数据多为历史数据,但用户的行为、偏好等特征多会随着时间的推移而发生变化。

*九、* *用户画像验证*

img

*十、* *用户画像的实际例子*

​ 注:此处涉及到工作中的项目内容,由于保密,就不能分享了

*十一、* *用户画像平台**&**架构***

用户画像平台需要实现的功能。

img

​ 用户画像系统技术架构

img

(1) 数据处理

a、数据指标的梳理来源于各个系统日常积累的日志记录系统,通过sqoop导入hdfs,也可以用代码来实现,比如spark的jdbc连接传统数据库进行数据的cache。还有一种方式,可以通过将数据写入本地文件,然后通过sparksql的load或者hive的export等方式导入HDFS。

b、通过hive编写UDF 或者hiveql根据业务逻辑拼接ETL,使用户对应上不同的用户标签数据(这里的指标可以理解为每个用户打上了相应的标签),生成相应的源表数据,以便于后续用户画像系统,通过不同的规则进行标签宽表的生成。

(2) 数据平台

a、数据平台应用的分布式文件系统为Hadoop的HDFS,因为Hadoop2.0以后,任何的大数据应用都可以通过ResoureManager申请资源,注册服务。比如(sparksubmit、hive)等等。而基于内存的计算框架的出现,就并不选用Hadoop的MapReduce了。当然很多离线处理的业务,很多人还是倾向于使用Hadoop,但是Hadoop封装的函数只有map和Reduce太过单一,而不像spark一类的计算框架有更多封装的函数(可参考博客spark专栏)。可以大大提升开发效率。

b、计算的框架选用Spark以及RHadoop,这里Spark的主要用途有两种,一种是对于数据处理与上层应用所指定的规则的数据筛选过滤,(通过Scala编写spark代码提交至sparksubmit)。一种是服务于上层应用的SparkSQL(通过启动spark thriftserver与前台应用进行连接)。 RHadoop的应用主要在于对于标签数据的打分,比如利用协同过滤算法等各种推荐算法对数据进行各方面评分。

c、MongoDB内存数据的应用主要在于对于单个用户的实时的查询,也是通过对spark数据梳理后的标签宽表进行数据格式转换(json格式)导入mongodb,前台应用可通过连接mongodb进行数据转换,从而进行单个标签的展现。(当然也可将数据转换为Redis中的key value形式,导入Redis集群)

d、mysql的作用在于针对上层应用标签规则的存储,以及页面信息的展现。后台的数据宽表是与spark相关联,通过连接mysql随后cache元数据进行filter、select、map、reduce等对元数据信息的整理,再与真实存在于Hdfs的数据进行处理。

(3) 面向应用

从刚才的数据整理、数据平台的计算,都已经将服务于上层应用的标签大宽表生成。(用户所对应的各类标签信息)。那么前台根据业务逻辑,勾选不同的标签进行求和、剔除等操作,比如本月流量大于200M用户(标签)+本月消费超过100元用户(标签)进行和的操作,通过前台代码实现sql的拼接,进行客户数目的探索。这里就是通过jdbc的方式连接spark的thriftserver,通过集群进行HDFS上的大宽表的运算求count。(这里要注意一点,很多sql聚合函数以及多表关联join 相当于hadoop的mapreduce的shuffle,很容易造成内存溢出,相关参数调整可参考本博客spark栏目中的配置信息)这样便可以定位相应的客户数量,从而进行客户群、标签的分析,产品的策略匹配从而精准营销。

*十二、**用户画像困难点、用户画像瓶颈*

用户画像困难点主要表现为以下4个方面

img

资料搜集和数据挖掘

​ 在画像之前需要知道产品的用户特征和用户使用产品的行为等因素,从而从总体上掌握对用户需求需求

​ 创建用户画像不是抽离出典型进行单独标签化的过程,而是要融合边缘环境的相关信息来进行讨论

定量调研分析

用户标签画像

我们的用户标签包含基本特征、社会身份、顾客用户生命周期、类目偏好等等。比如说你怎么判断一个人是不是对女装感兴趣,假设我们有一个类目就是女装,那很好办,如果你购买都是女装,那会认为你这个人对女装比较感兴趣。

挑战

我们期间遇到了两方面的挑战:

亿级画像系统实践和应用

记录和存储亿级用户的画像,支持和扩展不断增加的维度和偏好,毫秒级的更新,支撑个公司性化推荐、广告投放和精细化营销等产品。

构建电商用户画像技术和流程

构建一个用户画像,包括数据源端数据收集、数据预处理、行为建模、构建用户画像

有些标签是可以直接获取到的,有些标签需要通过数据挖掘分析到!

5.2 源数据分析

用户数据分为2类:动态信息数据、静态信息数据

静态信息数据来源:

Ø 用户填写的个人资料,或者由此通过一定的算法,计算出来的数据

Ø 如果有不确定的,可以建立模型来判断,比如用户的性别注册没有填写,可以建立模型,根据用户的行为来判断用户性别是什么,或者它的概率

动态信息数据来源:

Ø 用户行为产生的数据:注册、游览、点击、购买、签收、评价、收藏等等。

Ø 用户比较重要的行为数据:游览商品,收藏商品、加入购物车、关注商品

根据这些行为特性可以计算出:用户注册时间、首单时间、潮妈族、纠结商品、最大消费、订单数量、退货数量、败家指数、品牌偏好等等。

5.3 目标分析

用户画像的目标是通过分析用户行为,最终为每个用户打上标签,以及该标签的权重。

如,红酒 0.8、李宁 0.6。

标签:表现了内容,用户对该内容有兴趣、偏好、需求等等。

权重:表现了指数,用户的兴趣、偏好指数,也可能表现用户的需求度,可以简单的理解为可信度,概率。

5.4 用户画像建模

5.4.1 用户基本属性表

根据用户所填写的属性标签和推算出来的标签。用于了解用户的人口属性的基本情况和按不同属性维度统计。

作用:按人口属性营销、比如营销80后,对金牛座的优惠,生日营销。

主要数据来源:用户表、用户调查表、孕妇模型表、马甲模型表。

用户表:记录用户最基本的属性特性。

用户调查表:补充用户的其他基本信息。

用户所填写的基本信息:用户ID、用户名、密码、性别、手机号、邮箱、年龄、户籍省份、身份证编号、注册时间、收货地址等

用户所填信息计算得到的指标:

生日、星座、城市等级、手机前几位、手机运营商、邮件运营商

用户调查表得到:学历、收入、职业、婚姻、是否有小孩、是否有车有房、使用手机品牌。

根据算法得到:

身高、体重、性别模型、孩子性别概率、潜在汽车用户概率、是否孕妇、孩子年龄概率、手机品牌、更换手机频率、是否有小孩,是否有车,使用手机档次,疑似马甲标准、疑似马甲账号数、用户忠诚度、用户购物类型。

模型算法—性别模型

Ø 用户自己也填写了性别,但仍然要用算法算一次性别

用户性别1男 0女 -1未识别1、商品性别得分2、用户购买上述商品计算用户性别等得分3、最优化算法训练阀值,根据阀值判断
孩子性别0 仅有男孩1仅有女孩2男女都有3无法识别1、选择男孩女孩商品2、确定用户购买商品的男女性别比例3、训练阀值,判断孩子性别,同用户性别类似

Ø 性别验证方法

随机抽样几千条数据让客户打电话确认。

与用户自己填的性别做对比,确认百分比。

模型算法—用户汽车模型

用户是否有车1有 0 没有-1 未识别根据用户购买车相关产品判断用户是否有车
潜在汽车用户1有-1 未识别用户游览或者搜索汽车用户数据判断

模型算法—用户忠诚度模型

Ø 忠诚度越高的用户越多,对网站的发展越有利

用户忠诚度1忠诚型用户2偶尔型用户3投资型用户4游览型用户-1未识别总体规则是判断+聚类算法1、游览用户型:只游览不购买的2、购买天数大于一定天数的为忠诚用户3、购买天数小于一定天数,大部分是有优惠才购买的4、其他类型根据购买天数,购买最后一次距今时间,购买金额进行聚类

模型算法—用户身高尺码模型

男性用户身高尺码xxx-xxx身高段,-1未识别用户购买服装鞋帽等用户判断
男性身材1偏瘦、2标准、3偏胖4肥胖、-1未识别用户购买服装鞋帽等用户判断
女性用户身高尺码xxx-xxx身高段,-1未识别用户购买服装鞋帽等用户判断
女性身材1偏瘦、2标准、3偏胖4肥胖、-1未识别用户购买服装鞋帽等用户判断

模型算法—用户马甲标志模型

Ø 马甲是指一个用户注册多个账号

Ø 多次访问地址相同的用户账号是同一个人所有

Ø 同一台手机登陆多次的用户是同一个人所有

Ø 收货手机号相同的账号同一个人所有

模型算法—手机相关标签模型

Ø 对于手机营销参考意义比较大

Ø 使用手机品牌: 最常用手机直接得到

Ø 使用手机品牌档次:根据档次维表

Ø 使用多少种不同的手机:手机登陆情况

Ø 更换手机频率(月份):按时间段看手机登陆情况

5.4.2 客户消费订单表

根据客户消费的情况提取的客户标签,用于了解用户的消费总体情况,

最终的目的根据用户消费习惯与消费能力做营销。

主要数据来源:订单表、退货表、用户表、购物车表

订单表可以得到相关标签:

第一次消费时间、最近一次消费时间、首单距今时间、尾单距今时间------分析用户什么时候来购买商品以及多久没有购买了。最小消费金额、最大消费金额、累计消费次数(不含退拒)、累计消费金额(不含退拒)、累计使用代金券金额、累计使用代金券次数。-----分析用户总体消费情况。 客单价(含退拒)、 近60天客单价(含退拒)-----分析用户消费水平。 常用收货地址、 常用支付方式----分析用户常用的消费属性,方便做定向营销。

退货表可以得到相关标签:

近30天购买次数(不含退拒)、近30天购买金额(不含退拒)近30天购买次数(含退拒)、近30天购买金额(含退拒)----分析用户最近的消费能力。退货商品数量、退货商品金额、拒收商品数量、拒收商品金额、最近一次退货时间-----分析用户拒收和退货习惯。

购物车表可以得到相关标签:

最近30天购物车次数、最近30天购物车商品件数、最近30天购物车提交商品件数、最近30天购物车放弃件数、最近30天购物车成功率------分析用户购物车使用习惯

订单表和用户表可以得到相关标签:

学校下单总数、单位下单总数、家里下单总数、上午下单总数、下午下单总数、晚上下单总数----分析用户购物时间与地点习惯。

5.4.3 客户购买类目表

根据客户购买类目的情况提取客户标签,用于了解类目的购买人群情况和针对某一类目的营销等。

主要数据来源:订单表、购物车表、类目维表

类目维表可以得到相关标签:

一级分类ID、一级分类名称、二级分类ID、二级分类名称、三级分类ID、三级分类名称-----分析用户都购买了哪些类目。

电商的三级类目:

京东商城:

淘宝:

订单表和类目维表可以得到相关标签:

近30天购买类目次数、近30天购买类目金额、近90天购买类目次数、近90天购买类目金额、近180天购买类目次数、近180天购买类目金额、累计购买类目次数、累计购买类目金额----分析用户最近都购买了哪些类目。最近一次购买类目时间、最后一次购买类目距今天数----分析用户多久没有购买这个类目。

购物车表和类目维表可以得到相关标签:

近30天购物车类目次数、近30天购物车类目金额、近90天购物车类目次数、近90天购物车类目金额----分析用户最近都挑中哪些类目。

5.4.4 用户访问信息表

根据客户访问的情况提取相关客户标签。

用于了解用户的访问总体情况,方便根据客户游览习惯做营销

主要数据来源:点击流日志行为表(PC/APP端)

点击流日志行为表可以得到相关标签:

最近一次APP/PC端访问日期、最近一次APP/PC端访问使用操作系统、最近一次APP/PC端访问使用游览器、最近一次访问IP地址、最近一次访问城市、最近一次访问的省份-----分析用户最近一次访问情况。 第一次APP/PC端访问日期、第一次APP/PC端访问使用操作系统、第一次APP/PC端访问使用游览器、第一次访问IP地址、第一次访问城市、第一次访问的省份-----分析用户第一次访问情况。 近7天APP/PC端访问次数、近30天APP/PC访问次数、近60天APP/PC端访问次数、近90天APP/PC端访问次数、近180天APP/PC端访问次数、近365天APP/PC端访问次数----分析用户APP/PC端访问次数。 近30天PC/APP端访问天数、近30天PC/APP端访问并购买次数、近30天PC/APP端访问PV、近30天PC/APP端访问平均PV、近30天PC/APP端最常用的游览器、近30天PC/APP端不同IP数、近30天PC/APP端最常用IP-----分析用户访问详情。 近30天0-5点访问的次数、近30天6-7点访问的次数、近30天8-9点访问的次数、近30天10-12点访问的次数、近30天13-14点访问的次数、近30天15-17点访问的次数、近30天18-19点访问的次数、近30天20-21点访问的次数、近30天22-23点访问的次数----分析用户喜欢在哪个时间上网访问。

五、 电商用户画像环境搭建

众所周知,Hive的执行任务是将hql语句转化为MapReduce来计算的,Hive的整体解决方案很不错,但是从查询提交到结果返回需要相当长的时间,查询耗时太长。这个主要原因就是由于Hive原生是基于MapReduce的,那么如果我们不生成MapReduce Job,而是生成Spark Job,就可以充分利用Spark的快速执行能力来缩短HiveHQL的响应时间。

本项目采用SparkSql与hive进行整合,通过SparkSql读取hive中表的元数据,把HiveHQL底层采用MapReduce来处理任务,导致性能慢的特点,改为更加强大的Spark引擎来进行相应的分析处理,快速的为用户打上标签构建用户画像。

6.1 环境准备

Ø 1、搭建hadoop集群(见文档)

Ø 2、安装hive构建数据仓库(见文档)

Ø 3、安装spark集群(见文档)

Ø 4、sparksql 整合hive

6.2 sparksql整合hive

Spark SQL主要目的是使得用户可以在Spark上使用SQL,其数据源既可以是RDD,也可以是外部的数据源(比如文本、Hive、Json等)。Spark SQL的其中一个分支就是Spark on Hive,也就是使用Hive中HQL的解析、逻辑执行计划翻译、执行计划优化等逻辑,可以近似认为仅将物理执行计划从MR作业替换成了Spark作业。SparkSql整合hive就是获取hive表中的元数据信息,然后通过SparkSql来操作数据。

整合步骤:

① 需要将hive-site.xml文件拷贝到Spark的conf目录下,这样就可以通过这个配置文件找到Hive的元数据以及数据存放位置。

② 如果Hive的元数据存放在Mysql中,我们还需要准备好Mysql相关驱动,比如:mysql-connector-java-5.1.35.jar

6.3 测试sparksql整合hive是否成功

先启动hadoop集群,在启动spark集群,确保启动成功之后执行命令:

/var/local/spark/bin/spark-sql --master spark://itcast01:7077 --executor-memory 1g --total-executor-cores 4

指明master地址、每一个executor的内存大小、一共所需要的核数、

mysql数据库连接驱动。

执行成功后的界面:进入到spark-sql 客户端命令行界面

接下来就可以通过sql语句来操作数据库表:

查看当前有哪些数据库 —show databases;

看到以上结果,说明sparksql整合hive成功!

日志太多,我们可以修改spark的日志输出级别(conf/log4j.properties)

前方高能:

在spark2.0版本后由于出现了sparkSesssion,在初始化sqlContext的时候,会设置默认的spark.sql.warehouse.dir=spark-warehouse,

此时将hive与sparksql整合完成之后,在通过spark-sql脚本启动的时候,还是会在哪里启动spark-sql脚本,就会在当前目录下创建一个spark.sql.warehouse.dir为spark-warehouse的目录,存放由spark-sql创建数据库和创建表的元数据库信息,与之前hive的元数据库信息不是放在同一个路径下(可以互相访问)。但是此时spark-sql的元数据库在本地,不利于操作,也不安全。

所有在启动的时候需要加上这样一个参数:

–conf spark.sql.warehouse.dir=hdfs://node1:9000/user/hive/warehouse

保证spark-sql启动时不在产生新的元数据库,sparksql与hive最终使用的是hive同一元数据库。

如果使用的是spark2.0之前的版本,由于没有sparkSession,不会有spark.sql.warehouse.dir配置项,不会出现上述问题。

最后的执行脚本;

spark-sql --master spark://node1:7077 --executor-memory 1g --total-executor-cores 2 --conf spark.sql.warehouse.dir=hdfs://node1:9000/user/hive/warehouse

六、 电商用户画像数据仓库建立

7.1 数据仓库准备工作

为什么要对数据仓库分层?

l 用空间换时间,通过大量的预处理来提升应用系统的用户体验(效率),因此数据仓库会存在大量冗余的数据;

l 如果不分层的话,如果源业务系统的业务规则发生变化将会影响整个数据清洗过程,工作量巨大

l 通过数据分层管理可以简化数据清洗的过程,因为把原来一步的工作分到了多个步骤去完成,相当于把一个复杂的工作拆成了多个简单的工作,把一个大的黑盒变成了一个白盒,每一层的处理逻辑都相对简单和容易理解,这样我们比较容易保证每一个步骤的正确性,当数据发生错误的时候,往往我们只需要局部调整某个步骤即可。

数据仓库标准上可以分为四层:ODS(临时存储层)、PDW(数据仓库层)、MID(数据集市层)、APP(应用层)

ODS层:

为临时存储层,是接口数据的临时存储区域,为后一步的数据处理做准备。一般来说ODS层的数据和源系统的数据是同构的,主要目的是简化后续数据加工处理的工作。从数据粒度上来说ODS层的数据粒度是最细的。ODS层的表通常包括两类,一个用于存储当前需要加载的数据,一个用于存储处理完后的历史数据。历史数据一般保存3-6个月后需要清除,以节省空间。但不同的项目要区别对待,如果源系统的数据量不大,可以保留更长的时间,甚至全量保存;

PDW层:

为数据仓库层,PDW层的数据应该是一致的、准确的、干净的数据,即对源系统数据进行了清洗(去除了杂质)后的数据。这一层的数据一般是遵循数据库第三范式的,其数据粒度通常和ODS的粒度相同。在PDW层会保存BI系统中所有的历史数据,例如保存10年的数据

MID层:

为数据集市层,这层数据是面向主题来组织数据的,通常是星形或雪花结构的数据。从数据粒度来说,这层的数据是轻度汇总级的数据,已经不存在明细数据了。从数据的时间跨度来说,通常是PDW层的一部分,主要的目的是为了满足用户分析的需求,而从分析的角度来说,用户通常只需要分析近几年(如近三年的数据)的即可。从数据的广度来说,仍然覆盖了所有业务数据。

APP层:

为应用层,这层数据是完全为了满足具体的分析需求而构建的数据,也是星形或雪花结构的数据。从数据粒度来说是高度汇总的数据。从数据的广度来说,则并不一定会覆盖所有业务数据,而是MID层数据的一个真子集,从某种意义上来说是MID层数据的一个重复。从极端情况来说,可以为每一张报表在APP层构建一个模型来支持,达到以空间换时间的目的数据仓库的标准分层只是一个建议性质的标准,实际实施时需要根据实际情况确定数据仓库的分层,不同类型的数据也可能采取不同的分层方法。

这里我们采用的是京东的数据仓库分层模式,是根据标准的模型演化而来。

数据仓库分层:

BDM:缓冲数据,源数据的直接映像

FDM:基础数据层,数据拉链处理、分区处理

GDM:通用聚合

ADM:高度聚合

先把数据从源数据库中抽取加载到BDM层中,

然后FDM层根据BDM层的数据按天分区

7.2 数据仓库基本表介绍

BDM层数据表(贴源缓存层)订单表itcast_bdm_order
订单明细表itcast_bdm_order_desc
订单商品表itcast_bdm_order_goods
用户表itcast_bdm_user
购物车表itcast_bdm_order_cart
用户上网记录表itcast_bdm_user_pc_click_logitcast_bdm_user_app_click_log
FDM层数据表(拉链表、分区表)用户宽表itcast_fdm_user_wide
购物车表itcast_fdm_order_cart
订单表itcast_fdm_order
订单表明细表itcast_fdm_order_desc
用户app端view表itcast_fdm_user_app_pageview
用户pc端view表itcast_fdm_user_pc_pageview
GDM层数据表(通用数据模型层)客户基本属性表itcast_gdm_user_basic
客户消费订单表itcast_gdm_user_consume_order
订单模型表itcast_gdm_order
客户购买类目表itcast_gdm_user_buy_category
客户访问信息表itcast_gdm_user_visit

七、 电商用户画像开发

8.1用户画像–数据开发的步骤

u 数据开发前置依赖

-需求确定

-建模确定表结构

-实现方案确定

u 数据开发过程

-表落地

-写sql语句实现业务逻辑

-部署代码

-数据测试

-试运行与上线

1. 主键 超键 候选键 外键

**1)超键(super key) :**在关系中能唯一标识元组的属性集称为关系模式的超键

**2)候选键(candidate key):**不含有多余属性的超键称为候选键

**3)主键(primary key):**用户选作元组标识的一个候选键程序主键

**4)外键(foreign key):**如果关系模式R1中的某属性集不是R1的主键,而是另一个关系R2的主键则该属性集是关系模式R1的外键

实例讲解

假设有如下两个表:

​ 学生(学号,姓名,性别,身份证号,教师编号)

​ 教师(教师编号,姓名,工资)

**超键:**由超键的定义可知,学生表中含有学号或者身份证号的任意组合都为此表的超键。如:(学号)、(学号,姓名)、(身份证号,性别)等。

**候选键:**候选键属于超键,它是最小的超键,就是说如果再去掉候选键中的任何一个属性它就不再是超键了。学生表中的候选键为:(学号)、(身份证号)。

**主键:**主键就是候选键里面的一个,是人为规定的,例如学生表中,我们通常会让“学号”做主键,教师表中让“教师编号”做主键。

**外键:**外键比较简单,学生表中的外键就是“教师编号”。外键主要是用来描述两个表的关系。

2. 数据库事务

​ 数据库事务transanction正确执行的四个基本要素:ACID,原子性(Atomicity)、一致性(Correspondence)、隔离性(Isolation)、持久性(Durability)。

​ **原子性:**整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样

​ **一致性:**在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。

​ **隔离性:**隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行 相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请 求,使得在同一时间仅有一个请求用于同一数据。

​ **持久性:**在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

3. 视图

​ 视图是虚拟的表,与包含数据的表不一样,视图只包含使用时动态检索数据的查询;不包含任何列或数据。使用视图可以简化复杂的sql操作,隐藏具体的细节,保护数据;视图创建后,可以使用与表相同的方式利用它们。

​ 视图不能被索引,也不能有关联的触发器或默认值,如果视图本身内有order by 则对视图再次order by将被覆盖

​ 创建视图:create view XXX as XXXXXXXXXXXXXX;

​ 对于某些视图比如未使用联结子查询分组聚集函数Distinct Union等,是可以对其更新的,对视图的更新将对基表进行更新;但是视图主要用于简化检索,保护数据,并不用于更新,而且大部分视图都不可以更新。

4. drop,delete与truncate的区别

drop直接删掉表;truncate删除表中数据,再插入时自增长id又从1开始 ;delete删除表中数据,可以加where字句。

1) DELETE语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作。TRUNCATE TABLE 则一次性地从表中删除所有的数据并不把单独的删除操作记录记入日志保存,删除行是不能恢复的。并且在删除的过程中不会激活与表有关的删除触发器。执行速度快。

2) 表和索引所占空间。当表被TRUNCATE 后,这个表和索引所占用的空间会恢复到初始大小,而DELETE操作不会减少表或索引所占用的空间。drop语句将表所占用的空间全释放掉。

3) 一般而言,drop > truncate > delete

4) 应用范围。TRUNCATE 只能对TABLE;DELETE可以是table和view

5) TRUNCATE 和DELETE只删除数据,而DROP则删除整个表(结构和数据)。

6) truncate与不带where的delete :只删除数据,而不删除表的结构(定义)drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger)索引(index);依赖于该表的存储过程/函数将被保留,但其状态会变为:invalid。

7) delete语句为DML(data maintain Language),这个操作会被放到 rollback segment中,事务提交后才生效。如果有相应的 tigger,执行的时候将被触发。

8) truncate、drop是DLL(data define language),操作立即生效,原数据不放到 rollback segment中,不能回滚

9) 在没有备份情况下,谨慎使用 drop 与 truncate。要删除部分数据行采用delete且注意结合where来约束影响范围。回滚段要足够大。要删除表用drop;若想保留表而将表中数据删除,如果于事务无关,用truncate即可实现。如果和事务有关,或老师想触发trigger,还是用delete。

10) Truncate table 表名 速度快,而且效率高,因为:truncate table 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。

11) TRUNCATE TABLE 删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用 DELETE。如果要删除表定义及其数据,请使用 DROP TABLE 语句。

12) 对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。

5. 索引的工作原理及其种类

​ 数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
​ 为表设置索引要付出代价的:一是增加了数据库的存储空间,二是在插入和修改数据时要花费较多的时间(因为索引也要随之变动。
img

​ 图展示了一种可能的索引方式。左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在O(log2n)的复杂度内获取到相应数据。

**
**

*索引优点*

​ 1)通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。

​ 2)可以大大加快数据的检索速度,这也是创建索引的最主要的原因。

​ 3)可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。

​ 4)在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。

​ 5)通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

索引缺点

​ 1)创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。

​ 2)索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。

​ 3)当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。

建议创建索引列

​ 1)在经常需要搜索的列上,可以加快搜索的速度;

​ 2)在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;

​ 3)在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;

​ 4)在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;

​ 5)在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

**
**

*不建议创建索引列*

​ 1) 对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。

​ 2) 对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。

​ 3) 对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。

​ 4) 当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。

*索引分类*

​ **唯一索引:**唯一索引是不允许其中任何两行具有相同索引值的索引。

​ 当现有数据中存在重复的键值时,大多数数据库不允许将新创建的唯一索引与表一起保存。数据库还可能防止添加将在表中创建重复键值的新数据。例如,如果在employee表中职员的姓(lname)上创建了唯一索引,则任何两个员工都不能同姓。

主键索引

​ 数据库表经常有一列或列组合,其值唯一标识表中的每一行。该列称为表的主键。 在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。

聚集索引

​ 在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。

​ 如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。

*局部性原理与磁盘预读*

​ 由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。程序运行期间所需要的数据通常比较集中。
​ 由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。

​ 预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。

*B-/+Tree索引的性能*

​ 上文说过一般使用磁盘I/O次数评价索引结构的优劣。先从B-Tree分析,根据B-Tree的定义,可知检索一次最多需要访问h个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:

​ 每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。

​ B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)。一般实际应用中,出度d是非常大的数字,通常超过100,因此h非常小(通常不超过3)。

​ 而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。

​ 综上所述,用B-Tree作为索引结构效率是非常高的。

6. 连接种类

*外连接*

**1)左连接:**left join 或 left outer join

select * from table1 left join table2 on table1.id=table2.id

注释:包含table1的所有子句,根据指定条件返回table2相应的字段,不符合的以null显示

**2)右连接:**right join 或 right outer join

select * from table1 right join table2 on table1.id=table2.id

注释:包含table2的所有子句,根据指定条件返回table1相应的字段,不符合的以null显示

3)**完整外部联接:**full join 或 full outer join

select * from table1 full join table2 on table1.id=table2.id

注释:返回左右连接的和(见上左、右连接)

**内连接:**join 或 inner join

select * from table1 join table2 on table1.id=table2.id

注释:只返回符合条件的table1和table2的列

等价:

1)select a.,b. from table1 a,table2 b where a.id=b.id

2)select * from table1 cross join table2 where table1.id=table2.id (注:cross join后加条件只能用where,不能用on)

*交叉连接(完全)*

1) 概念:没有 WHERE 子句的交叉联接将产生联接所涉及的表的笛卡尔积。第一个表的行数乘以第二个表的行数等于笛卡尔积结果集的大小。(table1和table2交叉连接产生3*3=9条记录)

2) 交叉连接:cross join (不带条件where…)

3) sql语句

select * from table1 cross join table2

注释:笛卡尔积

4) 等价(与下列执行效果相同)

select * from table1,table2

7. 数据库范式

*第一范式(1NF)*

​ 在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。

所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复

属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。简而言之

第一范式就是无重复的列。

*第二范式(2NF)*

​ 第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行

必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键、主码。

第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离

来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是非主属性非部分依赖

于主关键字。

*第三范式(3NF)*

满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。例如,存在一个部

门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加

入工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性。(我的理解是消除冗余)

8. 数据库优化的思路

*SQL语句优化*

​ 1)应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

​ 2)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null。可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=0

​ 3)很多时候用 exists 代替 in 是一个好的选择

​ 4)用Where子句替换HAVING 子句 因为HAVING 只会在检索出所有记录之后才对结果集进行过滤

*索引优化*

看上文索引

*数据库结构优化*

​ 1)范式优化: 比如消除冗余(节省空间。。)

​ 2)反范式优化:比如适当加冗余等(减少join)

​ 3)拆分表: 垂直拆分和水平拆分

*服务器硬件优化*

​ 这个么多花钱咯!

9. MySql的复制原理以及流程

基本原理流程,3个线程以及之间的关联;
1) 主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;
2) 从:io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进 自己的relay log中;

​ 3) 从:sql执行线程——执行relay log中的语句;

10. MySQL中myisam与innodb的区别,至少5点

1)InnoDB支持事物,而MyISAM不支持事物

2)InnoDB支持行级锁,而MyISAM支持表级锁

3)InnoDB支持MVCC, 而MyISAM不支持

4)InnoDB支持外键,而MyISAM不支持

5)InnoDB不支持全文索引,而MyISAM支持。

11. innodb引擎的4大特性

插入缓冲(insert buffer)、二次写(double write)、自适应哈希索引(ahi)、预读(read ahead)

12. 两者select count(*)哪个更快,为什么

myisam更快,因为myisam内部维护了一个计数器,可以直接调取。

13. MySQL中varchar与char的区别以及varchar(50)中的50代表的涵义

*1)varchar与char的区别*

​ char是一种固定长度的类型,varchar则是一种可变长度的类型

*2)varchar(50)中50的涵义*

​ 最多存放50个字符,varchar(50)和(200)存储hello所占空间一样,但后者在排序时会消耗更多内存,因为order by col采用fixed_length计算col长度(memory引擎也一样)

*3)int(20)中20的涵义*

​ 指显示字符的长度,但要加参数的,最大为255,比如它是记录行数的id,插入10笔资料,它就显示00000000001 ~~~00000000010,当字符的位数超过11,它也只显示11位,如果你没有加那个让它未满11位就前面加0的参数,它不会在前面加0。20表示最大显示宽度为20,但仍占4字节存储,存储范围不变;

14. innodb的事务与日志的实现方式

*1)有多少种日志*

​ 错误日志:记录出错信息,也记录一些警告信息或者正确的信息。

​ 查询日志:记录所有对数据库请求的信息,不论这些请求是否得到了正确的执行

​ 慢查询日志:设置一个阈值,将运行时间超过该值的所有SQL语句都记录到慢查询的日志文件中。

​ 二进制日志:记录对数据库执行更改的所有操作。

​ 中继日志

​ 事务日志:

*2)事物的4种隔离级别*

​ 读未提交(RU)

​ 读已提交(RC)

​ 可重复读(RR)

​ 串行

*3)事务是如何通过日志来实现的*

​ 事务日志是通过redo和innodb的存储引擎日志缓冲(Innodb log buffer)来实现的,当开始一个事务的时候,会记录该事务的lsn(log sequence number)号; 当事务执行时

会往InnoDB存储引擎的日志的日志缓存里面插入事务日志;当事务提交时,必须将存储引擎的日志缓冲写入磁盘(通过innodb_flush_log_at_trx_commit来控制),也就是写数

据前,需要先写日志。这种方式称为“预写日志方式”。

15. MySQL binlog的几种日志录入格式以及区别

*1)Statement:每一条会修改数据的sql都会记录在binlog中*

**优点:**不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。(相比row能节约多少性能与日志量,这个取决于应用的SQL情况,正常同一条记录修改或者插入

row格式所产生的日志量还小于Statement产生的日志量,但是考虑到如果带条件的update操作,以及整表删除,alter表等操作,ROW格式会产生大量日志,因此在考虑是否使

用ROW格式日志时应该跟据应用的实际情况,其所产生的日志量会增加多少,以及带来的IO性能问题。)

**缺点:**由于记录的只是执行语句,为了这些语句能在slave上正确运行,因此还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在slave得到和在master端执

行时候相同 的结果。另外mysql 的复制,像一些特定函数功能,slave可与master上要保持一致会有很多相关问题(如sleep()函数, last_insert_id(),以user-defined-functions(udf)会出现问题).

使用以下函数的语句也无法被复制:
* LOAD_FILE()
* UUID()
* USER()
* FOUND_ROWS()
* SYSDATE() (除非启动时启用了 --sysdate-is-now 选项)

同时在INSERT …SELECT 会产生比 RBR 更多的行级锁

*2)Row:不记录sql语句上下文相关信息,仅保存哪条记录被修改*

优点: binlog中可以不记录执行的sql语句的上下文相关的信息,仅需要记录那一条记录被修改成什么了。所以rowlevel的日志内容会非常清楚的记录下每一行数据修改的细节

而且不会出现某些特定情况下的存储过程,或function,以及trigger的调用和触发无法被正确复制的问题

**缺点:**所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容,比如一条update语句,修改多条记录,则binlog中每一条修改

都会有记录,这样造成binlog日志量会很大,特别是当执行alter table之类的语句的时候,由于表结构修改,每条记录都发生改变,那么该表每一条记录都会记录到日志中。

*3)Mixedlevel: 是以上两种level的混合使用*

​ 一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区

分对待记录的日志形式,也就是在Statement和Row之间选择一种.新版本的MySQL中队row level模式也被做了优化,并不是所有的修改都会以row level来记录,像遇到表结构变

更的时候就会以statement模式来记录。至于update或者delete等修改数据的语句,还是会记录所有行的变更。

16. MySQL数据库cpu飙升到500%的话他怎么处理

1)列出所有进程:show processlist

2)观察所有进程:多秒没有状态变化的(干掉)

3)查看超时日志或者错误日志 (做了几年开发,一般会是查询以及大批量的插入会导致cpu与i/o上涨 … 当然不排除网络状态突然断了,导致一个请求服务器只接受到一半,比如where子句或分页子句没有发送,当然的一次被坑经历)

17. 一个6亿的表a,一个3亿的表b,通过外间tid关联,你如何最快的查询出满足条件的第50000到第50200中的这200条数据记录

1)如果A表TID是自增长,并且是连续的,B表的ID为索引

select * from a,b where a.tid = b.id and a.tid>500000 limit 200;

2)如果A表的TID不是连续的,那么就需要使用覆盖索引.TID要么是主键,要么是辅助索引,B表ID也需要有索引

select * from b , (select tid from a limit 50000,200) a where b.id = a .tid;

18. MySQL中InnoDB引擎的行锁是通过加在什么上完成

答:InnoDB是基于索引来完成行锁

例: select * from tab_with_index where id = 1 for update;

for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,

如果 id 不是索引键那么InnoDB将完成表锁,并发将无从谈起

19. xtrabackup实现原理

​ 在InnoDB内部会维护一个redo日志文件,我们也可以叫做事务日志文件。事务日志会存储每一个InnoDB表数据的记录修改。当InnoDB启动时,InnoDB会检

查数据文件的事务日志,并执行两个步骤:它应用(前滚)已经提交的事务日志到数据文件,并将修改过但没有提交的数据进行回滚操作。

20. 存储过程与触发器的区别

​ 触发器与存储过程非常相似,触发器也是SQL语句集,两者唯一的区别是触发器不能用EXECUTE语句调用,而是在用户执行Transact-SQL语句时自动触发

(激活)执行。触发器是在一个修改了指定表中的数据时执行的存储过程。通常通过创建触发器来强制实现不同表中的逻辑相关数据的引用完整性和一致性。由于

用户不能绕过触发器,所以可以用它来强制实施复杂的业务规则,以确保数据的完整性。触发器不同于存储过程,触发器主要是通过事件执行触发而被执行的,

存储过程可以通过存储过程名称名字而直接调用。当对某一表进行诸如UPDATE、INSERT、DELETE这些操作时,SQLSERVER就会自动执行触发器所定义的

SQL语句,从而确保对数据的处理必须符合这些SQL语句所定义的规则。

数据库基础知识

为什么要使用数据库

数据保存在内存

优点: 存取速度快

缺点: 数据不能永久保存

数据保存在文件

优点: 数据永久保存

缺点:1)速度比内存操作慢,频繁的IO操作。2)查询数据不方便

数据保存在数据库

1)数据永久保存

2)使用SQL语句,查询方便效率高。

3)管理数据方便

什么是SQL?

结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询语言。

作用:用于存取数据、查询、更新和管理关系数据库系统。

什么是MySQL?

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展。

数据库三大范式是什么

第一范式:每个列都不可以再拆分。

第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。

第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。

在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计。

mysql有关权限的表都有哪几个

MySQL服务器通过权限表来控制用户对数据库的访问,权限表存放在mysql数据库里,由mysql_install_db脚本初始化。这些权限表分别user,db,table_priv,columns_priv和host。下面分别介绍一下这些表的结构和内容:

  • user权限表:记录允许连接到服务器的用户帐号信息,里面的权限是全局级的。
  • db权限表:记录各个帐号在各个数据库上的操作权限。
  • table_priv权限表:记录数据表级的操作权限。
  • columns_priv权限表:记录数据列级的操作权限。
  • host权限表:配合db权限表对给定主机上数据库级操作权限作更细致的控制。这个权限表不受GRANT和REVOKE语句的影响。

MySQL的binlog有有几种录入格式?分别有什么区别?

有三种格式,statement,row和mixed。

  • statement模式下,每一条会修改数据的sql都会记录在binlog中。不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制。
  • row级别下,不记录sql语句上下文相关信息,仅保存哪条记录被修改。记录单元为每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大。
  • mixed,一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row。

此外,新版的MySQL中对row级别也做了一些优化,当表结构发生变化的时候,会记录语句而不是逐行记录。

数据类型

mysql有哪些数据类型

分类类型名称说明
整数类型tinyInt很小的整数(8位二进制)
smallint小的整数(16位二进制)
mediumint中等大小的整数(24位二进制)
int(integer)普通大小的整数(32位二进制)
小数类型float单精度浮点数
double双精度浮点数
decimal(m,d)压缩严格的定点数
日期类型yearYYYY 1901~2155
timeHH:MM:SS -838:59:59~838:59:59
dateYYYY-MM-DD 1000-01-01~9999-12-3
datetimeYYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~ 9999-12-31 23:59:59
timestampYYYY-MM-DD HH:MM:SS 19700101 00:00:01 UTC~2038-01-19 03:14:07UTC
文本、二进制类型CHAR(M)M为0~255之间的整数
VARCHAR(M)M为0~65535之间的整数
TINYBLOB允许长度0~255字节
BLOB允许长度0~65535字节
MEDIUMBLOB允许长度0~167772150字节
LONGBLOB允许长度0~4294967295字节
TINYTEXT允许长度0~255字节
TEXT允许长度0~65535字节
MEDIUMTEXT允许长度0~167772150字节
LONGTEXT允许长度0~4294967295字节
VARBINARY(M)允许长度0~M个字节的变长字节字符串
BINARY(M)允许长度0~M个字节的定长字节字符串
  • 1、整数类型,包括TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别表示1字节、2字节、3字节、4字节、8字节整数。任何整数类型都可以加上UNSIGNED属性,表示数据是无符号的,即非负整数。
    长度:整数类型可以被指定长度,例如:INT(11)表示长度为11的INT类型。长度在大多数场景是没有意义的,它不会限制值的合法范围,只会影响显示字符的个数,而且需要和UNSIGNED ZEROFILL属性配合使用才有意义。
    例子,假定类型设定为INT(5),属性为UNSIGNED ZEROFILL,如果用户插入的数据为12的话,那么数据库实际存储数据为00012。

  • 2、实数类型,包括FLOAT、DOUBLE、DECIMAL。
    DECIMAL可以用于存储比BIGINT还大的整型,能存储精确的小数。
    而FLOAT和DOUBLE是有取值范围的,并支持使用标准的浮点进行近似计算。
    计算时FLOAT和DOUBLE相比DECIMAL效率更高一些,DECIMAL你可以理解成是用字符串进行处理。

  • 3、字符串类型,包括VARCHAR、CHAR、TEXT、BLOB
    VARCHAR用于存储可变长字符串,它比定长类型更节省空间。
    VARCHAR使用额外1或2个字节存储字符串长度。列长度小于255字节时,使用1字节表示,否则使用2字节表示。
    VARCHAR存储的内容超出设置的长度时,内容会被截断。
    CHAR是定长的,根据定义的字符串长度分配足够的空间。
    CHAR会根据需要使用空格进行填充方便比较。
    CHAR适合存储很短的字符串,或者所有值都接近同一个长度。
    CHAR存储的内容超出设置的长度时,内容同样会被截断。

    使用策略:
    对于经常变更的数据来说,CHAR比VARCHAR更好,因为CHAR不容易产生碎片。
    对于非常短的列,CHAR比VARCHAR在存储空间上更有效率。
    使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。
    尽量避免使用TEXT/BLOB类型,查询时会使用临时表,导致严重的性能开销。

  • 4、枚举类型(ENUM),把不重复的数据存储为一个预定义的集合。
    有时可以使用ENUM代替常用的字符串类型。
    ENUM存储非常紧凑,会把列表值压缩到一个或两个字节。
    ENUM在内部存储时,其实存的是整数。
    尽量避免使用数字作为ENUM枚举的常量,因为容易混乱。
    排序是按照内部存储的整数

  • 5、日期和时间类型,尽量使用timestamp,空间效率高于datetime,
    用整数保存时间戳通常不方便处理。
    如果需要存储微妙,可以使用bigint存储。
    看到这里,这道真题是不是就比较容易回答了。

引擎

MySQL存储引擎MyISAM与InnoDB区别

存储引擎Storage engine:MySQL中的数据、索引以及其他对象是如何存储的,是一套文件系统的实现。

常用的存储引擎有以下:

  • Innodb引擎:Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
  • MyIASM引擎(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。
  • MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。

MyISAM与InnoDB区别

MyISAMInnodb
存储结构每张表被存放在三个文件:frm-表格定义、MYD(MYData)-数据文件、MYI(MYIndex)-索引文件所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB
存储空间MyISAM可被压缩,存储空间较小InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引
可移植性、备份及恢复由于MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了
文件格式数据和索引是分别存储的,数据.MYD,索引.MYI数据和索引是集中存储的,.ibd
记录存储顺序按记录插入顺序保存按主键大小有序插入
外键不支持支持
事务不支持支持
锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的)表级锁定行级锁定、表级锁定,锁定力度小并发能力高
SELECTMyISAM更优
INSERT、UPDATE、DELETEInnoDB更优
select count(*)myisam更快,因为myisam内部维护了一个计数器,可以直接调取。
索引的实现方式B+树索引,myisam 是堆表B+树索引,Innodb 是索引组织表
哈希索引不支持支持
全文索引支持不支持

MyISAM索引与InnoDB索引的区别?

  • InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
  • InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
  • MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
  • InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。

InnoDB引擎的4大特性

  • 插入缓冲(insert buffer)
  • 二次写(double write)
  • 自适应哈希索引(ahi)
  • 预读(read ahead)

存储引擎选择

如果没有特别的需求,使用默认的Innodb即可。

MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。

Innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高,支持事务和外键。比如OA自动化办公系统。

索引

什么是索引?

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。

索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。

更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

索引有哪些优缺点?

索引的优点

  • 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
  • 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

索引的缺点

  • 时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;
  • 空间方面:索引需要占物理空间。

索引使用场景(重点)

where

img

上图中,根据id查询记录,因为id字段仅建立了主键索引,因此此SQL执行可选的索引只有主键索引,如果有多个,最终会选一个较优的作为检索的依据。

-- 增加一个没有建立索引的字段
alter table innodb1 add sex char(1);
-- 按sex检索时可选的索引为null
EXPLAIN SELECT * from innodb1 where sex='男';
1234

img

可以尝试在一个字段未建立索引时,根据该字段查询的效率,然后对该字段建立索引(alter table 表名 add index(字段名)),同样的SQL执行的效率,你会发现查询效率会有明显的提升(数据量越大越明显)。

order by

当我们使用order by将查询结果按照某个字段排序时,如果该字段没有建立索引,那么执行计划会将查询出的所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序,最后合并排序结果),这个操作是很影响性能的,因为需要将查询涉及到的所有数据从磁盘中读到内存(如果单条数据过大或者数据量过多都会降低效率),更无论读到内存之后的排序了。

但是如果我们对该字段建立索引alter table 表名 add index(字段名),那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用像上述那取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)

join

join语句匹配关系(on)涉及的字段建立索引能够提高效率

索引覆盖

如果要查询的字段都建立过索引,那么引擎会直接在索引表中查询而不会访问原始数据(否则只要有一个字段没有建立索引就会做全表扫描),这叫索引覆盖。因此我们需要尽可能的在select后只写必要的查询字段,以增加索引覆盖的几率。

这里值得注意的是不要想着为每个字段建立索引,因为优先使用索引的优势就在于其体积小。

索引有哪几种类型?

主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。

唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

  • 可以通过 ALTER TABLE table_name ADD UNIQUE (column); 创建唯一索引
  • 可以通过 ALTER TABLE table_name ADD UNIQUE (column1,column2); 创建唯一组合索引

普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。

  • 可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引
  • 可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引

全文索引: 是目前搜索引擎使用的一种关键技术。

  • 可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引

索引的数据结构(b树,hash)

索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有Hash索引B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。

1)B树索引

mysql通过存储引擎取数据,基本上90%的人用的就是InnoDB了,按照实现方式分,InnoDB的索引类型目前只有两种:BTREE(B树)索引和HASH索引。B树索引是Mysql数据库中使用最频繁的索引类型,基本所有存储引擎都支持BTree索引。通常我们说的索引不出意外指的就是(B树)索引(实际是用B+树实现的,因为在查看表索引时,mysql一律打印BTREE,所以简称为B树索引)

img

查询方式:

主键索引区:PI(关联保存的时数据的地址)按主键查询,

普通索引区:si(关联的id的地址,然后再到达上面的地址)。所以按主键查询,速度最快

B+tree性质:

1.)n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。

2.)所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。

3.)所有的非终端结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。

4.)B+ 树中,数据对象的插入和删除仅在叶节点上进行。

5.)B+树有2个头指针,一个是树的根节点,一个是最小关键码的叶节点。

2)哈希索引

简要说下,类似于数据结构中简单实现的HASH表(散列表)一样,当我们在mysql中用哈希索引时,主要就是通过Hash算法(常见的Hash算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储。当然这只是简略模拟图。

img

索引的基本原理

索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。

索引的原理很简单,就是把无序的数据变成有序的查询

  1. 把创建了索引的列的内容进行排序
  2. 对排序结果生成倒排表
  3. 在倒排表内容上拼上数据地址链
  4. 在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据

索引算法有哪些?

索引算法有 BTree算法和Hash算法

BTree算法

BTree是最常用的mysql数据库索引算法,也是mysql默认的算法。因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量, 例如:

-- 只要它的查询条件是一个不以通配符开头的常量
select * from user where name like 'jack%'; 
-- 如果一通配符开头,或者没有使用常量,则不会使用索引,例如: 
select * from user where name like '%jack'; 
1234

Hash算法

Hash Hash索引只能用于对等比较,例如=,<=>(相当于=)操作符。由于是一次定位数据,不像BTree索引需要从根节点到枝节点,最后才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引。

索引设计的原则?

  1. 适合索引的列是出现在where子句中的列,或者连接子句中指定的列
  2. 基数较小的类,索引效果较差,没有必要在此列建立索引
  3. 使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
  4. 不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。

创建索引的原则(重中之重)

索引虽好,但也不是无限制的使用,最好符合一下几个原则

1) 最左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

2)较频繁作为查询条件的字段才去创建索引

3)更新频繁字段不适合创建索引

4)若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)

5)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

6)定义有外键的数据列一定要建立索引。

7)对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。

8)对于定义为text、image和bit的数据类型的列不要建立索引。

创建索引的三种方式,删除索引

第一种方式:在执行CREATE TABLE时创建索引

CREATE TABLE user_index2 (
	id INT auto_increment PRIMARY KEY,
	first_name VARCHAR (16),
	last_name VARCHAR (16),
	id_card VARCHAR (18),
	information text,
	KEY name (first_name, last_name),
	FULLTEXT KEY (information),
	UNIQUE KEY (id_card)
);
12345678910

第二种方式:使用ALTER TABLE命令去增加索引

ALTER TABLE table_name ADD INDEX index_name (column_list);
1

ALTER TABLE用来创建普通索引、UNIQUE索引或PRIMARY KEY索引。

其中table_name是要增加索引的表名,column_list指出对哪些列进行索引,多列时各列之间用逗号分隔。

索引名index_name可自己命名,缺省时,MySQL将根据第一个索引列赋一个名称。另外,ALTER TABLE允许在单个语句中更改多个表,因此可以在同时创建多个索引。

第三种方式:使用CREATE INDEX命令创建

CREATE INDEX index_name ON table_name (column_list);
1

CREATE INDEX可对表增加普通索引或UNIQUE索引。(但是,不能创建PRIMARY KEY索引)

删除索引

根据索引名删除普通索引、唯一索引、全文索引:alter table 表名 drop KEY 索引名

alter table user_index drop KEY name;
alter table user_index drop KEY id_card;
alter table user_index drop KEY information;
123

删除主键索引:alter table 表名 drop primary key(因为主键只有一个)。这里值得注意的是,如果主键自增长,那么不能直接执行此操作(自增长依赖于主键索引):

img

需要取消自增长再行删除:

alter table user_index
-- 重新定义字段
MODIFY id int,
drop PRIMARY KEY
1234

但通常不会删除主键,因为设计主键一定与业务逻辑无关。

创建索引时需要注意什么?

  • 非空字段:应该指定列为NOT NULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值;
  • 取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高;
  • 索引字段越小越好:数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高。

使用索引查询一定能提高查询的性能吗?为什么

通常,通过索引查询数据比全表扫描要快。但是我们也必须注意到它的代价。

  • 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改。 这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5 次的磁盘I/O。 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
  • 基于一个范围的检索,一般查询返回结果集小于表中记录数的30%
  • 基于非唯一性索引的检索

百万级别或以上的数据如何删除

关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。

  1. 所以我们想要删除百万数据的时候可以先删除索引(此时大概耗时三分多钟)
  2. 然后删除其中无用数据(此过程需要不到两分钟)
  3. 删除完成后重新创建索引(此时数据较少了)创建索引也非常快,约十分钟左右。
  4. 与之前的直接删除绝对是要快速很多,更别说万一删除中断,一切删除会回滚。那更是坑了。

前缀索引

语法:index(field(10)),使用字段值的前10个字符建立索引,默认是使用字段的全部内容建立索引。

前提:前缀的标识度高。比如密码就适合建立前缀索引,因为密码几乎各不相同。

实操的难度:在于前缀截取的长度。

我们可以利用select count(*)/count(distinct left(password,prefixLen));,通过从调整prefixLen的值(从1自增)查看不同前缀长度的一个平均匹配度,接近1时就可以了(表示一个密码的前prefixLen个字符几乎能确定唯一一条记录)

什么是最左前缀原则?什么是最左匹配原则

  • 顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
  • 最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
  • =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

B树和B+树的区别

  • 在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。

  • B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。

    img

使用B树的好处

B树可以在内部节点同时存储键和值,因此,把频繁访问的数据放在靠近根节点的地方将会大大提高热点数据的查询效率。这种特性使得B树在特定数据重复多次查询的场景中更加高效。

使用B+树的好处

由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。 B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间

Hash索引和B+树所有有什么区别或者说优劣呢?

首先要知道Hash索引和B+树索引的底层实现原理:

hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据。B+树底层实现是多路平衡查找树。对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。

那么可以看出他们有以下的不同:

  • hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询。

因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询。而B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围。

  • hash索引不支持使用索引进行排序,原理同上。
  • hash索引不支持模糊查询以及多列索引的最左前缀匹配。原理也是因为hash函数的不可预测。AAAA和AAAAB的索引没有相关性。
  • hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询。
  • hash索引虽然在等值查询上较快,但是不稳定。性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差。而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低。

因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度。而不需要使用hash索引。

数据库为什么使用B+树而不是B树

  • B树只适合随机检索,而B+树同时支持随机检索和顺序检索;
  • B+树空间利用率更高,可减少I/O次数,磁盘读写代价更低。一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树的内部结点并没有指向关键字具体信息的指针,只是作为索引使用,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素;
  • B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。
  • B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作。
  • 增删文件(节点)时,效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。

B+树在满足聚簇索引和覆盖索引的时候不需要回表查询数据,

在B+树的索引中,叶子节点可能存储了当前的key值,也可能存储了当前的key值以及整行的数据,这就是聚簇索引和非聚簇索引。 在InnoDB中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。如果没有唯一键,则隐式的生成一个键来建立聚簇索引。

当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询。

什么是聚簇索引?何时使用聚簇索引与非聚簇索引

  • 聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
  • 非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因

澄清一个概念:innodb中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值

何时使用聚簇索引与非聚簇索引

img

非聚簇索引一定会回表查询吗?

不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询。

举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行select age from employee where age < 20的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询。

联合索引是什么?为什么需要注意联合索引中的顺序?

MySQL可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。

具体原因为:

MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序。

当进行查询时,此时索引仅仅按照name严格有序,因此必须首先使用name字段进行等值查询,之后对于匹配到的列而言,其按照age字段严格有序,此时可以使用age字段用做索引查找,以此类推。因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面。此外可以根据特例的查询或者表结构进行单独的调整。

事务

什么是数据库事务?

事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。

事务最经典也经常被拿出来说例子就是转账了。

假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。

事物的四大特性(ACID)介绍一下?

关系性数据库需要遵循ACID规则,具体内容如下:

事务的特性

  1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  2. 一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
  3. 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

什么是脏读?幻读?不可重复读?

  • 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
  • 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
  • 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

什么是事务的隔离级别?MySQL的默认隔离级别是什么?

为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

隔离级别脏读不可重复读幻影读
READ-UNCOMMITTED
READ-COMMITTED×
REPEATABLE-READ××
SERIALIZABLE×××

SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读

这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别

事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。

InnoDB 存储引擎在 分布式事务 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。

对MySQL的锁了解吗

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他使用完毕才可以再次使用。

隔离级别与锁的关系

在Read Uncommitted级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突

在Read Committed级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁;

在Repeatable Read级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。

SERIALIZABLE 是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。

按照锁的粒度分数据库锁有哪些?锁机制与InnoDB锁算法

在关系型数据库中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎 )。

MyISAM和InnoDB存储引擎使用的锁:

  • MyISAM采用表级锁(table-level locking)。
  • InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

行级锁,表级锁和页级锁对比

行级锁 行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁 和 排他锁。

特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

表级锁 表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。

特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。

页级锁 页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。

特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

从锁的类别上分MySQL都有哪些锁呢?像上面那样子进行锁定岂不是有点阻碍并发效率了

从锁的类别上来讲,有共享锁和排他锁。

共享锁: 又叫做读锁。 当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。

排他锁: 又叫做写锁。 当用户要进行数据的写入时,对数据加上排他锁。排他锁只可以加一个,他和其他的排他锁,共享锁都相斥。

用上面的例子来说就是用户的行为有两种,一种是来看房,多个用户一起看房是可以接受的。 一种是真正的入住一晚,在这期间,无论是想入住的还是想看房的都不可以。

锁的粒度取决于具体的存储引擎,InnoDB实现了行级锁,页级锁,表级锁。

他们的加锁开销从大到小,并发能力也是从大到小。

MySQL中InnoDB引擎的行锁是怎么实现的?

答:InnoDB是基于索引来完成行锁

例: select * from tab_with_index where id = 1 for update;

for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么InnoDB将完成表锁,并发将无从谈起

InnoDB存储引擎的锁的算法有三种

  • Record lock:单个行记录上的锁
  • Gap lock:间隙锁,锁定一个范围,不包括记录本身
  • Next-key lock:record+gap 锁定一个范围,包含记录本身

相关知识点:

  1. innodb对于行的查询使用next-key lock
  2. Next-locking keying为了解决Phantom Problem幻读问题
  3. 当查询的索引含有唯一属性时,将next-key lock降级为record key
  4. Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
  5. 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1

什么是死锁?怎么解决?

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

常见的解决死锁的方法

1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。

2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;

3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;

如果业务处理不好可以用分布式事务锁或者使用乐观锁

数据库的乐观锁和悲观锁是什么?怎么实现的?

数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。实现方式:使用数据库中的锁机制

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来,通过version的方式来进行锁定。实现方式:乐一般会使用版本号机制或CAS算法实现。

两种锁的使用场景

从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。

但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

视图

为什么要使用视图?什么是视图?

为了提高复杂SQL语句的复用性和表操作的安全性,MySQL数据库管理系统提供了视图特性。所谓视图,本质上是一种虚拟表,在物理上是不存在的,其内容与真实的表相似,包含一系列带有名称的列和行数据。但是,视图并不在数据库中以储存的数据值形式存在。行和列数据来自定义视图的查询所引用基本表,并且在具体引用视图时动态生成。

视图使开发者只关心感兴趣的某些特定数据和所负责的特定任务,只能看到视图中所定义的数据,而不是视图所引用表中的数据,从而提高了数据库中数据的安全性。

视图有哪些特点?

视图的特点如下:

  • 视图的列可以来自不同的表,是表的抽象和在逻辑意义上建立的新关系。
  • 视图是由基本表(实表)产生的表(虚表)。
  • 视图的建立和删除不影响基本表。
  • 对视图内容的更新(添加,删除和修改)直接影响基本表。
  • 当视图来自多个基本表时,不允许添加和删除数据。

视图的操作包括创建视图,查看视图,删除视图和修改视图。

视图的使用场景有哪些?

视图根本用途:简化sql查询,提高开发效率。如果说还有另外一个用途那就是兼容老的表结构。

下面是视图的常见使用场景:

  • 重用SQL语句;
  • 简化复杂的SQL操作。在编写查询后,可以方便的重用它而不必知道它的基本查询细节;
  • 使用表的组成部分而不是整个表;
  • 保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限;
  • 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。

视图的优点

  1. 查询简单化。视图能简化用户的操作
  2. 数据安全性。视图使用户能以多种角度看待同一数据,能够对机密数据提供安全保护
  3. 逻辑数据独立性。视图对重构数据库提供了一定程度的逻辑独立性

视图的缺点

  1. 性能。数据库必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也把它变成一个复杂的结合体,需要花费一定的时间。

  2. 修改限制。当用户试图修改视图的某些行时,数据库必须把它转化为对基本表的某些行的修改。事实上,当从视图中插入或者删除时,情况也是这样。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的

    这些视图有如下特征:1.有UNIQUE等集合操作符的视图。2.有GROUP BY子句的视图。3.有诸如AVG\SUM\MAX等聚合函数的视图。 4.使用DISTINCT关键字的视图。5.连接表的视图(其中有些例外)

什么是游标?

游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果,每个游标区都有一个名字。用户可以通过游标逐一获取记录并赋给主变量,交由主语言进一步处理。

存储过程与函数

什么是存储过程?有哪些优缺点?

存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需要创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。

优点

1)存储过程是预编译过的,执行效率高。

2)存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。

3)安全性高,执行存储过程需要有一定权限的用户。

4)存储过程可以重复使用,减少数据库开发人员的工作量。

缺点

1)调试麻烦,但是用 PL/SQL Developer 调试很方便!弥补这个缺点。

2)移植问题,数据库端代码当然是与数据库相关的。但是如果是做工程型项目,基本不存在移植问题。

3)重新编译问题,因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译(不过也可以设置成运行时刻自动编译)。

4)如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难很难、而且代价是空前的,维护起来更麻烦。

触发器

什么是触发器?触发器的使用场景有哪些?

触发器是用户定义在关系表上的一类由事件驱动的特殊的存储过程。触发器是指一段代码,当触发某个事件时,自动执行这些代码。

使用场景

  • 可以通过数据库中的相关表实现级联更改。
  • 实时监控某张表中的某个字段的更改而需要做出相应的处理。
  • 例如可以生成某些业务的编号。
  • 注意不要滥用,否则会造成数据库及应用程序的维护困难。
  • 大家需要牢记以上基础知识点,重点是理解数据类型CHAR和VARCHAR的差异,表存储引擎InnoDB和MyISAM的区别。

MySQL中都有哪些触发器?

在MySQL数据库中有如下六种触发器:

  • Before Insert
  • After Insert
  • Before Update
  • After Update
  • Before Delete
  • After Delete

常用SQL语句

SQL语句主要分为哪几类

数据定义语言DDL(Data Ddefinition Language)CREATE,DROP,ALTER

主要为以上操作 即对逻辑结构等有操作的,其中包括表结构,视图和索引。

数据查询语言DQL(Data Query Language)SELECT

这个较为好理解 即查询操作,以select关键字。各种简单查询,连接查询等 都属于DQL。

数据操纵语言DML(Data Manipulation Language)INSERT,UPDATE,DELETE

主要为以上操作 即对数据进行操作的,对应上面所说的查询操作 DQL与DML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种 被划分到DQL中。

数据控制功能DCL(Data Control Language)GRANT,REVOKE,COMMIT,ROLLBACK

主要为以上操作 即对数据库安全性完整性等有操作的,可以简单的理解为权限控制等。

超键、候选键、主键、外键分别是什么?

  • 超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。
  • 候选键:是最小超键,即没有冗余元素的超键。
  • 主键:数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。
  • 外键:在一个表中存在的另一个表的主键称此表的外键。

SQL 约束有哪几种?

SQL 约束有哪几种?

  • NOT NULL: 用于控制字段的内容一定不能为空(NULL)。
  • UNIQUE: 控件字段内容不能重复,一个表允许有多个 Unique 约束。
  • PRIMARY KEY: 也是用于控件字段内容不能重复,但它在一个表只允许出现一个。
  • FOREIGN KEY: 用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
  • CHECK: 用于控制字段的值范围。

六种关联查询

  • 交叉连接(CROSS JOIN)
  • 内连接(INNER JOIN)
  • 外连接(LEFT JOIN/RIGHT JOIN)
  • 联合查询(UNION与UNION ALL)
  • 全连接(FULL JOIN)
  • 交叉连接(CROSS JOIN)
SELECT * FROM A,B(,C)或者SELECT * FROM A CROSS JOIN B (CROSS JOIN C)#没有任何关联条件,结果是笛卡尔积,结果集会很大,没有意义,很少使用内连接(INNER JOIN)SELECT * FROM A,B WHERE A.id=B.id或者SELECT * FROM A INNER JOIN B ON A.id=B.id多表中同时符合某种条件的数据记录的集合,INNER JOIN可以缩写为JOIN
1

内连接分为三类

  • 等值连接:ON A.id=B.id
  • 不等值连接:ON A.id > B.id
  • 自连接:SELECT * FROM A T1 INNER JOIN A T2 ON T1.id=T2.pid

外连接(LEFT JOIN/RIGHT JOIN)

  • 左外连接:LEFT OUTER JOIN, 以左表为主,先查询出左表,按照ON后的关联条件匹配右表,没有匹配到的用NULL填充,可以简写成LEFT JOIN
  • 右外连接:RIGHT OUTER JOIN, 以右表为主,先查询出右表,按照ON后的关联条件匹配左表,没有匹配到的用NULL填充,可以简写成RIGHT JOIN

联合查询(UNION与UNION ALL)

SELECT * FROM A UNION SELECT * FROM B UNION ...
1
  • 就是把多个结果集集中在一起,UNION前的结果为基准,需要注意的是联合查询的列数要相等,相同的记录行会合并
  • 如果使用UNION ALL,不会合并重复的记录行
  • 效率 UNION 高于 UNION ALL

全连接(FULL JOIN)

  • MySQL不支持全连接
  • 可以使用LEFT JOIN 和UNION和RIGHT JOIN联合使用
SELECT * FROM A LEFT JOIN B ON A.id=B.id UNIONSELECT * FROM A RIGHT JOIN B ON A.id=B.id
1

表连接面试题

有2张表,1张R、1张S,R表有ABC三列,S表有CD两列,表中各有三条记录。

R表

ABC
a1b1c1
a2b2c2
a3b3c3

S表

CD
c1d1
c2d2
c4d3
  1. 交叉连接(笛卡尔积):

select r.*,s.* from r,s

ABCCD
a1b1c1c1d1
a2b2c2c1d1
a3b3c3c1d1
a1b1c1c2d2
a2b2c2c2d2
a3b3c3c2d2
a1b1c1c4d3
a2b2c2c4d3
a3b3c3c4d3
  1. 内连接结果:

    select r.*,s.* from r inner join s on r.c=s.c

ABCCD
a1b1c1c1d1
a2b2c2c2d2
  1. 左连接结果:

    select r.*,s.* from r left join s on r.c=s.c

ABCCD
a1b1c1c1d1
a2b2c2c2d2
a3b3c3
  1. 右连接结果:

    select r.*,s.* from r right join s on r.c=s.c

ABCCD
a1b1c1c1d1
a2b2c2c2d2
c4d3
  1. 全表连接的结果(MySql不支持,Oracle支持):

    select r.*,s.* from r full join s on r.c=s.c

ABCCD
a1b1c1c1d1
a2b2c2c2d2
a3b3c3
c4d3

什么是子查询

  1. 条件:一条SQL语句的查询结果做为另一条查询语句的条件或查询结果
  2. 嵌套:多条SQL语句嵌套使用,内部的SQL查询语句称为子查询。

子查询的三种情况

  1. 子查询是单行单列的情况:结果集是一个值,父查询使用:=、 <、 > 等运算符
-- 查询工资最高的员工是谁? 
select  * from employee where salary=(select max(salary) from employee);   
12
  1. 子查询是多行单列的情况:结果集类似于一个数组,父查询使用:in 运算符
-- 查询工资最高的员工是谁? 
select  * from employee where salary=(select max(salary) from employee);    
12
  1. 子查询是多行多列的情况:结果集类似于一张虚拟表,不能用于where条件,用于select子句中做为子表
-- 1) 查询出2011年以后入职的员工信息
-- 2) 查询所有的部门信息,与上面的虚拟表中的信息比对,找出所有部门ID相等的员工。
select * from dept d,  (select * from employee where join_date > '2011-1-1') e where e.dept_id =  d.id;    

-- 使用表连接:
select d.*, e.* from  dept d inner join employee e on d.id = e.dept_id where e.join_date >  '2011-1-1'  
123456

mysql中 in 和 exists 区别

mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。

  1. 如果查询的两个表大小相当,那么用in和exists差别不大。
  2. 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。
  3. not in 和not exists:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。

varchar与char的区别

char的特点

  • char表示定长字符串,长度是固定的;
  • 如果插入数据的长度小于char的固定长度时,则用空格填充;
  • 因为长度固定,所以存取速度要比varchar快很多,甚至能快50%,但正因为其长度固定,所以会占据多余的空间,是空间换时间的做法;
  • 对于char来说,最多能存放的字符个数为255,和编码无关

varchar的特点

  • varchar表示可变长字符串,长度是可变的;
  • 插入的数据是多长,就按照多长来存储;
  • varchar在存取方面与char相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法;
  • 对于varchar来说,最多能存放的字符个数为65532

总之,结合性能角度(char更快)和节省磁盘空间角度(varchar更小),具体情况还需具体来设计数据库才是妥当的做法。

varchar(50)中50的涵义

最多存放50个字符,varchar(50)和(200)存储hello所占空间一样,但后者在排序时会消耗更多内存,因为order by col采用fixed_length计算col长度(memory引擎也一样)。在早期 MySQL 版本中, 50 代表字节数,现在代表字符数。

int(20)中20的涵义

是指显示字符的长度。20表示最大显示宽度为20,但仍占4字节存储,存储范围不变;

不影响内部存储,只是影响带 zerofill 定义的 int 时,前面补多少个 0,易于报表展示

mysql为什么这么设计

对大多数应用没有意义,只是规定一些工具用来显示字符的个数;int(1)和int(20)存储和计算均一样;

mysql中int(10)和char(10)以及varchar(10)的区别

  • int(10)的10表示显示的数据的长度,不是存储数据的大小;chart(10)和varchar(10)的10表示存储数据的大小,即表示存储多少个字符。

    int(10) 10位的数据长度 9999999999,占32个字节,int型4位
    char(10) 10位固定字符串,不足补空格 最多10个字符
    varchar(10) 10位可变字符串,不足补空格 最多10个字符

  • char(10)表示存储定长的10个字符,不足10个就用空格补齐,占用更多的存储空间

  • varchar(10)表示存储10个变长的字符,存储多少个就是多少个,空格也按一个字符存储,这一点是和char(10)的空格不同的,char(10)的空格表示占位不算一个字符

FLOAT和DOUBLE的区别是什么?

  • FLOAT类型数据可以存储至多8位十进制数,并在内存中占4字节。
  • DOUBLE类型数据可以存储至多18位十进制数,并在内存中占8字节。

drop、delete与truncate的区别

三者都表示删除,但是三者有一些差别:

DeleteTruncateDrop
类型属于DML属于DDL属于DDL
回滚可回滚不可回滚不可回滚
删除内容表结构还在,删除表的全部或者一部分数据行表结构还在,删除表中的所有数据从数据库中删除表,所有的数据行,索引和权限也会被删除
删除速度删除速度慢,需要逐行删除删除速度快删除速度最快

因此,在不再需要一张表的时候,用drop;在想删除部分数据行时候,用delete;在保留表而删除所有数据的时候用truncate。

UNION与UNION ALL的区别?

  • 如果使用UNION ALL,不会合并重复的记录行
  • 效率 UNION 高于 UNION ALL

SQL优化

如何定位及优化SQL语句的性能问题?创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因?

对于低性能的SQL语句的定位,最重要也是最有效的方法就是使用执行计划,MySQL提供了explain命令来查看语句的执行计划。 我们知道,不管是哪种数据库,或者是哪种数据库引擎,在对一条SQL语句进行执行的过程中都会做很多相关的优化,对于查询语句,最重要的优化方式就是使用索引。 而执行计划,就是显示数据库引擎对于SQL语句的执行的详细情况,其中包含了是否使用索引,使用什么索引,使用的索引的相关信息等

在这里插入图片描述

执行计划包含的信息 id 有一组数字组成。表示一个查询中各个子查询的执行顺序;

  • id相同执行顺序由上至下。
  • id不同,id值越大优先级越高,越先被执行。
  • id为null时表示一个结果集,不需要使用它查询,常出现在包含union等查询语句中。

select_type 每个子查询的查询类型,一些常见的查询类型。

idselect_typedescription
1SIMPLE不包含任何子查询或union等查询
2PRIMARY包含子查询最外层查询就显示为 PRIMARY
3SUBQUERY在select或 where字句中包含的查询
4DERIVEDfrom字句中包含的查询
5UNION出现在union后的查询语句中
6UNION RESULT从UNION中获取结果集,例如上文的第三个例子

table 查询的数据表,当从衍生表中查数据时会显示 x 表示对应的执行计划id partitions 表分区、表创建的时候可以指定通过那个列进行表分区。 举个例子:

create table tmp (
    id int unsigned not null AUTO_INCREMENT,
    name varchar(255),
    PRIMARY KEY (id)
) engine = innodb
partition by key (id) partitions 5;
123456

type(非常重要,可以看到有没有走索引) 访问类型

  • ALL 扫描全表数据
  • index 遍历索引
  • range 索引范围查找
  • index_subquery 在子查询中使用 ref
  • unique_subquery 在子查询中使用 eq_ref
  • ref_or_null 对Null进行索引的优化的 ref
  • fulltext 使用全文索引
  • ref 使用非唯一索引查找数据
  • eq_ref 在join查询中使用PRIMARY KEYorUNIQUE NOT NULL索引关联。

possible_keys 可能使用的索引,注意不一定会使用。查询涉及到的字段上若存在索引,则该索引将被列出来。当该列为 NULL时就要考虑当前的SQL是否需要优化了。

key 显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。

TIPS:查询中若使用了覆盖索引(覆盖索引:索引的数据覆盖了需要查询的所有数据),则该索引仅出现在key列表中

key_length 索引长度

ref 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

rows 返回估算的结果集数目,并不是一个准确的值。

extra 的信息非常丰富,常见的有:

  1. Using index 使用覆盖索引
  2. Using where 使用了用where子句来过滤结果集
  3. Using filesort 使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。
  4. Using temporary 使用了临时表 sql优化的目标可以参考阿里开发手册
【推荐】SQL性能优化的目标:至少要达到 range 级别,要求是ref级别,如果可以是consts最好。 
说明: 
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。 
2) ref 指的是使用普通的索引(normal index)。 
3) range 对索引进行范围检索。 
反例:explain表的结果,type=index,索引物理文件全扫描,速度非常慢,这个index级别比较range还低,与全表扫描是小巫见大巫。
123456

SQL的生命周期?

  1. 应用服务器与数据库服务器建立一个连接

  2. 数据库进程拿到请求sql

  3. 解析并生成执行计划,执行

  4. 读取数据到内存并进行逻辑处理

  5. 通过步骤一的连接,发送结果到客户端

  6. 关掉连接,释放资源

    在这里插入图片描述

大表数据查询,怎么优化

  1. 优化shema、sql语句+索引;
  2. 第二加缓存,memcached, redis;
  3. 主从复制,读写分离;
  4. 垂直拆分,根据你模块的耦合度,将一个大的系统分为多个小的系统,也就是分布式系统;
  5. 水平切分,针对数据量大的表,这一步最麻烦,最能考验技术水平,要选择一个合理的sharding key, 为了有好的查询效率,表结构也要改动,做一定的冗余,应用也要改,sql中尽量带sharding key,将数据定位到限定的表上去查,而不是扫描全部的表;

超大分页怎么处理?

超大的分页一般从两个方向上来解决.

  • 数据库层面,这也是我们主要集中关注的(虽然收效没那么大),类似于select * from table where age > 20 limit 1000000,10这种查询其实也是有可以优化的余地的. 这条语句需要load1000000数据然后基本上全部丢弃,只取10条当然比较慢. 当时我们可以修改为select * from table where id in (select id from table where age > 20 limit 1000000,10).这样虽然也load了一百万的数据,但是由于索引覆盖,要查询的所有字段都在索引中,所以速度会很快. 同时如果ID连续的好,我们还可以select * from table where id > 1000000 limit 10,效率也是不错的,优化的可能性有许多种,但是核心思想都一样,就是减少load的数据.
  • 从需求的角度减少这种请求…主要是不做类似的需求(直接跳转到几百万页之后的具体某一页.只允许逐页查看或者按照给定的路线走,这样可预测,可缓存)以及防止ID泄漏且连续被人恶意攻击.

解决超大分页,其实主要是靠缓存,可预测性的提前查到内容,缓存至redis等k-V数据库中,直接返回即可.

在阿里巴巴《Java开发手册》中,对超大分页的解决办法是类似于上面提到的第一种.

【推荐】利用延迟关联或者子查询优化超多分页场景。 

说明:MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。 

正例:先快速定位需要获取的id段,然后再关联: 

SELECT a.* FROM1 a, (select id from1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
1234567

mysql 分页

LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1)

mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15 
1

为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:

mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last. 
1

如果只给定一个参数,它表示返回最大的记录行数目:

mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行 
1

换句话说,LIMIT n 等价于 LIMIT 0,n。

慢查询日志

用于记录执行时间超过某个临界值的SQL日志,用于快速定位慢查询,为我们的优化做参考。

开启慢查询日志

配置项:slow_query_log

可以使用show variables like ‘slov_query_log’查看是否开启,如果状态值为OFF,可以使用set GLOBAL slow_query_log = on来开启,它会在datadir下产生一个xxx-slow.log的文件。

设置临界时间

配置项:long_query_time

查看:show VARIABLES like 'long_query_time',单位秒

设置:set long_query_time=0.5

实操时应该从长时间设置到短的时间,即将最慢的SQL优化掉

查看日志,一旦SQL超过了我们设置的临界时间就会被记录到xxx-slow.log

关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?

在业务系统中,除了使用主键进行的查询,其他的我都会在测试库上测试其耗时,慢查询的统计主要由运维在做,会定期将业务中的慢查询反馈给我们。

慢查询的优化首先要搞明白慢的原因是什么? 是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大?

所以优化也是针对这三个方向来的,

  • 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写。
  • 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。
  • 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。

为什么要尽量设定一个主键?

主键是数据库确保数据行在整张表唯一性的保障,即使业务上本张表没有主键,也建议添加一个自增长的ID列作为主键。设定了主键之后,在后续的删改查的时候可能更加快速以及确保操作数据范围安全。

主键使用自增ID还是UUID?

推荐使用自增ID,不要使用UUID。

因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降。

总之,在数据量大一些的情况下,用自增主键性能会好一些。

关于主键是聚簇索引,如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键。

字段为什么要求定义为not null?

null值会占用更多的字节,且会在程序中造成很多与预期不符的情况。

如果要存储用户的密码散列,应该使用什么字段进行存储?

密码散列,盐,用户身份证号等固定长度的字符串应该使用char而不是varchar来存储,这样可以节省空间且提高检索效率。

优化查询过程中的数据访问

  • 访问数据太多导致查询性能下降
  • 确定应用程序是否在检索大量超过需要的数据,可能是太多行或列
  • 确认MySQL服务器是否在分析大量不必要的数据行
  • 避免犯如下SQL语句错误
  • 查询不需要的数据。解决办法:使用limit解决
  • 多表关联返回全部列。解决办法:指定列名
  • 总是返回全部列。解决办法:避免使用SELECT *
  • 重复查询相同的数据。解决办法:可以缓存数据,下次直接读取缓存
  • 是否在扫描额外的记录。解决办法:
  • 使用explain进行分析,如果发现查询需要扫描大量的数据,但只返回少数的行,可以通过如下技巧去优化:
  • 使用索引覆盖扫描,把所有的列都放到索引中,这样存储引擎不需要回表获取对应行就可以返回结果。
  • 改变数据库和表的结构,修改数据表范式
  • 重写SQL语句,让优化器可以以更优的方式执行查询。

优化长难的查询语句

  • 一个复杂查询还是多个简单查询
  • MySQL内部每秒能扫描内存中上百万行数据,相比之下,响应数据给客户端就要慢得多
  • 使用尽可能小的查询是好的,但是有时将一个大的查询分解为多个小的查询是很有必要的。
  • 切分查询
  • 将一个大的查询分为多个小的相同的查询
  • 一次性删除1000万的数据要比一次删除1万,暂停一会的方案更加损耗服务器开销。
  • 分解关联查询,让缓存的效率更高。
  • 执行单个查询可以减少锁的竞争。
  • 在应用层做关联更容易对数据库进行拆分。
  • 查询效率会有大幅提升。
  • 较少冗余记录的查询。

优化特定类型的查询语句

  • count(*)会忽略所有的列,直接统计所有列数,不要使用count(列名)
  • MyISAM中,没有任何where条件的count(*)非常快。
  • 当有where条件时,MyISAM的count统计不一定比其它引擎快。
  • 可以使用explain查询近似值,用近似值替代count(*)
  • 增加汇总表
  • 使用缓存

优化关联查询

  • 确定ON或者USING子句中是否有索引。
  • 确保GROUP BY和ORDER BY只有一个表中的列,这样MySQL才有可能使用索引。

优化子查询

  • 用关联查询替代
  • 优化GROUP BY和DISTINCT
  • 这两种查询据可以使用索引来优化,是最有效的优化方法
  • 关联查询中,使用标识列分组的效率更高
  • 如果不需要ORDER BY,进行GROUP BY时加ORDER BY NULL,MySQL不会再进行文件排序。
  • WITH ROLLUP超级聚合,可以挪到应用程序处理

优化LIMIT分页

  • LIMIT偏移量大的时候,查询效率较低
  • 可以记录上次查询的最大ID,下次查询时直接根据该ID来查询

优化UNION查询

  • UNION ALL的效率高于UNION

优化WHERE子句

解题方法

对于此类考题,先说明如何定位低效SQL语句,然后根据SQL语句可能低效的原因做排查,先从索引着手,如果索引没有问题,考虑以上几个方面,数据访问的问题,长难查询句的问题还是一些特定类型优化的问题,逐一回答。

SQL语句优化的一些方法?

  • 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
  • 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
-- 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=
123
  • 3.应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
  • 4.应尽量避免在 where 子句中使用or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
-- 可以这样查询:
select id from t where num=10 union all select id from t where num=20
123
  • 5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3) 
-- 对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
123
  • 6.下面的查询也将导致全表扫描:select id from t where name like ‘%李%’若要提高效率,可以考虑全文检索。
  • 7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
-- 可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
123
  • 8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
-- 应改为:
select id from t where num=100*2
123
  • 9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=’abc’
-- name以abc开头的id应改为:
select id from t where name like ‘abc%123
  • 10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

数据库优化

为什么要优化

  • 系统的吞吐量瓶颈往往出现在数据库的访问速度上
  • 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢
  • 数据是存放在磁盘上的,读写速度无法和内存相比

优化原则:减少系统瓶颈,减少资源占用,增加系统的反应速度。

数据库结构优化

一个好的数据库设计方案对于数据库的性能往往会起到事半功倍的效果。

需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容。

将字段很多的表分解成多个表

对于字段较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。

因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。

增加中间表

对于需要经常联合查询的表,可以建立中间表以提高查询效率。

通过建立中间表,将需要通过联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询。

增加冗余字段

设计数据表时应尽量遵循范式理论的规约,尽可能的减少冗余字段,让数据库设计看起来精致、优雅。但是,合理的加入冗余字段可以提高查询速度。

表的规范化程度越高,表和表之间的关系越多,需要连接查询的情况也就越多,性能也就越差。

注意:

冗余字段的值在一个表中修改了,就要想办法在其他表中更新,否则就会导致数据不一致的问题。

MySQL数据库cpu飙升到500%的话他怎么处理?

当 cpu 飙升到 500%时,先用操作系统命令 top 命令观察是不是 mysqld 占用导致的,如果不是,找出占用高的进程,并进行相关处理。

如果是 mysqld 造成的, show processlist,看看里面跑的 session 情况,是不是有消耗资源的 sql 在运行。找出消耗高的 sql,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成。

一般来说,肯定要 kill 掉这些线程(同时观察 cpu 使用率是否下降),等进行相应的调整(比如说加索引、改 sql、改内存参数)之后,再重新跑这些 SQL。

也有可能是每个 sql 消耗资源并不多,但是突然之间,有大量的 session 连进来导致 cpu 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等

大表怎么优化?某个表有近千万数据,CRUD比较慢,如何优化?分库分表了是怎么做的?分表分库了有什么问题?有用到中间件么?他们的原理知道么?

当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:

  1. 限定数据的范围: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。;
  2. 读/写分离: 经典的数据库拆分方案,主库负责写,从库负责读;
  3. 缓存: 使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存;

还有就是通过分库分表的方式进行优化,主要有垂直分表和水平分表

  1. 垂直分区:

    根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。

    简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了。

    img

    垂直拆分的优点: 可以使得行数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。

    垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;

    垂直分表

    把主键和一些列放在一个表,然后把主键和另外的列放在另一个表中

    img

    适用场景
    • 1、如果一个表中某些列常用,另外一些列不常用
    • 2、可以使数据行变小,一个数据页能存储更多数据,查询时减少I/O次数
    缺点
    • 有些分表的策略基于应用层的逻辑算法,一旦逻辑算法改变,整个分表逻辑都会改变,扩展性较差
    • 对于应用层来说,逻辑算法增加开发成本
    • 管理冗余列,查询所有数据需要join操作
  2. 水平分区:

    保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。

    水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。

    数据库水平拆分

    水品拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库

    水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨界点Join性能较差,逻辑复杂。

    《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。

    水平分表:

    表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询次数

    img

    适用场景
    • 1、表中的数据本身就有独立性,例如表中分表记录各个地区的数据或者不同时期的数据,特别是有些数据常用,有些不常用。
    • 2、需要把数据存放在多个介质上。
    水平切分的缺点
    • 1、给应用增加复杂度,通常查询时需要多个表名,查询所有数据都需UNION操作
    • 2、在许多数据库应用中,这种复杂度会超过它带来的优点,查询时会增加读一个索引层的磁盘次数

    下面补充一下数据库分片的两种常见方案:

    • 客户端代理: 分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。 当当网的 Sharding-JDBC 、阿里的TDDL是两种比较常用的实现。
    • 中间件代理: 在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。 我们现在谈的 Mycat 、360的Atlas、网易的DDB等等都是这种架构的实现。

分库分表后面临的问题

  • 事务支持 分库分表后,就成了分布式事务了。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价; 如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。

  • 跨库join

    只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。 分库分表方案产品

  • 跨节点的count,order by,group by以及聚合函数问题 这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。

  • 数据迁移,容量规划,扩容等问题 来自淘宝综合业务平台团队,它利用对2的倍数取余具有向前兼容的特性(如对4取余得1的数对2取余也是1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。总得来说,这些方案都不是十分的理想,多多少少都存在一些缺点,这也从一个侧面反映出了Sharding扩容的难度。

  • ID问题

  • 一旦数据库被切分到多个物理结点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的ID无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得ID,以便进行SQL路由. 一些常见的主键生成策略

UUID 使用UUID作主键是最简单的方案,但是缺点也是非常明显的。由于UUID非常的长,除占用大量存储空间外,最主要的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题。 Twitter的分布式自增ID算法Snowflake 在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位 机器ID 10位 毫秒内序列12位。

  • 跨分片的排序分页

    般来讲,分页时需要按照指定字段进行排序。当排序字段就是分片字段的时候,我们通过分片规则可以比较容易定位到指定的分片,而当排序字段非分片字段的时候,情况就会变得比较复杂了。为了最终结果的准确性,我们需要在不同的分片节点中将数据进行排序并返回,并将不同分片返回的结果集进行汇总和再次排序,最后再返回给用户。如下图所示: