Spark 的容错机制:保障数据处理的稳定性与高效性

Spark 的介绍与搭建:从理论到实践_spark环境搭建-CSDN博客

Spark 的Standalone集群环境安装与测试-CSDN博客

PySpark 本地开发环境搭建与实践-CSDN博客

Spark 程序开发与提交:本地与集群模式全解析-CSDN博客

Spark on YARN:Spark集群模式之Yarn模式的原理、搭建与实践-CSDN博客

Spark 中 RDD 的诞生:原理、操作与分区规则-CSDN博客

Spark 中的 RDD 分区的设定规则与高阶函数、Lambda 表达式详解-CSDN博客

RDD 算子全面解析:从基础到进阶与面试要点-CSDN博客

PySpark 数据处理实战:从基础操作到案例分析-CSDN博客

目录

一、Spark 容错机制概述

1、各个软件为了防止数据丢失的解决方案

2、Spark如何保障数据的安全

二、RDD 持久化机制

(一)cache 算子

(二)persist 算子

(三)unpersist 算子

(四)示例代码分析

三、RDD 检查点机制

(一)功能与原理

(二)适用场景

四、RDD 的 cache、persist 持久化机制和 checkpoint 检查点机制的区别

五、将日志分析案例进行优化

六、总结


        在大数据处理领域,Spark 作为一款强大的分布式计算框架,面临着数据丢失和性能优化的双重挑战。为了确保数据的安全性和处理效率,Spark 构建了一套完善的容错机制。本文将深入探讨 Spark 的容错机制,包括 RDD 的持久化机制(persist 和 cache 算子)以及检查点机制(checkpoint),并分析它们的特点、适用场景以及相互之间的区别。

一、Spark 容错机制概述

1、各个软件为了防止数据丢失的解决方案

操作日志:

将内存变化操作日志追加记录在一个文件中,下一次读取文件对内存重新操作
  - NAMENODE:元数据的操作日志记录在edits
  - MySQL:日志记录binlog ()


 副本机制:

将数据构建多份冗余副本
  - HDFS:构建每个数据块的3个副本


依赖关系:

每份数据保留与其他数据之间的一个转换关系
  - RDD:保留RDD与其他RDD之间的依赖关系

2、Spark如何保障数据的安全

        每个RDD在构建数据时,会根据自己来源一步步导到数据来源,然后再一步步开始构建RDD数据。

问题:如果一个RDD被触发多次,这个RDD就会按照依赖关系被构建多次,性能相对较差,怎么解决?

例如:日志分析的时候,三个问题,tupleRdd 之前的所有操作都要执行三次,每次读取100M多的数据,效率非常的低

  • 第一次:一定会通过血脉构建这个RDD的数据
  • 希望从第二次开始,就不要重复构建,直接使用第一个构建的内容
  • 实现:Spark持久化机制:主动将RDD进行保存,供多次使用,避免重复构建

二、RDD 持久化机制

(一)cache 算子

  • 功能:将 RDD 缓存在内存中,以便后续多次使用时无需重新计算。
  • 语法cache()
  • 本质:底层实际调用的是 persist(StorageLevel.MEMORY_ONLY),即只尝试将 RDD 缓存在内存。但如果内存资源不足,缓存操作可能会失败。
  • 场景:适用于资源充足且确定 RDD 只需在内存中缓存的情况,例如对于一些频繁使用且数据量较小能够完全容纳在内存中的 RDD,可以使用 cache 算子提高数据读取速度。

