热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Spark面试题汇总大全

1RDD简介RDD是Spark最基本也是最根本的数据抽象,它具备像MapReduce等数据流模型的容错性,并且允许开发人员在大型集群上执行基于内存的计

1 RDD 简介

RDD是Spark最基本也是最根本的数据抽象,它具备像MapReduce等数据流模型的容错性,并且允许开发人员在大型集群上执行基于内存的计算。现有的数据流系统对两种应用的处理并不高效:一是迭代式算法;二是交互式数据挖掘工具。这两种情况下,将数据保存在内存中能够极大地提高性能。为了有效地实现容错,RDD提供了一种高度受限的共享内存,即RDD是只读的,并且只能通过其他RDD上的批量操作来创建。


2 分布式数据集容错

一般来说,分布式数据集的容错性有两种方式:数据检查点和记录数据的更新。我们面向的是大规模数据分析,数据检查点操作成本很高:需要通过数据中心的网络连接在机器之间复制庞大的数据集,而网络带宽往往比内存带宽低得多,同时还需要消耗更多的存储资源(在内存中复制数据可以减少需要缓存的数据量,而存储到磁盘则会降低应用程序速度)。所以,我们选择记录更新的方式。但是,如果更新太多,记录更新成本也不低。因此,RDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建RDD的一系列转换记录下来(即Lineage),以便恢复丢失的分区。

理解RDD的容错性

系谱图+检查点。

RDD可以通过系谱图机制来进行RDD的恢复。在RDD进行转换和动作的时候,会形成RDD的Lineage依赖链,当某一个RDD失效的时候,可以通过重新计算上游的RDD来生成丢失的RDD数据。

但是在spark计算里面,假如计算流程DAG特别长,服务器需要将整个DAG计算完成得出结果。如果在这很长的计算流程中突然中间算出的数据丢失了,spark又会根据RDD的依赖关系从头到尾计算一遍,这样子就很费性能,当然我们可以将中间的计算结果通过cache或者persist放到内存或者磁盘中,但是这样也不能保证数据完全不会丢失,存储的这个内存出问题了或者磁盘坏了,也会导致spark从头再根据RDD计算一遍,所以就有了checkpoint,其中checkpoint的作用就是将DAG中比较重要的中间数据做一个检查点将结果存储到一个高可用的地方(通常这个地方就是HDFS里面)。


3 每个RDD有5个主要的属性:


  • 1)一组分片(Partition),即数据集的基本组成单位。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。图3-1描述了分区存储的计算模型,每个分配的存储是由BlockManager实现的。每个分区都会被逻辑映射成BlockManager的一个Block,而这个Block会被一个Task负责计算。

在这里插入图片描述


  • 2)一个计算每个分区的函数。Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。详情请参阅3.4.5节。
  • 3)RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。
  • 4)一个Partitioner,即RDD的分片函数。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。使用Partitioner必须满足两个前提:1.rdd必须是形式 2.发生shuffle操作。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。
  • 5)一个列表,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。

4 创建RDD的方式

可以通过两种方式创建RDD:1)由一个已经存在的Scala集合创建。2)由外部存储系统的数据集创建,包括本地的文件系统,还有所有Hadoop支持的数据集,比如HDFS、Cassandra、HBase、Amazon S3等。

5,RDD的缓存能够在第一次计算完成后,将计算结果保存到内存、本地文件系统或者Tachyon中。通过缓存,Spark避免了RDD上的重复计算,能够极大地提升计算速度。但是,如果缓存丢失了,则需要重新计算。为了避免缓存丢失重新计算带来的开销,Spark又引入了检查点(checkpoint)机制。
RDD检查点(Checkpoint)是Spark Core计算过程中的容错机制。通过将RDD的数据与状态持久化,一旦计算过程出错,就可以从之前的状态直接恢复现场,而不必从头重算,大大提高了效率与可靠性。
缓存是在计算结束后,直接将计算结果通过用户定义的存储级别(存储级别定义了缓存存储的介质,现在支持内存、本地文件系统和Tachyon)写入不同的介质。而检查点不同,它是在计算完成后,重新建立一个Job来计算。为了避免重复计算,推荐先将RDD缓存,这样就能保证检查点的操作可以快速完成。


