python面试常见问题,软件测试python面试题
1.Python1、数据类型、变量和不可变:tuple、str、int、float、bool变量:list、dict、set2、轻拷贝通常只拷贝对象本身,而改变拷贝中的原对象不会改变,而深拷贝不仅会拷贝对象,还会递归地拷贝与对象关联的对象。更改对象的副本时,不会影响原始对象。深度复制会导致两个问题:如果一个对象直接或间接引用自己,就会导致无休止的递归复制(被复制的对象可以用memo字典保存,这样就避免了刚才说的自引用递归的问题)。它还可以复制最初设计为由多个对象共享的数据。test=[1,2,3,[a , b ]]import copy a=copy . copy(test)b=copy . deep copy(test)a[1,2,3,[a , b]] a[3][1]=c a[1,2,3,[a , c]] test[1,2,3,[a ,C]] b [1,2,3,[a , b ]]b[3][0]= b b b[1,2,2
#数据结构只有一层:类型可变时,浅拷贝id不一致;当类型不可变时,浅表副本id是一致的。A=[1,2,3]B=copy . copy(a)ID(a)1996137345800 ID(B)1996137414600 a=(1,2,3)B=copy . copy(a)ID(a)1996136097400 ID(B)19961360974003。Python是如何实现内存管理的?Python提供自动内存管理,内存空间的分配和释放由Python解释器运行时自动执行。以CPython解释器为例,其内存管理有三个关键点:引用计数、标签清理和代收集。
引用计数
Python中的每个对象实际上都是一个PyObject结构,内部有一个名为ob_refcnt的引用计数器成员变量。在程序运行期间,ob_refcnt的值将被更新,以反映有多少变量被引用到该对象。当一个对象的引用计数值为0时,它的内存将被释放。
以下情况将导致引用计数增加1:
被引用的对象将创建的对象作为参数传递给函数,对象作为元素存储在容器中。以下情况将导致参考计数减少1:
Del语句用来表明被删除的对象引用被重新分配给其他对象的对象引用。当一个对象离开它的作用域时,保存该对象本身的容器就被销毁了。删除对象时,可以通过sys模块的getrefcount函数获取对象的引用计数。计数的内存管理方式遇到循环引用会很致命,需要其他垃圾收集算法来补充。
清理:
CPython使用“标记和清除”算法来解决容器类型可能导致的循环引用问题。该算法在垃圾收集中分为两个阶段:
标记阶段,遍历所有对象,如果对象可达(被其他对象引用),则标记为可达;在清除阶段,再次遍历对象,如果发现某个对象没有被标记为可达,则逐代回收。
在循环引用对象的回收中,整个应用程序将被挂起。为了减少应用暂停的时间,Python通过分代回收(空间换时间)的方法提高了垃圾收集的效率。逐代回收的基本思想是:一个物体存在的时间越长,它就越不可能是垃圾,所以我们应该尽量不回收这样的物体。
4.大量的字符串被拼接转换成一个列表再连接,但是使用数字会生成一个新的字符串(字符串是不可变的)。
5.理解Python中的迭代器和生成器。迭代器:实现迭代器协议的对象。__next__和_ _ iterator _ _这两个神奇的方法,代表了迭代器协议。可以通过for-in循环从迭代器对象中取出值,next函数可以取出下一个值生成器:在调用生成器运行的过程中,每次遇到yield,函数都会暂停并保存当前运行的所有信息,返回yield的值,下次执行next()方法时从当前位置继续运行生成器函数:实现yield的函数,自动实现迭代器协议生成器表达式:列表推导中的括号用括号替换。
迭代器是访问容器的一种方式,也就是容器已经出现,但是这次迭代只打印出现有元素的一个副本。生成器是自动生成元素,只能遍历一次。迭代生成器,属于边缘计算,随着使用而生成,用完就释放。它效率高,不占用太多内存,节省资源。如果一个对象想要迭代,它需要在内部实现迭代器协议:
class Fib(object):def _ _ init _ _(self,num): self.num=num self.a,self.b=0,1 Self . idx=0 def _ _ ITER _ _(Self):return Self def _ _ next _ _(Self):if Self . idx Self . num:Self . a,self.b=self.b,Self。一个自我。b自我。idx=1返回自我。一个raise stop iteration()和迭代器协议已经在生成器内部自动实现,这更加简洁:
Def fib (num): a,b=0,1 for _ inrange (num): a,b=b,a b产生参考:
生成器和迭代器的区别
话题008:说说你对Python中迭代器和生成器的理解
6.多线程和多进程的差异
通常,我们运行的程序包含一个或多个进程,每个进程包含一个或多个线程。
多线程:操作系统分配CPU的基本单位,适用于IO密集型任务,比如请求请求。
优点:多线程可以共享进程内存空间,进程间的通信容易实现。但是由于GIL的限制,多线程无法利用CPU的多核特性。
多进程是操作系统内存分配的基本单位。适用于需要大量计算的CPU密集型任务,如视频编解码、数据处理、科学计算等。
我们可以充分利用CPU的多核特性,但是进程间通信比较麻烦,IPC机制(管道,socket等。)是需要的。
8.列表扩展,越晚添加数据,性能越差。怎么优化呢?
列表会自动扩展,但会自动摊销和相乘。您可以使用字典或集合来代替大型列表。
9.写一个函数来计算列表中每个元素出现的次数。
方法1:
a=[a , b , a , c]
信息={}
因为我在一个:
.info[i]=info.get(i,0) 1
信息
{a: 2, b: 1, c: 1}
方法二:
从集合导入计数器
信息=计数器(a)
信息
计数器({a: 2, b: 1, c: 1})
10.Python为什么运行很慢?
Python是一种动态的强类型语言,一边解释一边运行。比较和转换类型的成本很高。每当你读、写或引用一个变量时,你必须检查它的类型。
用C运行时,先编译,它可以直接生成高效的机械代码。python在执行的时候是源代码,一个从源代码到机械代码的过程变量需要随时切换,所以运行时需要随时检查类型。
GIL限制了多核CPU的并发执行,这是一种同步线程的机制,因此任何时候都只有一个线程在执行。即使在多核处理器上,使用GIL的解释器也只允许单个进程一次只使用一个线程。
1.Python构造方法和析构函数构造方法
构造方法__init__:用于实例化一个类。
析构函数__del__:函数是在对象被调用后将其释放,不再使用。
A类:
def __init__(self):
及格
def __del__(self):
打印(“对象已发布”)
a=A()
德尔阿
Print(a) #调用会报告一个错误,因为A已经被清理了
12.函数参数*arg和**kwargs分别代表什么?
在Python中,函数的参数分为位置参数、变量参数、关键字参数和命名关键字参数。
*args代表可变参数,它可以接收0个或任意数量的参数。当您不确定调用者将传入多少个位置参数时,您可以使用variable parameter,它会将传入的参数打包到一个元组中。
**kwargs代表关键字参数,可以接收参数名=参数值形式传入的参数。传入的参数将被打包成一个字典。
如果在定义函数时同时使用了*args和**kwargs,那么该函数可以接收任意参数。
13.写一个记录函数时间的装饰器。
1.用函数实现装饰器:
导入时间
从functools导入包装
定义记录时间(函数):
@wraps(func)
def包装(*args,**kwargs):
start_time=time.time()
result=func(*args,**kwargs)
Print(f 总时间:{time.time()-start_time} )
回送结果
返回包装
@record_time
def ff():
对于在幅度内的I(22000):
打印(一)
if __name__==__main__ :
法国法郎()
2.用类实现装饰器:
班级记录:
def __call__(self,func,*args,**kwargs):
‘’这个方法的作用类似于在类中重载()运算符,这样就可以像调用普通函数一样,以“object name()”的形式使用类实例对象。
@wraps(func)
def包装(*args,**kwargs):
start_time=time.time()
result=func(*args,**kwargs)
Print(f 总时间:{time.time()-start_time} )
回送结果
返回包装
记录=记录()
# record()将触发__call__
@记录
def ff():
对于在幅度内的I(22000):
打印(一)
14.单一模式
装饰器实现、元类实现、导入
1)、装饰形式:
从functools导入包装
def singleton(cls):
单纯形类装饰器
实例={}
@wraps(cls)
def包装(*args,**kwargs):
如果cls不在实例中:
instances[cls]=cls(*args,**kwargs)
返回实例[cls]
返回包装
@singletonclass
总统:
及格
2)、元类:
类SingletonMeta(类型):
自定义单例元类
def __init__(cls,*args,**kwargs):
cls。__instance=无
超级()。__init__(*args,**kwargs)
def __call__(cls,*args,**kwargs):
如果cls。_ _实例为无:
cls。__instance=super()。__call__(*args,**kwargs)
返回cls。_ _实例
班长(元
15.什么是lambda及其使用场景?
匿名函数不会与其他函数命令冲突。一行代码可以实现一个函数要实现的功能,表达式的执行结果就是函数的返回值。
其主要目的是将一个函数转换成另一个高阶函数(如filter、Python内置的map等。)对功能进行解耦,增强功能的灵活性和通用性。
列表(map(lambda x: x*2,[1,2,3]))
#按值对字典排序
info={a: 1, b: 2}
sorted(info.items(),key=lambda x: x[1],reverse=True) # [(b ,2),( a ,1)]
16.什么是鸭子打字?
鸭式是动态类型语言用来判断一个对象是否是某一类型的方法,也叫鸭式判断法。简单来说,鸭型是指判断一只鸟是不是鸭子。我们只在乎它游得像鸭子,叫得像鸭子,走得像鸭子。换句话说,如果一个物体的行为与我们的预期一致(它能接受某种信息),我们就会假设它是某种类型的物体。
在Python中,有很多类字节对象(如bytes,bytearray,array.array,memoryview),类文件对象(如StringIO,BytesIO,GzipFile,socket),类路径对象(如str,bytes),其中,类文件对象可以支持读写操作,可以像文件一样读写。这就是所谓的判断方法,一个物体如果表现得像鸭子,就可以判断它是鸭子。另一个例子是Python中list的extend方法。它需要的参数不一定是列表,只要是可迭代的对象就没有问题。
说明:动态语言的duck类型大大简化了设计模式的应用。
17.谈谈对闭包的理解
由于各种原因,有时需要在函数外部获取函数内部的局部变量,但由于Python作用域的限制,外部访问会直接报告NameError,这是无法直接实现的,比如:
定义f1():
n=999
打印(n)
但是,如果在函数内部定义了内部函数,它就可以访问函数的局部变量。如果直接返回内部函数,则函数内部的局部变量也可以在外部间接访问:
定义f1():
n=999
定义f2():
打印(n)
返回f2
结果=f1()
结果()
上面的f2()函数是一个闭包。闭包的定义是,当一个函数中可以定义(嵌套)另一个函数时,如果内部函数引用外部函数的变量,就可能产生闭包。
关闭的目的
读取函数内部的变量。
将函数内部的局部变量一直保存在内存中:函数运行后,函数内部的局部变量会被Python的垃圾收集机制从内存中清除。如果想让这个局部变量长时间存放在内存中,可以用闭包来实现这个功能,这样会导致很大的内存开销,所以要避免滥用。
18、收藏
1、默认字典
是内置dict类的子类。它实现了键不存在时返回默认值的功能,只是和内置的dict函数完全一样。
从集合导入默认字典
default_dict=defaultdict(int)
default_dict[x]=22
default_dict[x]
default_dict[y]
#自定义返回默认值
def get_default_info()。
.return {name: 123}
D1=default dict(get _ default _ info)
d1[aa]
{ 姓名: 123}
2、有序直接
有序字典,可以保持元素的插入顺序。
从集合导入订单直接
d=OrderedDict()
d[名字]=罗斯
d[年龄]=18
OrderedDict([(name , rose ),( age ,18)])
D.move_to_end(name) #可以将指定的键移动到末尾,D.move_to_end(name ,last=false)移动到开头。
OrderedDict([(age ,18),( name , rose)])
3、柜台
统计元素出现的次数,其附加的most_common()函数通常用于解决Top k问题。
从集合导入计数器
d=[a , b , a , c]
d_counter=计数器(d)
d _计数器
计数器({a: 2, b: 1, c: 1})
d_counter.most_common(1)
[(a ,2)]
4、德克
双端队列
从集合导入队列
q=deque([1,2,3])
q.append(4)
q.appendleft(5)
德克([5,1,2,3,4])
q .波普()
q.popleft()
五
5、命名双拼
命名元组(Named tuple ),返回一个新的带有命名字段的元组子类,可以用来构建一个只有几个属性而没有方法的类对象;比直接定义类节省了很多空间。其次,它的返回值是一个元组,支持各种元组。
从集合导入命名元组
Point=namedtuple(Point ,[x , y])
点. x
0x000001D0C31050E8处的属性对象
P=点(1,2) #实例化
页(page的缩写)_asdict()
OrderedDict([(x ,1),( y ,2)])
6、链式地图
将多个字典组合成一个字典,向外界提供统一的视图。
从集合导入链图
def演示链():
user1={name: rose , age: 18}
user2={name: lila , age: 19}
用户=链图(用户1,用户2)
print(user . maps)#[{ name : rose , age: 18},{name: lila , age: 19}]
print(user . keys())# keys view(chain map({ name : rose , age: 18},{name: lila , age: 19}))
print(user . values())# values view(chain map({ name : rose , age: 18},{name: lila , age: 19}))
对于user.items()中的k,v:
打印(k,v)
名字叫罗斯
18岁
如果ChainMap()中的多个字典有重复的键,那么在查看的时候可以看到所有的键,但是在遍历的时候只会遍历第一个出现键的地方,而忽略其余的。
19.线程池的工作原理
线程池是一种可以减少线程的创建和销毁所带来的开销的技术,属于以空间换时间的操作。
由于线程的创建和销毁涉及大量的系统底层操作,开销较大;线程池的原理是将创建和释放线程的操作改为预创建。在创建了一定数量的线程后,它们被放入空闲队列。最初,这些线程被阻塞,不会消耗CPU资源,但会占用少量内存空间。
当一个新任务到来时,从队列中取出一个空闲线程,并将其标记为已占用。任务完成后,线程不会结束,而是继续留在池中等待下一个任务。当系统空闲且大部分线程长时间空闲时,线程池可以自动销毁部分线程,回收系统资源。
基于这种预创建技术,线程池将线程创建和销毁的成本分配给每个特定的任务。执行的次数越多,每个任务的成本越小。
20.如何读取大文件,比如8G的文件?
使用产量生成器指定尺寸读数。
def读取文件():
用open(path,encoding=utf-8 )作为f:
虽然正确:
chunk _ data=f . read(chunk _ size=2048)#指定每次读取的大小(字节)。
如果不是chunk_data:
破裂
产出区块数据
21.Python实例方法、类方法、静态方法详解
方法:可以通过对象直接调用。
Classmethod:不能访问实例变量,只能访问类变量,类变量可以通过类名和对象调用。
Staticmethod:与类无关,只是类中的一个函数。不能作为类变量或实例变量,可以通过类名或对象调用。
班级狗:
年龄=3 #类变量
def __init__(self):
Self.name=小白 #实例变量
DEF (self): #实例方法
print({}岁的{}正在运行!。格式(自我年龄,自我姓名))
@classmethod
def eat(cls):
# print(cls.name) # class方法,无法访问实例变量(属性)
打印(小黑{岁)。format (cls.age)) # Class方法只能访问类变量。
@静态方法
定义睡眠(名称):
#静态方法与类无关,只有类中的一个函数。
#静态方法不能访问类变量和实例变量。
打印(({}正在睡觉)。格式(名称))
d=狗()
D.run() #通过实例化对象来调用实例方法
Dog.run(d) #通过类名调用实例方法,实例对象需要传入方法。
D.eat() #通过实例化对象来调用类方法
Dog.eat() #通过类名调用类方法
D.sleep(小兰)#通过实例化对象来调用静态方法
Dog.sleep(晓兰)#通过类名调用静态方法
参考:Python实例方法、类方法、静态方法的详细讲解。
第二,卡夫卡
1.提交人:卡夫卡胶印
自动提交:基于时间提交,很难把握提交时机。
手动提交:
提交:它影响吞吐量,将无法重试。
异步提交:最常用的,没有重试机制可能会失败。
2.信息泄露消费和重复消费的场景
无论是同步还是异步提交抵销,都有可能遗漏或重复消耗。
漏消费:消费前提交。
重复消费:提交前消费。
3.自定义偏移
在kafka 0.9之前,offset存储在zk中,之后默认存储在内置主题中。用户还可以自定义存储模式。
其目的是保证抵销的消耗和提交同时成功或失败。可以利用数据库的事务函数来实现,所以offset可以存储在MySQL中。
4.邮件积压场景
或者实时消耗任务挂机:任务挂机,不被监控,直到自动拉起。
卡夫卡分区数设置不合理(太少),消费者消费能力不足。
kafka消息的密钥不均匀,导致分区间数据不均衡(生产者生产消息时可以指定密钥)。
解决:
任务重启后,直接消耗最新消息,滞后的历史数据被离线程序捕获。
监控和自动上拉任务从最后一次提交偏移开始。如果数据积压量大,就要增加任务的处理能力,比如增加资源,让任务以最快的速度消耗和处理,赶上最新的消耗消息,合理增加分区。
如果使用Spark stream和Kafka direct方法,KafkaRDD也可以重新分区以增加并行性。
添加随机后缀的关键,以平衡它。
Redis:非关系数据库,数据类型:字符串、列表、集合、有序集、hash,存储在内存中,读写速度快,可以作为缓存键值存储结构。
缺点:由于是基于内存查询,限制了可以存储的数据量,限制了Redis在大规模数据场景下的应用。
场景:适用于读写性能极高、数据表结构简单、查询条件简单的应用场景。
MongoDB:非关系数据库,存储在磁盘上,读取的数据会加载到内存中,读取速度快,表结构灵活可变,字段类型可以随时修改。插入数据时,不必考虑表结构的限制。
缺点:妨碍多表查询、复杂事务等高级操作。
场景:适用于那些表结构变化频繁,数据逻辑结构没有那么复杂,不需要多表查询操作,数据量比较大的应用场景。
HBase:非关系型数据库(列存储),横向扩展能力强,适合存储海量数据。使用廉价的PC就可以构建一个用于海量数据处理的大数据集群。
缺点:数据的读取受到限制,只能把同一列族的数据放在一起,所有的查询都必须依赖于键,导致很多复杂的查询很难实现。
场景:hbase是一个重产品,依赖于很多hadoop组件。如果数据规模不大,没必要用hbase,MongoDB完全可以满足需求。因为列存储的容量带来了海量数据的容量,所以非常适合数据量巨大,查询条件简单,列间联系少的场景。
唯一索引:加速查询列值是唯一的(可以为空)。
主键索引:只有一个整表,只有一个列值用于加速查询(不允许null)
复合(联合)索引:多个列构成一个索引。
采用单线程,避免不必要的上下文切换和竞争条件,不存在多进程或多线程导致的切换对CPU的消耗。不需要考虑各种锁,没有锁和解锁操作,也不会因为可能的死锁而消耗性能。
使用不同的底层模型,它们与客户端通信的底层实现方法和应用协议是不同的。Redis直接自建VM机制,因为一般系统调用系统函数,会浪费一定的时间去移动和请求。
四。火花
1.星火任务如何解决第三方依赖?
比如机器学习包需要本地安装?- py-files添加py、zip和egg文件不需要安装在每个节点上。
2.如何解决火花数据的偏斜?
spark中的数据偏斜是指shuffle过程中的数据偏斜,主要是由于key对应的数据不同,导致不同任务处理的数据不同。
例如,reduce点必须处理总共100万条数据。第一个和第二个任务分别分配到10000条数据,计算在5分钟内完成。第三个任务分配给98万条数据。此时,第三个任务可能需要10个小时才能完成,这使得整个Spark作业需要10个小时才能运行和完成。这就是数据倾斜的后果。
数据倾斜的性能:
Spark jobs的大部分任务执行得很快,只有有限的几个任务执行得非常慢。这时候可能会有数据倾斜,作业可以运行,但是运行很慢。
Spark jobs的大部分任务执行速度都很快,但是有些任务在运行过程中会突然报告OOM,有些任务在重复执行几次后会报告OOM错误。此时可能会发生数据倾斜,作业无法正常运行。
定位数据倾斜问题:
检查代码中的shuffle操作符,如reduceByKey、countByKey、groupByKey、join等。并根据代码逻辑判断这里是否会出现数据偏斜。
检查Spark作业的日志文件。日志文件会将错误记录到代码的某一行。根据异常所在代码的位置,可以确定错误发生在哪个阶段,对应的是哪个shuffle运算符。
3.火花驱动器和执行器
在执行Spark的应用时,Spark集群会启动两个JVM进程,Driver和Executor。
驱动程序:负责创建spark上下文,提交spark job作业,将作业转换为计算任务,并协调各执行器进程之间的任务调度。
执行程序:负责在工作节点执行具体的计算任务,将结果返回给驱动程序,并为需要持久化的RDD提供存储功能。
4.火花内部和外部存储器
Spark内存管理包括堆上内存和堆外内存。因为驱动的内存管理比较简单,所以下面说的内存指的是执行程序的内存。
Excutor作为一个JVM进程,它的内存管理是建立在JVM内存管理的基础上,Spark更细致地分配JVM的堆空间,充分利用内存。同时还引入了堆外内存,可以直接在工作节点的系统内存中开辟空间,进一步优化了内存的使用。
堆内内存的大小,由- executor-memory或spark.executor.memory参数在spark应用程序启动时配置。
堆内内存分区
执行程序堆内存可以分为四个主要块:
执行程序内存(Excutor memory):主要用于计算过程中的临时数据的洗牌、连接、排序、聚合等。
存储内存:主要用于存储缓存数据,如rdd缓存和展开数据。
用户内存:主要用于存储rdd转换操作所需的数据,如rdd依赖等信息。
预留内存:系统预留内存来存储内部spark对象,防止OOM,因为spark堆中的内存大小记录不准确,所以需要留出安全区域(在Spark 2.2.1中是写死的,其值等于300MB,不可修改)
# systemMaxMemory取决于JVM堆中当前的内存大小,实际上是由spark.executor.memory或- executor-memory配置的。
可用存储内存=system maxmemory * spark . storage . memory fraction * spark . storage . safety fraction。
可用执行内存=system maxmemory * spark . shuffle . memory fraction * spark . shuffle . safety fraction。
可用内存=系统内存-预留内存,也就是Spark的可用内存。
堆外内存
堆外空间的分配相对简单,只有存储内存和执行内存。
可用的执行内存和存储内存占用的空间直接由参数spark.memory.storageFraction决定,由于堆外内存占用的空间可以精确计算,所以不需要设置保险区域。
spark . memory . off heap . enabled true
spark . memory . offheap . size 10747 . 48868868687
两者的区别
参考:Spark内存管理的堆内/堆外内存原理详解
5.火花血缘关系
父子rdd的构造中存在依赖关系,通过依赖关系可以实现rdd的容错,连续几个rdd的依赖关系变成血缘关系。
每个rdd不会保存数据,但是会保存血缘关系。如果当前rdd的计算有错误,可以根据其保存的血缘关系重新读取数据源进行计算。
参考:RDD血统的火花
6.火花宽度依赖性
狭义依赖
如果依赖关系可以在设计时确定,就不需要考虑父rdd分区中的记录,父rdd中的每个分区最多只有一个子分区。
父rdd的每个分区最多被子rdd的一个分区使用。
子rdd中的分区或者仅依赖于父rdd中的一个分区(例如,映射和过滤操作)。
或者可以在设计时确定子rdd是父rdd的子集(例如,coalesce)。
窄依赖变换可以在任何分区上独立执行,而不需要任何其他分区的信息。
广泛依赖
父rdd的分区依赖于多个子rdd的分区,这称为广泛依赖。
计算宽依赖时,不能随意在一些记录中运行,需要使用特殊的方式(比如根据键获取分区中的所有数据)。
比如排序sort的时候,数据必须分区,相同范围的键必须在同一个分区。
具有广泛依赖性的转换操作包括:sort、reduceByKey、groupByKey、join以及任何调用修复函数的操作。
7.常见的转换和动作操作
转换:
Map(func):返回一个新的rdd,其结果由func函数处理的每个输入元素组成。
MapPartition(func):类似于map,但是在rdd的每个分区中独立运行。假设有N个元素,M个分区,map的函数会被调用N次,而mapPartition会被调用M次,一次性处理所有分区。
FlatMap(func):对集合中的每个元素进行操作,然后将其展平。
Filter(func):返回一个新的rdd,rdd中的每个元素都会被func函数的逻辑过滤掉。
RedubyKey (func,[numtask]):调用一个(K,V) RDD并返回一个(K,V) RDD。使用reduce函数将同一个键的值聚合在一起。第二个参数可以设置reduce任务的数量。
动作:首先、计数、收集、保存文本文件、获取、foraech、countByKey
8.spark可以通过多种方式部署。
Local:它在机器上运行,通常用于手工练习或测试。
Standalone:基于主从的资源调度集群,将spark任务提交给Master运行,这是spark本身的一个调度系统。
纱线:有两种模式:纱线客户端和纱线集群。主要区别在于驱动程序的运行节点。Spark客户端直接连接到Yarn,因此不需要构建额外的Spark集群。
Mesos:在家庭环境中很少使用。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。