(二)persist 算子

  • 功能:能够将 RDD(包含其依赖关系)进行缓存,并且可以根据需求自行指定缓存的级别,这是它与 cache 算子的主要区别。
  • 语法persist(StorageLevel)
  • 级别
    • 将 RDD 缓存在磁盘中
      • StorageLevel.DISK_ONLY = StorageLevel(True, False, False, False):将庞大且暂时不急需使用的 RDD 放入磁盘,释放 Executor 内存。
      • StorageLevel.DISK_ONLY_2 = StorageLevel(True, False, False, False, 2):在磁盘中多存储一个缓存副本,提高数据的冗余性和可用性。
      • StorageLevel.DISK_ONLY_3 = StorageLevel(True, False, False, False, 3):类似地,存储三个副本。
        StorageLevel.DISK_ONLY = StorageLevel(True, False, False, False)
        StorageLevel.DISK_ONLY_2 = StorageLevel(True, False, False, False, 2)
        StorageLevel.DISK_ONLY_3 = StorageLevel(True, False, False, False, 3)

    • 将 RDD 缓存在内存中
      • StorageLevel.MEMORY_ONLY = StorageLevel(False, True, False, False):仅使用内存进行缓存,常用于高频使用且数据量不大能适应内存容量的 RDD。
      • StorageLevel.MEMORY_ONLY_2 = StorageLevel(False, True, False, False, 2):增加一个内存缓存副本。
        StorageLevel.MEMORY_ONLY = StorageLevel(False, True, False, False)
        StorageLevel.MEMORY_ONLY_2 = StorageLevel(False, True, False, False, 2)

    • 将 RDD 优先缓存在内存中,如果内存不足,就缓存在磁盘中
      • StorageLevel.MEMORY_AND_DISK = StorageLevel(True, True, False, False):对于高频使用的大 RDD 较为合适,先利用内存缓存,当内存空间不足时,自动将多余数据溢出到磁盘。
      • StorageLevel.MEMORY_AND_DISK_2 = StorageLevel(True, True, False, False, 2):多一个缓存副本。
        StorageLevel.MEMORY_AND_DISK = StorageLevel(True, True, False, False)
        StorageLevel.MEMORY_AND_DISK_2 = StorageLevel(True, True, False, False, 2)
        

    • 使用堆外内存StorageLevel.OFF_HEAP = StorageLevel(True, True, True, False, 1):提供了除内存和磁盘之外的存储选择,可利用堆外内存资源。
      StorageLevel.OFF_HEAP = StorageLevel(True, True, True, False, 1)
      

    • 使用序列化StorageLevel.MEMORY_AND_DISK_DESER = StorageLevel(True, True, False, True):在内存和磁盘缓存时采用序列化方式,有助于减少内存占用,但在读取时需要进行反序列化操作,会有一定性能开销。
      StorageLevel.MEMORY_AND_DISK_DESER = StorageLevel(True, True, False, True)

  • 场景:根据实际的资源状况,灵活地将 RDD 缓存在不同的存储介质中或者设置多个缓存副本,以平衡内存使用、数据读取速度和数据安全性。例如在内存和磁盘资源都较为充裕但内存使用较为紧张的情况下,可以选择 MEMORY_AND_DISK 级别进行缓存,既能保证数据的快速读取,又能在内存不足时利用磁盘空间。

  • 总结:Spark的StorageLevel共有9个缓存级别

DISK_ONLY:缓存入硬盘。这个级别主要是讲那些庞大的Rdd,之后仍需使用但暂时不用的,放进磁盘,腾出Executor内存。
DISK_ONLY_2:多一个缓存副本。
MEMORY_ONLY:只使用内存进行缓存。这个级别最为常用,对于马上用到的高频rdd,推荐使用。
MEMORY_ONLY_2:多一个缓存副本。
MEMORY_AND_DISK:先使用内存,多出来的溢出到磁盘,对于高频的大rdd可以使用。
MEMORY_AND_DISK_2:多一个缓存副本。
OFF_HEAP:除了内存、磁盘,还可以存储在OFF_HEAP

常用的:

项目中经常使用

MEMORY_AND_DISK_2

MEMORY_AND_DISK_DESER

(三)unpersist 算子

  • 功能:释放已缓存的 RDD,回收缓存占用的资源。
  • 语法unpersist,还可以使用 unpersist(blocking=True),表示等待 RDD 释放完资源后再继续执行下一步操作。
  • 场景:当确定某个 RDD 不再被使用,且后续还有大量代码需要执行时,及时调用 unpersist 算子将其数据从缓存中释放,避免资源的浪费。需要注意的是,如果不手动释放缓存,在 Spark 程序结束时,系统也会自动清理该程序中的所有缓存内存。