6,Spark如何根据DAG来生成计算任务

首先,根据依赖关系的不同将DAG划分为不同的阶段(Stage)。对于窄依赖,由于Partition依赖关系的确定性,Partition的转换处理就可以在同一个线程里完成,窄依赖被Spark划分到同一个执行阶段;对于宽依赖,由于Shuffle的存在,只能在parent RDD(s)Shuffle处理完成后,才能开始接下来的计算,因此宽依赖就是Spark划分Stage的依据,即Spark根据宽依赖将DAG划分为不同的Stage。在一个Stage内部,每个Partition都会被分配一个计算任务(Task),这些Task是可以并行执行的。Stage之间根据依赖关系变成了一个大粒度的DAG,这个DAG的执行顺序也是从前向后的。也就是说,Stage只有在它没有parent Stage或者parent Stage都已经执行完成后,才可以执行。

7,reduceByKey时会触发一个Shuffle(洗牌)的过程。在Shuffle开始之前,有一个本地聚合的过程,比如第三个分片的(e,1)(e,1)聚合成了(e,2)。Shuffle的结果是为下游的Task生成了三个分片,这三个分片就构成了ShuffledRDD。之后在做了一个聚合之后,就生成了结果的RDD。
在这里插入图片描述


8,Spark的Task分为两种:

1)org.apache.spark.scheduler.ShuffleMapTask
2)org.apache.spark.scheduler.ResultTask
简单来说,DAG的最后一个阶段会为每个结果的Partition生成一个ResultTask,其余所有的阶段都会生成ShuffleMapTask。生成的Task会被发送到已经启动的Executor上,由Executor来完成计算任务的执行,执行过程的实现在org.apache.spark.executor.Executor.TaskRunner#run。

9,Scheduler(任务调度)模块作为Spark Core的核心模块之一,主要包含两大部分,即DAGScheduler和TaskScheduler。
DAGScheduler主要负责分析用户提交的应用,并根据计算任务的依赖关系建立DAG,然后将DAG划分为不同的Stage(阶段),其中每个Stage由可以并发执行的一组Task构成,这些Task的执行逻辑完全相同,只是作用于不同的数据。而且DAG在不同的资源管理框架(即部署方式,包括Standalone、Mesos、YARN、Local、EC2等)下的实现是相同的。
在DAGScheduler将这组Task划分完成后,会将这组Task提交到TaskScheduler。

TaskScheduler通 过Cluster Manager在 集 群 中 的 某 个Worker的Executor上启 动任务。在Executor中运行的任务,如果缓存中没有计算结果,那么就需要开始计算,同时,计算的结果会回传到Driver或者保存在本地。在不同的资源管理框架下, TaskScheduler的实现方式是有差别的,但是最重要的实现是org.apache.spark.scheduler. TaskSchedulerImpl。对于Local、Standalone和Mesos来说,它们的TaskScheduler就是TaskSchedulerImpl;对于YARNCluster和YARN Client的TaskScheduler的实现也是继承自TaskSchedulerImpl。


10,任务调度模块涉及的最重要的三个类是:

DAGScheduler SchedulerBackend TaskScheduler
org.apache.spark.scheduler.SchedulerBackend是一个trait,作用是分配当前可用的资源,具体就是向当前等待分配计算资源的Task分配计算资源(即Executor),并且在分配的Executor上启动Task,完成计算的调度过程。它使用reviveOffers完成上述的任务调度。reviveOffers可以说是它最重要的实现。

