摘要:本案例将带大家使用一份开源的S.M.A.R.T.数据集和机器学习中的随机森林算法,来训练一个硬盘故障预测模型,并测试效果。
本文分享自华为云社区《基于随机森林算法进行硬盘故障预测》,作者:HWCloudAI 。
随着互联网、云计算的发展,数据的存储需求与日倍增,大规模海量数据存储中心是必不可少的基础性设施。虽然新的存储介质例如SSD,已经很多方面拥有了比磁盘更好的性能,但就目前来讲,其高昂的花费仍然使大部分数据中心难以负担,因此,大型数据中心依然会采用传统的机械硬盘作为存储介质。
机械硬盘生命周期通常为3到5年,在2到3年后故障率明显升高,导致换盘量陡增。据统计,在服务器硬件故障中,硬盘故障占比达到48%+,是影响服务器运行可靠性的重要因素。早在上个世纪九十年代,人们就意识到数据的宝贵性远胜于硬盘自身价值,渴望有种技术能对硬盘故障进行预测并实现相对安全的数据保护,因此S.M.A.R.T.技术应运而生。
S.M.A.R.T.,全称为“Self-Monitoring Analysis and Reporting Technology”,即“自我监测、分析及报告技术”,是一种自动的硬盘状态检测与预警系统和规范。通过在硬盘硬件内的检测指令对硬盘的硬件如磁头、盘片、马达、电路的运行情况进行监控、记录并与厂商所设定的预设安全值进行比较,若监控情况将或已超出预设安全值的安全范围,就可以通过主机的监控硬件或软件自动向用户作出警告并进行轻微的自动修复,以提前保障硬盘数据的安全。除一些出厂时间极早的硬盘外,现在大部分硬盘均配备该项技术。关于该技术的更多介绍,请查看S.M.A.R.T.-百度百科。
虽然硬盘厂商采用了S.M.A.R.T.技术来监测硬盘的健康状态,但是大多数厂商都是基于设计规则制定的故障预测手段,预测效果非常差,不能满足日渐严格的提前预测硬盘故障的需求。因此,业界期望使用机器学习技术来构建硬盘故障预测的模型,更准确地提前感知硬盘故障,降低运维成本,提升业务体验。
本案例将带大家使用一份开源的S.M.A.R.T.数据集和机器学习中的随机森林算法,来训练一个硬盘故障预测模型,并测试效果。
关于随机森林算法的理论知识,可参考此视频。
本案例使用的数据集是来自于Backblaze公司的开源数据集,它是一家计算机备份和云存储服务提供商。自2013年以来,Backbreze每年都会公开发布他们的数据中心所使用硬盘的S.M.A.R.T.日志数据,有效地推动了使用机器学习技术进行硬盘故障预测的发展。
由于Backblaze公司发布的S.M.A.R.T.日志数据量较大,本案例为快速演示使用机器学习构建硬盘故障预测模型的过程,仅使用了该公司发布的2020年的数据,相关数据已经准备好,放在OBS中,运行如下代码即可下载这部分数据。
import os
import moxing as mox
if not os.path.exists('./dataset_2020'):
mox.file.copy('obs://modelarts-labs-bj4-v2/course/ai_in_action/2021/machine_learning/hard_drive_disk_fail_prediction/datasets/dataset_2020.zip', './dataset_2020.zip')
os.system('unzip dataset_2020.zip')
if not os.path.exists('./dataset_2020'):
raise Exception('错误!数据不存在!')
!ls -lh ./dataset_2020
INFO:root:Using MoXing-v1.17.3-
INFO:root:Using OBS-Python-SDK-3.20.7
total 102M
-rw-r--r-- 1 ma-user ma-group 51M Mar 21 11:56 2020-12-08.csv
-rw-r--r-- 1 ma-user ma-group 51M Mar 21 11:56 2020-12-09.csv
-rw-r--r-- 1 ma-user ma-group 1.2M Mar 21 11:55 dataset_2020.csv
-rw-r--r-- 1 ma-user ma-group 3.5K Mar 22 15:59 prepare_data.py
数据解释:
2020-12-08.csv:从backblaze公司发布的2020 Q4数据集中抽取出来的2020-12-08这天的S.M.A.R.T.日志数据
2020-12-09.csv:从backblaze公司发布的2020 Q4数据集中抽取出来的2020-12-09这天的S.M.A.R.T.日志数据
dataset_2020.csv:已经处理过的2020年全年S.M.A.R.T.日志数据,下文中“第2.6节 类别均衡度分析”会解释如何得到这部分数据
prepare_data.py: 运行该脚本,会下载2020年全年S.M.A.R.T.日志数据,并进行处理,得到dataset_2020.csv。运行该脚本需要20G的本地存储空间
使用机器学习构建任何模型之前,都需要先对数据集进行分析,了解数据集的规模、属性名、属性值、各类统计指标及空值情况。因为我们要先了解数据,才能用好数据。
pandas是常用的python数据分析模块,我们先用它来加载数据集中的csv文件。以2020-12-08.csv为例,我们先加载该文件来分析S.M.A.R.T.日志数据的情况
import pandas as pd
df_data = pd.read_csv("./dataset_2020/2020-12-08.csv")
type(df_data)
pandas.core.frame.DataFrame
print('单个csv文件数据的规模,行数:%d, 列数:%d' % (df_data.shape[0], df_data.shape[1]))
单个csv文件数据的规模,行数:162008, 列数:149
使用pandas加载csv后,得到的是一个DataFrame对象,可以理解为一个表格,调用该对象的head()函数,可以查看表格的头5行数据
df_data.head()
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
</style>
5 rows × 149 columns
如上所示是表格的头5行数据,表头是属性名,属性名下面是属性值,backblaze网站解释了属性值的含义,翻译为如下:
查看完表格的头5行数据,我们再调用DataFrame对象的describe()函数,计算表格数据的统计指标
df_data.describe()
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
</style>
8 rows × 146 columns
如上所示是表格数据的统计指标,describe()函数默认对数值类型的列进行统计分析,由于表格的前三列’date’、‘serial_number’、'model’是字符串类型,所以这三列没有统计指标。
各行统计指标的含义解释如下:
count: 该列有多少个非空值
mean: 该列的均值
std: 该列数值的标准差
min: 该列数值的最小值
25%: 该列数值的25%中位值
50%: 该列数值的50%中位值
75%: 该列数值的75%中位值
max: 该列数值的最大值
从上面的输出可以观察到,某些属性的count指标比较小,比如smart_2_raw的count数就比df_train的总行数要小很多,因此我们要再进一步看看各列属性的空值情况,执行如下代码可以查看空值情况
df_data.isnull().sum()
date 0
serial_number 0
model 0
capacity_bytes 0
failure 0
smart_1_normalized 179
smart_1_raw 179
smart_2_normalized 103169
smart_2_raw 103169
smart_3_normalized 1261
smart_3_raw 1261
smart_4_normalized 1261
smart_4_raw 1261
smart_5_normalized 1221
smart_5_raw 1221
smart_7_normalized 1261
smart_7_raw 1261
smart_8_normalized 103169
smart_8_raw 103169
smart_9_normalized 179
smart_9_raw 179
smart_10_normalized 1261
smart_10_raw 1261
smart_11_normalized 161290
smart_11_raw 161290
smart_12_normalized 179
smart_12_raw 179
smart_13_normalized 161968
smart_13_raw 161968
smart_15_normalized 162008
...
smart_232_normalized 160966
smart_232_raw 160966
smart_233_normalized 160926
smart_233_raw 160926
smart_234_normalized 162008
smart_234_raw 162008
smart_235_normalized 160964
smart_235_raw 160964
smart_240_normalized 38968
smart_240_raw 38968
smart_241_normalized 56030
smart_241_raw 56030
smart_242_normalized 56032
smart_242_raw 56032
smart_245_normalized 161968
smart_245_raw 161968
smart_247_normalized 162006
smart_247_raw 162006
smart_248_normalized 162006
smart_248_raw 162006
smart_250_normalized 162008
smart_250_raw 162008
smart_251_normalized 162008
smart_251_raw 162008
smart_252_normalized 162008
smart_252_raw 162008
smart_254_normalized 161725
smart_254_raw 161725
smart_255_normalized 162008
smart_255_raw 162008
Length: 149, dtype: int64
这种显示方式不太方便查看,我们把可以空值的数量绘制成曲线图,看起来更直观
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
df_data_null_num = df_data.isnull().sum()
x = list(range(len(df_data_null_num)))
y = df_data_null_num.values
plt.plot(x, y)
plt.show()
从上面的结果可以看出,表格中的某些属性有大量的空值。
在机器学习领域中,数据集中存在空值是很常见的现象,引起空值的原因有很多种,比如一份用户画像中有很多个属性,但又不是所有用户都有对应的属性值,这时就产生了空值。或者某些数据因为传输超时,导致没有采集上来,也可能会出现空值。
我们要实现的任务是“硬盘故障预测”,即预测某个硬盘在某个时间是正常还是损坏,这就是一个故障预测问题或异常检测问题,这类问题有个特点就是:正常样本非常多,故障样本非常少,两类样本的数量差异非常大。
比如,执行如下代码,可以看到df_data中硬盘正常的样本有16万个以上,故障的样本却只有8个,类别极度不均衡。
valid = df_data[df_data['failure'] == 0]
failed = df_data[df_data['failure'] == 1]
print("valid hdds:",len(valid))
print("failed hdds:",len(failed))
valid hdds: 162000
failed hdds: 8
由于大多数机器学习方法的学习过程都是基于统计学的思路来进行学习的,如果直接使用上面这样类别不均衡的数据进行训练,那么模型的能力可能会明显偏向于类别多的样本,类别少的样本就会被“淹没”掉了,在学习过程中发挥不了作用,因此我们需要平衡不同类别的数据。
为了获得更多的故障样本数据,我们可以从backblaze公司发布的2020年全年S.M.A.R.T.日志数据中将所有的故障样本都挑选出来,同时也随机挑出相同数量的正常样本,可以通过下面的代码来实现。
这段代码已被注释掉,如需运行,需要20G的本地存储空间。您也可以不必运行这段代码,因为本案例开头已经下载了dataset_2020.zip,这个压缩包中已经提供了dataset_2020.csv,该csv就是运行下面这段代码得到的文件
# if not os.path.exists('./dataset_2020/dataset_2020.csv'):
# os.system('python ./dataset_2020/prepare_data.py')
import gc
del df_data # 删除 df_data 对象
gc.collect() # 回收内存
2655
dataset_2020.csv是已经经过类别均衡处理的硬盘S.M.A.R.T.日志数据,下面我们加载该文件,再确认一下类别均衡情况
df_data = pd.read_csv("./dataset_2020/dataset_2020.csv")
valid = df_data[df_data['failure'] == 0]
failed = df_data[df_data['failure'] == 1]
print("valid hdds:", len(valid))
print("failed hdds:", len(failed))
valid hdds: 1497
failed hdds: 1497
可以看到,正常样本和故障样本都是1497个
准备好可用的训练集之后,接下来要做特征工程,通俗地讲,特性工程就是要选择表格中的哪些属性来构建机器学习模型。人工设计特征的好坏,很大程度上决定了机器学习模型效果的好坏,所以机器学习领域的研究人员需耗费大量精力在人工设计特征上,是一项比较耗时、耗力,且需要专家经验的工程。
(1)BackBlaze分析了其HDD故障和SMART属性之间的相关性,并发现了SMART 5、187、188、197、198与HDD故障的相关率最高,这些SMART属性还与扫描错误,重新分配计数和试用计数有关[1];
(2)El-Shimi等发现在随机森林模型中除了以上5个特征外,还有SMART 9、193、194、241、242这5个属性有最大权重[2];
(3)Pitakrat等人评估了21种用于预测硬盘故障的机器学习算法,发现在测试的21种机器学习算法中,随机森林算法在ROC曲线下有最大面积,而KNN分类器具有最高的F1值[3];
(4)Hughes等人也研究用于预测硬盘故障的机器学习方法,他们分析了SVM、朴素贝叶斯的表现,SVM实现了最高性能,检测率为50.6%,误报率为0%[4];
[1] Klein, Andy. “What SMART Hard Disk Errors Actually Tell Us.” Backblaze Blog Cloud Storage & Cloud Backup,6 Oct. 2016, www.backblaze.com/blog/what-smart-stats-indicate-hard-drive-failures/
[2] El-Shimi, Ahmed. “Predicting Storage Failures.” VAULT-Linux Storage and File Systems Conference.VAULT-Linux Storage and File Systems Conference, 22 Mar. 2017, Cambridge.
[3] Pitakrat, Teerat, André van Hoorn, and Lars Grunske. “A comparison of machine learning algorithms for proactive hard disk drive failure detection.” Proceedings of the 4th international ACM Sigsoft symposium on Architecting critical systems. ACM, 2013.
[4] Hughes, Gordon F., et al. “Improved disk-drive failure warnings.” IEEE Transactions on Reliability 51.3 (2002):350-357.
如上就是前人的一些研究成果,本案例计划采用随机森林模型,因此可以根据上面第2条研究成果,选择SMART 5, 9, 187, 188, 193, 194, 197, 198, 241, 242这些属性来作为特征,它们的含义分别是:
SMART 5: 重映射扇区计数
SMART 9: 通电时间累计
SMART 187: 无法校正的错误
SMART 188: 指令超时计数
SMART 193: 磁头加载/卸载计数
SMART 194: 温度
SMART 197: 等待被映射的扇区数
SMART 198: 报告给操作系统的无法通过硬件ECC校正的错误
SMART 241: 逻辑块寻址模式写入总数
SMART 242: 逻辑块寻址模式读取总数
另外,由于不同硬盘厂商的不同型号硬盘记录SMART日志数据的标准可能不一样,所以我们最好将同一型号的硬盘数据挑出来作为训练数据,专门训练一个预测该型号硬盘是否故障的模型。如果需要预测多个不同型号的硬盘是否故障,则可能需要分别训练多个模型。
执行下面的代码,看一下每种型号的硬盘数据量有多少
df_data.model.value_counts()
ST12000NM0007 664
ST4000DM000 491
ST8000NM0055 320
ST12000NM0008 293
TOSHIBA MG07ACA14TA 212
ST8000DM002 195
HGST HMS5C4040BLE640 193
HGST HUH721212ALN604 153
TOSHIBA MQ01ABF050 99
ST12000NM001G 53
HGST HMS5C4040ALE640 50
ST500LM012 HN 40
TOSHIBA MQ01ABF050M 35
HGST HUH721212ALE600 34
ST10000NM0086 29
ST14000NM001G 23
HGST HUH721212ALE604 21
ST500LM030 15
HGST HUH728080ALE600 14
Seagate BarraCuda SSD ZA250CM10002 12
WDC WD5000LPVX 11
WDC WUH721414ALE6L4 10
ST6000DX000 9
TOSHIBA MD04ABA400V 3
Seagate SSD 2
ST8000DM004 2
ST18000NM000J 2
ST4000DM005 2
WDC WD5000LPCX 1
ST8000DM005 1
DELLBOSS VD 1
HGST HDS5C4040ALE630 1
TOSHIBA HDWF180 1
HGST HUS726040ALE610 1
ST16000NM001G 1
Name: model, dtype: int64
可以看到 ST12000NM0007 型号的硬盘数据量最多,因此我们把该型号硬盘的数据过滤出来
df_data_model = df_data[df_data['model'] == 'ST12000NM0007']
选取上文提到的10个属性作为特征
features_specified = []
features = [5, 9, 187, 188, 193, 194, 197, 198, 241, 242]
for feature in features:
features_specified += ["smart_{0}_raw".format(feature)]
X_data = df_data_model[features_specified]
Y_data = df_data_model['failure']
X_data.isnull().sum()
smart_5_raw 1
smart_9_raw 1
smart_187_raw 1
smart_188_raw 1
smart_193_raw 1
smart_194_raw 1
smart_197_raw 1
smart_198_raw 1
smart_241_raw 1
smart_242_raw 1
dtype: int64
有空值存在,所以先要填充空值
X_data = X_data.fillna(0)
print("valid hdds:", len(Y_data) - np.sum(Y_data.values))
print("failed hdds:", np.sum(Y_data.values))
valid hdds: 325
failed hdds: 339
使用sklearn的train_test_split即可划分训练集和测试集,test_size表示测试集的比例,一般取值为0.3、0.2或0.1
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X_data, Y_data, test_size=0.2, random_state=0)
准备好训练集和测试集之后,就可以开始构建模型了,构建模型的步骤非常简单,直接调用机器学习框架sklearn中的RandomForestClassifier即可
from sklearn.ensemble import RandomForestClassifier
rfc = RandomForestClassifier()
随机森林算法的超参数有很多个,取不同的参数值构建模型会得到不同的训练效果,对于初学者,可以直接使用库中提供的默认参数值,在对随机森林算法的原理有一定的了解之后,可以尝试修改模型的参数来调整模型的训练效果。
模型训练的过程,也就是拟合训练数据的过程,实现也非常简单,调用fit函数即可开始训练
rfc.fit(X_train, Y_train)
/home/ma-user/anaconda3/envs/XGBoost-Sklearn/lib/python3.6/site-packages/sklearn/ensemble/forest.py:248: FutureWarning: The default value of n_estimators will change from 10 in version 0.20 to 100 in 0.22.
"10 in version 0.20 to 100 in 0.22.", FutureWarning)
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=None,
oob_score=False, random_state=None, verbose=0,
warm_start=False)
调用predict函数即可开始预测
Y_pred = rfc.predict(X_test)
在机器学习中,分类问题的性能指标,常用的有四种:accuracy(精度)、precision(查准率)、recall(查全率)、F1-Score,四种指标越接近1,表示效果越好。sklearn库中有这四种指标的函数,直接调用即可。
关于四种指标的理论解释,可参考此视频
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
print("Model used is: Random Forest classifier")
acc = accuracy_score(Y_test, Y_pred)
print("The accuracy is {}".format(acc))
prec = precision_score(Y_test, Y_pred)
print("The precision is {}".format(prec))
rec = recall_score(Y_test, Y_pred)
print("The recall is {}".format(rec))
f1 = f1_score(Y_test, Y_pred)
print("The F1-Score is {}".format(f1))
Model used is: Random Forest classifier
The accuracy is 0.8270676691729323
The precision is 0.8548387096774194
The recall is 0.7910447761194029
The F1-Score is 0.8217054263565892
每次进行随机森林模型的训练,会得到该模型不同的测试准确率指标,这是由于随机森林算法的训练过程具有一定的随机性导致的,是正常现象。但是同一模型、同一样本的预测结果是确定不变的。
模型保存
import pickle
with open('hdd_failure_pred.pkl', 'wb') as fw:
pickle.dump(rfc, fw)
模型加载
with open('hdd_failure_pred.pkl', 'rb') as fr:
new_rfc = pickle.load(fr)
模型再预测
new_Y_pred = new_rfc.predict(X_test)
new_prec = precision_score(Y_test, new_Y_pred)
print("The precision is {}".format(new_prec))
The precision is 0.8548387096774194
要分析分类模型的效果如何,还可以使用混淆矩阵来查看,混淆矩阵的横轴表示预测结果的各个类别,纵轴表示真实标签的类别,矩阵方格中的值就代表对应横纵坐标重叠的测试样本数量。
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
LABELS = ['Healthy', 'Failed']
conf_matrix = confusion_matrix(Y_test, Y_pred)
plt.figure(figsize =(6, 6))
sns.heatmap(conf_matrix, xticklabels = LABELS,
yticklabels = LABELS, annot = True, fmt ="d");
plt.title("Confusion matrix")
plt.ylabel('True class')
plt.xlabel('Predicted class')
plt.show()
如上内容是使用随机森林算法构建硬盘故障预测模型的过程演示,模型精度并不算高,有如下几个思路可以提升模型的精度:
(1)本案例只使用了Backblaze公司2020年的数据,您可以尝试使用更多的训练数据;
(2)本案例只使用了10个SMART属性作为特征,您可以尝试使用其他方法来构建特征;
(3)本案例使用了随机森林算法来训练模型,您可以尝试使用其他的机器学习算法;
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][
我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o