(四)示例代码分析

以下是一个简单的 Spark 程序示例,展示了如何使用 cache 、 persist 和 unpersist 算子:

import os
import time# 导入pyspark模块
from pyspark import SparkContext, SparkConf
from pyspark.storagelevel import StorageLevelif __name__ == '__main__':# 配置环境os.environ['JAVA_HOME'] = 'C:/Program Files/Java/jdk1.8.0_241'# 配置Hadoop的路径,就是前面解压的那个路径os.environ['HADOOP_HOME'] = 'D:/hadoop-3.3.1'# 配置base环境Python解析器的路径os.environ['PYSPARK_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'  # 配置base环境Python解析器的路径os.environ['PYSPARK_DRIVER_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'# 获取 conf 对象# setMaster  按照什么模式运行,local  bigdata01:7077  yarn#  local[2]  使用2核CPU   * 你本地资源有多少核就用多少核#  appName 任务的名字conf = SparkConf().setMaster("local[*]").setAppName("spark的持久化机制")# 假如我想设置压缩# conf.set("spark.eventLog.compression.codec","snappy")# 根据配置文件,得到一个SC对象,第一个conf 是 形参的名字,第二个conf 是实参的名字sc = SparkContext(conf=conf)print(sc)fileRdd = sc.textFile("../resources/1.dat")# cache 是转换算子#cacheRdd = fileRdd.cache()cacheRdd = fileRdd.persist(StorageLevel.MEMORY_AND_DISK_2)print(type(cacheRdd))cacheRdd.foreach(lambda x: print(x))time.sleep(20)cacheRdd.unpersist(blocking=True)time.sleep(10)# 使用完后,记得关闭sc.stop()# unpersist(blocking=True):等RDD释放完再继续下一步
# blocking = True:阻塞

        在上述代码中,首先配置了 Spark 运行所需的环境变量,然后创建了 SparkConf 和 SparkContext 对象。通过 textFile 方法读取文本文件创建了 fileRdd,接着使用 persist 算子将其缓存到内存和磁盘,并设置了两个副本。之后对缓存的 cacheRdd 进行了遍历操作,模拟了对 RDD 的使用。在暂停 20 秒后,调用 unpersist 算子释放缓存,最后关闭 SparkContext。

三、RDD 检查点机制

(一)功能与原理

  • 功能:将 RDD 的数据(不包含 RDD 依赖关系)存储在可靠的存储系统(如 HDFS)中。可以将其类比为虚拟机中的快照,作为数据处理过程中的一个重要里程碑。
  • 设置与使用
    • 首先需要设置一个检查点目录,例如:sc.setCheckpointDir("../datas/chk/chk1")
    • 然后对需要设置检查点的 RDD 调用 checkpoint() 方法,如:rs_rdd.checkpoint()。需要注意的是,一定要在触发该 RDD 的算子之前调用 checkpoint() 方法,否则检查点中可能没有数据。
  • 注意事项:启用检查点机制后,在代码执行过程中会专门多一个 job,用于将 RDD 数据持久化存储到 HDFS 中。

(二)适用场景

        适用于对 RDD 数据安全性要求极高,但对性能要求相对不那么苛刻的场景。例如在一些数据处理任务中,数据的准确性和完整性至关重要,不容许因为任何故障导致数据丢失或错误,即使这可能会带来一定的性能开销,如在金融数据处理、关键业务数据分析等领域。

四、RDD 的 cache、persist 持久化机制和 checkpoint 检查点机制的区别

  1. 存储位置
    • persist:可以将 RDD 缓存在内存或者磁盘中,根据指定的缓存级别灵活选择存储介质。
    • checkpoint:将 RDD 的数据存储在文件系统磁盘(通常是 HDFS)中,提供更可靠的持久化存储。
  2. 生命周期
    • persist:当代码中遇到了 unpersist 算子调用或者整个 Spark 程序结束时,缓存会被自动清理,释放资源。
    • checkpoint:检查点的数据不会被自动清理,需要手动删除,这使得数据在长时间内都能保持可用状态,有利于数据的长期保存和回溯。
  3. 存储内容
    • persist:会保留 RDD 的血脉关系,即与其他 RDD 的依赖关系。这样在缓存丢失时,可以依据依赖关系重新构建 RDD,恢复数据。
    • checkpoint:会斩断 RDD 的血脉关系,仅存储 RDD 的数据本身。这意味着一旦检查点数据可用,就不再依赖之前的 RDD 依赖链,简化了数据恢复过程,但也失去了基于依赖关系的灵活重建能力。