org.apache.spark.scheduler.TaskScheduler也是一个trait,它的作用是为创建它的SparkContext调度任务,即从DAGScheduler接收不同Stage的任务,并且向集群提交这些任务,并为执行特别慢的任务启动备份任务。TaskScheduler是以后实现多种任务调度器的基础。org.apache.spark.scheduler.TaskSchedulerImpl是唯一实现。TaskSchedulerImpl会在以下几种场景下调用org.apache.spark.scheduler.Scheduler-Backend#reviveOffers:
1)有新任务提交时。2)有任务执行失败时。3)计算节点(即Executor)不可用时。4)某些任务执行过慢而需要为它重新分配资源时。
每个SchedulerBackend都会对应一个的唯一TaskScheduler,而它们都被Spark-Context创建和持有。


11,RDD的action操作

实际上是调用org.apache.spark.SparkContext#runJob来进行提交Job的:SparkContext实现了很多runJob,这些函数的区别就是调用参数的不同,不过这些runJob最后都会调用DAGScheduler的runJob:

12,TaskScheduler和DAGScheduler都是在SparkContext创建的时候创建的。其中,Taskcheduler是通过org.apache.spark.SparkContext#createTaskScheduler创建的。而DAGScheduler是直接调用的它的构造函数创建。只不过,DAGScheduler保存了TaskScheduler的引用,因此需要在TaskScheduler创建之后进行创建。

13,每 个TaskScheduler都 对 应 一 个SchedulerBackend。其 中,TaskScheduler负责Application的不同Job之间的调度,在Task执行失败时启动重试机制,并且为执行速度慢的Task启动备份的任务。而SchedulerBackend负责与Cluster Manager交互,取得该Application分配到的资源,并且将这些资源传给TaskScheduler,由TaskScheduler为Task最终分配计算资源。


14,Spark容错

容错(fault tolerance)指的是在一个系统的部分模块出现错误的情况下还能持续地对外提供服务;如果出现了服务质量的下降,这个下降也是和出错的严重性成正比的。对于没有容错的系统,即使一个微小的错误也可能会导致整个服务停止。

提到容错,不得不提一下容灾(或者称为灾难恢复,disaster recovery)。容灾技术是通过在异地建立和维护一个备份系统,利用地理上的分散性来保证数据对于灾难性事件的抵抗能力。容灾系统在实现中可以分为两个层次:数据容灾和应用容灾。数据容灾指建立一个异地的数据系统,作为本地关键应用数据的一个备份。应用容灾是在数据容灾的基础上,在异地建立一套完整的和本地生产系统相同的备份应用系统(可以是互为备份)。在灾难情况下,由远程系统迅速接管业务运行。容错和容灾都是为了实现系统的高可用性,容错是在系统的部分模块出现问题时的错误恢复机制;容灾则是在整个系统的层面,通过使用数据和应用的镜像,来实现服务的高可用性的。

接下来将从Master、Worker和Executor的异常退出出发,讨论Spark是如何处理的。

Master容错

Master出错,Spark 有四种恢复策略:ZOOKEEPER,FILESYSTEM,CUSTOM,NONE,可以在配置文件 Spark-env.sh 配置项 spark.deploy.recoveryMode 进行设置,默认为 NONE。

Standalone集群模式下的Master容错是通过ZooKeeper来完成的,即有多个Master,一个角色是Active,其他的角色是Standby。当处于Active的Master异常时,需要重新选择新的Master,通过ZooKeeper的ElectLeader功能实现,可以参考Paxos。

要使用ZooKeeper模式,需要在conf/spark-env.sh中为SPARK_DAEMON_JAVA_OPTS添加一些选项
在这里插入图片描述
Master还支持一种更简单的单点模式下的错误恢复,即当Master进程异常时,重启Master进程并从错误中恢复。具体方法是设置spark.deploy.recoveryMode属性的值为FILESYSTEM,并为spark.deploy.recoveryDirectory属性设置一个本地目录,用于存储必要的信息来进行错误恢复

Executor容错


  • Executor启动过程:集群中的master节点给应用分配运行所需要的资源之后,Worker节点会启动ExecutorRunner,ExecutorRunner会根据当前运行模式启动GoarseGrainedExecutorBackend进程,他会向Driver发送注册Executor信息。如果注册成功,GoarseGrainedExecutorBackend会在内部启动Executor。Executor由ExecutorRunner进行管理,当Executor出现异常时,由ExecutorRunner捕获该异常,并发送ExecutorStateChanged消息给Worker。
  • Worker接收到异常信息后,在Worker上的HandlerExecutorStateChanged方法会依据Executor的状态进行信息更新,同时将Executor信息发送给Master。
  • Master接收到Executor状态变化后,如果发现Executor异常退出,则调用Master.schedule方法,尝试获取可用的Worker节点并启动Executor。而这个Worker节点很可能不是之前的Worker节点。系统会尝试10次,如果超过10次,则标记该应用程序失败,并从集群中移除。这种设定是为了避免反复提交有Bug的程序,占用系统资源。

Worker容错


  • 1,Spark集群中采用Master/Slaver模式。Worker扮演从节点,会定时给Master发送心跳信息,让Master知道Worker的实时信息。不仅如此,Master还会感知注册的Worker节点是否出现超时。
  • 2,为了感知Worker是否超时未发送心跳信息。在Master接收到Worker心跳的同时,在其启动方法onStart中启动Worker超时检测线程。
  • 3,当Worker出现异常时,会依据自身运行的是Executor还是Driver进行处理:
    • (1)如果运行的是Executor,Master会先把Worker上运行的Executor信息发送消息ExecutorUpdated给对应的Driver,告知Executor已丢失,同时把Executor从运行的应用程序列表中删除。之后在进行Executor异常的处理流程。
    • (2)如果运行的是Driver,则判断是否设置了重新启动。如果需要,则调用Master.schedule方法进行调度,分配合适节点重启Driver。否则就删除该应用。

15 AppClient和Driver Client

Client,它的任务其实是由AppClient和DriverClient共同完成的。AppClient是一个允许app(Client)和Spark集群通信的中间人,接受master URL、app的信息、一个集群事件的监听器以及事件监听的回调函数,主要和Master交互App相关的信息,DriverClient主要用于和Master交互Driver相关的信息,比如启动、停止及运行状况等


16 Spark Shuffle

之所以需要Shuffle,还是因为具有某种共同特征的一类数据需要最终汇聚(aggregate)到一个计算节点上进行计算。这些数据分布在各个存储节点上并且由不同节点的计算单元处理。

在Spark 1.0以前,Spark只支持Hash Based Shuffle。因为在很多运算场景中并不需要排序,因此多余的排序只能使性能变差,比如Hadoop的Map Reduce就是这么实现的,也就是Reducer拿到的数据都是已经排好序的。实际上Spark的实现很简单:每个Shuffle Map Task根据key的哈希值,计算出每个key需要写入的Partition然后将数据单独写入一个文件,这个Partition实际上就对应了下游的一个Shuffle Map Task或者Result Task。因此下游的Task在计算时会通过网络(如果该Task与上游的Shuffle Map Task运行在同一个节点上,那么此时就是一个本地的硬盘读写)读取这个文件并进行计算。
在这里插入图片描述