五、将日志分析案例进行优化

 对前文的一个案例进行优化

import os
import re# 导入pyspark模块
from pyspark import SparkContext, SparkConf
import jieba
from pyspark.storagelevel import StorageLevelif __name__ == '__main__':# 配置环境os.environ['JAVA_HOME'] = 'D:/Program Files/Java/jdk1.8.0_271'# 配置Hadoop的路径,就是前面解压的那个路径os.environ['HADOOP_HOME'] = 'D:/hadoop-3.3.1/hadoop-3.3.1'# 配置base环境Python解析器的路径os.environ['PYSPARK_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'  # 配置base环境Python解析器的路径os.environ['PYSPARK_DRIVER_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'# 获取 conf 对象# setMaster  按照什么模式运行,local  bigdata01:7077  yarn#  local[2]  使用2核CPU   * 你本地资源有多少核就用多少核#  appName 任务的名字conf = SparkConf().setMaster("local[*]").setAppName("第一个Spark程序")# 假如我想设置压缩# conf.set("spark.eventLog.compression.codec","snappy")# 根据配置文件,得到一个SC对象,第一个conf 是 形参的名字,第二个conf 是实参的名字sc = SparkContext(conf=conf)fileRdd = sc.textFile("../datas/sogou.tsv")print(fileRdd.count())print(fileRdd.first())listRdd = fileRdd.map(lambda line: re.split("\\s+", line))filterList = listRdd.filter(lambda l1: len(l1) == 6)# 这个结果只获取而来时间 uid 以及热词,热词将左右两边的[] 去掉了tupleRdd = filterList.map(lambda l1: (l1[0], l1[1], l1[2][1:-1]))# 将tupleRdd 缓存到内存中tupleRdd.cache()#tupleRdd.persist(storageLevel=StorageLevel.MEMORY_AND_DISK)# 求热词wordRdd = tupleRdd.flatMap(lambda t1: jieba.cut_for_search(t1[2]))filterRdd2 = wordRdd.filter(lambda word: len(word.strip()) != 0 and word != "的").filter(lambda word: re.fullmatch("[\u4e00-\u9fa5]+", word) is not None)# filterRdd2.foreach(print)result = filterRdd2.map(lambda word: (word, 1)).reduceByKey(lambda sum, num: sum + num).sortBy(keyfunc=lambda tup: tup[1], ascending=False).take(10)for ele in result:print(ele)# 第二问:  ((uid,"功夫")  10)# [(time,uid,"中华人民"),()]def splitWord(tupl):li1 = jieba.cut_for_search(tupl[2]) # 中国 中华 共和国li2 = list()for word in li1:li2.append(((tupl[1], word),1))return li2newRdd = tupleRdd.flatMap(splitWord)#newRdd.foreach(print)reduceByUIDAndWordRdd = newRdd.reduceByKey(lambda sum,num : sum + num)# reduceByUIDAndWordRdd.foreach(print)valList =reduceByUIDAndWordRdd.values()print(valList.max())print(valList.min())print(valList.mean()) # 中位数print(valList.sum() / valList.count()) ## 第三问 统计一天每小时点击量并按照点击量降序排序reductByKeyRDD = tupleRdd.map(lambda tup: (tup[0][0:2],1)).reduceByKey(lambda sum,num : sum + num)sortRdd = reductByKeyRDD.sortBy(keyfunc=lambda tup:tup[1],ascending=False)listNum = sortRdd.take(24)for ele in listNum:print(ele)tupleRdd.unpersist(blocking=True)# 使用完后,记得关闭sc.stop()

 将任务运行,运行过程中,发现内存中存储了50M的缓存数据

 适用场景:RDD需要多次使用,或者RDD是经过非常复杂的转换过程所构建。