缺点:
1)每个节点可能会同时打开多个文件,每次打开文件都会占用一定内存。假设每个Write Handler的默认需要100KB的内存,那么同时打开这些文件需要50GB的内存,对于一个集群来说,还是有一定的压力的。尤其是如果Shuffle Map Task和下游的Task同时增大10倍,那么整体的内存就增长到5TB。
2)从整体的角度来看,打开多个文件对于系统来说意味着随机读,尤其是每个文件比较小但是数量非常多的情况。而现在机械硬盘在随机读方面的性能特别差,非常容易成为性能的瓶颈。如果集群依赖的是固态硬盘,也许情况会改善很多,但是随机写的性能肯定不如顺序写的。
为了解决Shuffle过程产生文件过多的问题,在Spark 0.8.1中加入了ShuffleConsolidate Files机制。它的主要目标是减少Shuffle过程产生的文件。若使用这个功能,则需要将spark.shuffle.consolidateFiles设置为true。在Spark 1.2.0中,这个功能还是没有成为默认选项。官方给出的说法是这个功能还欠稳定。接下来介绍这个机制的实现原理。对于运行在同一个Core的Shuffle Map Task,第一个Shuffle Map Task会创建一个文件;之后的就会将数据追加到这个文件上而不是新建一个文件。因此文件数量就从number(shuffle_map_task)*number(following_task)变 成 了number(cores)*number (following_task)。当然,如果每个Core都只运行一个Shuffle Map Task,那么就和原来的机制一样了。但是Shuffle Map Task 明显多于Core数量或者说每个Core都会运行多个Shuffle Map Task,所以这个实现能够显著减少文件的数量。相当于每个core为每个reduce task创建一个文件。
在这里插入图片描述

缺点:
Shuffle Consolidate的机制虽然在某些场景下缓解了Shuffle过程产生文件过多的问题,但是还没有彻底解决7.1.2节中提出的两个问题,即同时打开多个文件造成Writer Handler内存使用过大的问题和产生多个文件造成的随机读从而影响性能的问题。
Spark采用了一个很聪明的做法,即建立另外一套Shuffle机制,让用户自己选择。Spark 1.0建立了Shuffle Pluggable的框架,通过实现一个接口来很容易地实现第三方的external Shuffle Service。而Spark 1.1实现了Sort Based Shuffle;而Spark1.2里,Sort Based Shuffle取代Hash Based Shuffle成为Shuffle的默认选项。

为了更好地解决这个问题,Spark 1.1 引入了Sort BasedShuffle。首先,每个Shuffle Map Task不会为每个Reducer生成一个单独的文件;相反,它会将所有的结果写到一个文件里,同时会生成一个Index文件,Reducer可以通过这个Index文件取得它需要处理的数据。避免产生大量文件的直接收益就是节省了内存的使用和顺序Disk IO带来的低延时。节省内存的使用可以减少GC的风险和频率。而减少文件的数量可以避免同时写多个文件给系统带来的压力。
在这里插入图片描述

Shuffle Map Task会按照key相对应的Partition ID进行Sort,其中属于同一个Partition的key不会Sort。因为对于不需要Sort的操作来说,这个Sort是负收益的;要知道之前Spark刚开始使用Hash Based的Shuffle而不是Sort Based就是为了避免Hadoop Map Reduce对于所有计算都会Sort的性能损耗。对于那些需要Sort的运算,比如sortByKey,这个Sort在Spark 1.2.0里还是由Reducer完成的。

17 groupbykey、reducebykey以及aggregateByKey
groupbykey是全局聚合算子,将所有map task中的数据都拉取到shuffle中将key相同的数据进行聚合,它存在很多弊端,例如:将大量的数据进行网络传输,浪费大量的资源,最重要的是如果数据量太大还会出现GC和OutOfMemoryError的错误,如果数据某个key的数据量远大于其他key的数据,在进行全局聚合的时候还会出现数据倾斜的问题。
所以在实际的项目中要谨慎使用groupbykey,随着数据量的增加,groupbykey暴露出来的问题会越来越多。
reducebykey是在map阶段进行本地聚合以后才会到shuffle中进行全局聚合,相当于是进入shuffle之前已经做了一部分聚合,那么它的网络传输速度会比groupbykey快很多而且占用资源也会减少很多,但是算子本身就如它的名字一样,主要是进行计算的将相同key的数据进行计算,返回计算结果。
但是实际的项目中在进行聚合之后我们不一定只是要计算,还会找聚合后某个字段的最大值,最小值等等操作,groupbykey聚合后返回的是(K,Iterable[V]),我们可以把iterable[V]这个集合的数据进行二次处理来实现我们实际的项目需求,但是上面已经提到了groupbykey的诸多问题,reducebykey也是只有在单纯的对数据进行计算的时候才能和groupbykey有等价效果。既想像reducebykey那样进行本地聚合,又想像groupbykey那样返回一个集合便于我们操作。aggregatebykey和reducebykey一样首先在本地聚合,然后再在全局聚合。它的返回值也是由我们自己设定的。