一般缓存的RDD都是经过过滤,经过转换之后重复利用的rdd,可以添加缓存,否则不要加。

六、总结

        Spark 的容错机制通过多种方式保障了数据处理的稳定性和高效性。RDD 的持久化机制(包括 cache 和 persist 算子)为频繁使用的 RDD 提供了灵活的缓存策略,能够有效减少重复计算,提高处理效率。而检查点机制则侧重于数据的高安全性存储,在面对可能的缓存丢失或系统故障时,确保数据的完整性和可用性。在实际应用中,需要根据数据处理任务的特点、资源状况以及对数据安全性和性能的要求,合理选择使用持久化机制和检查点机制,以充分发挥 Spark 框架的优势,构建可靠高效的大数据处理应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/885794.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux内核编程(二十)RTC子系统一驱动rx8010

本文目录 一、基础知识点1.什么是RTC?2. RTC方案3. 电路原理图 二、RTC芯片(RX8010)移植三、关于时间的一些命令四、应用层使用1. 使用RTC驱动2. 使用time.h库(额外知识点) 一、基础知识点 1.什么是RTC? R…

GESP4级考试语法知识(贪心算法(一))

海盗船代码&#xff1a; #include<iostream> #include<algorithm> using namespace std; int data[21]; int main() {int n;cin>>n;for(int i0;i<n;i)cin>>data[i];sort(data,datan);int temp0,sum0;for(int i0;i<n;i){tempdata[i];if(temp>…

036 RabbitMQ消息确认 死信队列 延时队列

文章目录 生产者确认模式application.propertiesMessageController.javaMessageConfirmRallback.java 生产者回退模式application.propertiesMessageConfirmRallback.javaMessageController.java 消费者手动确认application.propertiesConsumerAckQueueListener.java 死信队列延…

Unity 插件 - Project窗口资源大小显示

Unity 插件 - Project窗口资源大小显示 &#x1f354;功能&#x1f32d;安装 &#x1f354;功能 &#x1f4a1;.显示Project Assets 和Packages下所有文件的大小&#xff08;右侧显示&#xff09; &#x1f4a1;.统计选中文件夹及其子文件夹下所有文件的大小并显示&#xff08…

Maven 中央仓库地址 mvnrepository.com

下载一些 jar 包驱动&#xff0c;不需用去官网下了&#xff0c;直接去 Maven 中央仓库&#xff0c;高效、简单 Maven 中央仓库地址 https://mvnrepository.com/open-source 我们下期见&#xff0c;拜拜&#xff01;

制作自己的刷题小题库,提高刷题效率

日常刷题 乱序/背题多种模式 组队刷题 查看小组的刷题统计 在线考试 创建考试多人同时答题 ----这是一条分割线----- 土著刷题&#xff0c;是一款可以导入题库的在线刷题学习小&#x1f34a;序&#xff0c;提供一套以【搭建题库-组建小组-刷题练习-在线考试】为中心的完整服务…

WPF在MVVM模式下怎么实现导航功能

在mvvm的模式下wpf通过frame实现页面跳转_哔哩哔哩_bilibili 视频讲解同步可观看 如下图&#xff0c;我们要实现点击左侧的菜单&#xff0c;在右侧展示不同的页面 实现代码如下&#xff1a; 一、如何从主窗体跳转到页面。 1、在mainwindow.xaml的菜单栏代码里加入如下代码 …

VSCode中python插件安装后无法调试

问题 VSCode中python插件安装后无法调试&#xff0c;如下&#xff0c;点击调试&#xff0c;VScode中不报错&#xff0c;也没有调试 解决方法 1、查看配置 打开所在路径 2、拷贝 将整个文件夹拷贝到vscode默认路径下 3、问题解决 再次调试&#xff0c;可以正常使用了…

光模块基础知识

1. 光模块的封装 光模块是光收发模块的简称&#xff0c;主要根据不同的外型来区分&#xff0c;而在同一外型中&#xff0c;又有着多种规格&#xff1b;在数据通信领域&#xff0c;最常见的光模块&#xff08;根据外型区分&#xff09;分别是SFF、GBIC、SFP、和XFP、QSFP 、XEN…

【GPTs】MJ Prompt Creator:轻松生成创意Midjourney提示词

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 &#x1f4af;GPTs指令&#x1f4af;前言&#x1f4af;MJ Prompt Creator主要功能适用场景优点缺点 &#x1f4af; 小结 &#x1f4af;GPTs指令 中文翻译&#xff1a; 任务说明 您是一款为幻灯片工…

uniapp路由与页面跳转详解:API调用与Navigator组件实战

UniApp路由与页面跳转详解&#xff1a;API调用与Navigator组件实战 路由 uniapp页面路由为框架统一管理&#xff0c;开发者需要在page.json里面配置每个路由页面的路径及页面样式。 路由跳转 uniapp有两种页面路由跳转方式&#xff0c;调用API跳转和navigator组件跳转。 调…

机器学习 笔记

特征值提取 字典 from sklearn.extaction import DictVectorizer mDictVectorizer(sparseFalse)#sparse是否转换成三元组形式 data[], #传入字典数据 data1model.fit_transform(data) #使用API 英文特征值提取 from sklearn.feature_extraction.text import CountVe…

【智谱开放平台-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

如何搭建自动化测试框架

软件测试资料领取&#xff1a;[内部资源] 想拿年薪40W的软件测试人员&#xff0c;这份资料必须领取~ 软件测试面试刷题工具&#xff1a;软件测试面试刷题【800道面试题答案免费刷】 关于测试框架的好处&#xff0c;比如快速回归提高测试效率&#xff0c;提高测试覆盖率等这里…

学Linux的第八天

目录 管理进程 概念 程序、进程、线程 进程分类 查看进程 ps命令 unix 风格 bsd风格 GNU风格 top命令 格式 统计信息区 进程信息区&#xff1a;显示了每个进程的运行状态 kill命令 作用 格式 管理进程 概念 程序、进程、线程 程序&#xff1a; 二进制文件&…

使用Matlab建立决策树

综述 除了神经网络模型以外&#xff0c;树模型及基于树的集成学习模型是较为常用的效果较好的预测模型。我们以下先构建一个决策树模型。 决策树算法的优点如下&#xff1a;1、 决策树易于理解和实现&#xff0c;用户在学习过程中不需要了解过多的背景知识&#xff0c;其能够…

安卓主板_基于联发科MTK MT8788平台平板电脑方案_安卓核心板开发板定制

联发科MT8788安卓核心板平台介绍&#xff1a; MTK8788设备具有集成的蓝牙、fm、wlan和gps模块&#xff0c;是一个高度集成的基带平台&#xff0c;包括调制解调器和应用处理子系统&#xff0c;启用LTE/LTE-A和C2K智能设备应用程序。该芯片集成了工作在2.0GHz的ARM Cortex-A73、最…

思科模拟器路由器配置实验

一、实验目的 了解路由器的作用。掌握路由器的基本配置方法。掌握路由器模块的使用和互连方式。 二、实验环境 设备&#xff1a; 2811 路由器 1 台计算机 2 台Console 配置线 1 根网线若干根 拓扑图&#xff1a;实验拓扑图如图 8-1 所示。计算机 IP 地址规划&#xff1a;如表…

Python酷库之旅-第三方库Pandas(206)

目录 一、用法精讲 961、pandas.IntervalIndex.mid属性 961-1、语法 961-2、参数 961-3、功能 961-4、返回值 961-5、说明 961-6、用法 961-6-1、数据准备 961-6-2、代码示例 961-6-3、结果输出 962、pandas.IntervalIndex.length属性 962-1、语法 962-2、参数 …

【前端】CSS入门笔记+案例

目录 CSS css 的语法 1.字体大小 font-size 2.背景颜色 backgrount-color 3.背景的宽高 w h css的三种引入方式 1.内嵌式 2.外联式 3.行内式 选择器 1.标签选择器 2.类选择器 3.id选择器 4.通配符选择器 字体和文本样式 1.字体样式 1.1字体大小 font-size 1.…