18 Spark内存溢出
(1)driver端的内存溢出
可以增大driver的内存参数:spark.driver.memory (default 1g)
这个参数用来设置Driver的内存。在Spark程序中,SparkContext,DAGScheduler都是运行在Driver端的。对应rdd的Stage切分也是在Driver端运行,如果用户自己写的程序有过多的步骤,切分出过多的Stage,这部分信息消耗的是Driver的内存,这个时候就需要调大Driver的内存。
(2)map过程产生大量对象导致内存溢出
这种溢出的原因是在单个map中产生了大量的对象导致的,例如:rdd.map(x=>for(i <- 1 to 10000) yield i.toString),这个操作在rdd中,每个对象都产生了10000个对象,这肯定很容易产生内存溢出的问题。针对这种问题,在不增加内存的情况下,可以通过减少每个Task的大小,以便达到每个Task即使产生大量的对象Executor的内存也能够装得下。具体做法可以在会产生大量对象的map操作之前调用repartition方法,分区成更小的块传入map。例如:rdd.repartition(10000).map(x=>for(i <- 1 to 10000) yield i.toString)。面对这种问题注意,不能使用rdd.coalesce方法,这个方法只能减少分区,不能增加分区,不会有shuffle的过程。
(3)数据不平衡导致内存溢出
数据不平衡除了有可能导致内存溢出外,也有可能导致性能的问题,解决方法和上面说的类似,就是调用repartition重新分区。这里就不再累赘了。
(4)shuffle后内存溢出
shuffle内存溢出的情况可以说都是shuffle后,单个文件过大导致的。在Spark中,join,reduceByKey这一类型的过程,都会有shuffle的过程,在shuffle的使用,需要传入一个partitioner,大部分Spark中的shuffle操作,默认的partitioner都是HashPatitioner,默认值是父RDD中最大的分区数,这个参数通过spark.default.parallelism控制(在spark-sql中用spark.sql.shuffle.partitions) , spark.default.parallelism参数只对HashPartitioner有效,所以如果是别的Partitioner或者自己实现的Partitioner就不能使用spark.default.parallelism这个参数来控制shuffle的并发量了。如果是别的partitioner导致的shuffle内存溢出,就需要从partitioner的代码增加partitions的数量。
(5)standalone模式下资源分配不均匀导致内存溢出
在standalone的模式下如果配置了–total-executor-cores 和 --executor-memory 这两个参数,但是没有配置–executor-cores这个参数的话,就有可能导致,每个Executor的memory是一样的,但是cores的数量不同,那么在cores数量多的Executor中,由于能够同时执行多个Task,就容易导致内存溢出的情况。这种情况的解决方法就是同时配置–executor-cores或者spark.executor.cores参数,确保Executor资源分配均匀。
(6)使用rdd.persist(StorageLevel.MEMORY_AND_DISK_SER)代替rdd.cache()
rdd.cache()和rdd.persist(Storage.MEMORY_ONLY)是等价的,在内存不足的时候rdd.cache()的数据会丢失,再次使用的时候会重算,而rdd.persist(StorageLevel.MEMORY_AND_DISK_SER)在内存不足的时候会存储在磁盘,避免重算,只是消耗点IO时间。


推荐阅读
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了在Oracle数据库中创建序列时如何选择cache或nocache参数。cache参数可以提高序列的存取速度,但可能会导致序列丢失;nocache参数可以避免序列丢失,但在高并发访问时可能导致性能问题。文章详细解释了两者的区别和使用场景。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
author-avatar
雨季莫犹忆
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有