Golang基础面试题汇总_第1页
Golang基础面试题汇总_第2页
Golang基础面试题汇总_第3页
Golang基础面试题汇总_第4页
Golang基础面试题汇总_第5页
已阅读5页,还剩2页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Golang基础⾯试题汇总数据库分布式锁原理raft算法。Etcd锁实现原理1.在etcd系统⾥创建⼀个key。2.如果创建失败,key存在,则监听key的变化事件,直到该key被删除,回到1。3.如果创建成功,则认为获得了锁。Etcd⼯作原理httpserver接受请求并转发给store进⾏处理,如果涉及节点修改,则交给raft进⾏状态变更、⽇志记录,然后同步给其他节点以确认提交,最后提交数据,并再次同步。其中etcd使⽤wal来进⾏持久化存储。Redis持久化存储⽅式rdb:(默认)定时快照存储。aof:即时存储。Redis队列类型:list。新增:lpush。查询:blpop。和Redis⽐较memcached:单⼀,只能存缓数据不能持久化,适⽤多读少写。redis:数据结构丰富,两个持久化⽅案,并可以数据恢复。Leveldb基于本地⽂件存储,数据存量是物理内存的3-5倍,将⼀部分分热区数据.log保存在内存,持久化数据.sst保存在磁盘上。Mysql索引采⽤b+tree,b+tree采⽤⼆分查找法。SQL优化和索引优化sql优化:避免索引失效,全表扫描。适当使⽤in和exists(in先执⾏⼦查询,exists先执⾏外层,所以in适合外表⼤⽽内表⼩的情况,exitst适合外表⼩⽽内表⼤的情况)。避免返回不必要的列(*)和数据(⽤limit)。尽量避免⼦查询,使⽤join⽤⼩表驱动⼤表,减少NestedLoop的循环提升性能。尽量⽤innerjoin,少⽤leftjoin,innerjoin会⾃动选择⼩表去驱动⼤表,leftjoin⼀般使⽤场景是⼤表驱动⼩表。join连接最好不超过2个。索引优化:避免索引失效:索引列上做操作,计算、函数、类型转换。!=或<>,会导致后⾯索引失效。null。like通配符前缀%,会导致后⾯索引失效。字符串不加单引号。MysqlInnoDB和MyIsam存储引擎区别innoDb:⽀持外键。聚集索引,数据⽂件和索引绑在⼀起,必须要有主键,通过主键索引效率很⾼。不保存表的具体⾏数,不保存表的具体⾏数,执⾏selectcount(8)fromtable时需要全表扫描。不⽀持全⽂索引。⽀持事务。myIsam:不⽀持外键。⾮聚集索引,数据⽂件和索引是分离的,索引保存数据⽂件的指针。保存表的具体⾏数,⽤⼀个变量保存了整个表的⾏数,执⾏selectcount(*)fromtable语句时只需要读出该变量即可,速度很快。⽀持全⽂索引,查询效率⾼。不⽀持事务。GoGo语⾔优势底层原⽣的多核并发技术。Go为什么需要指针因为函数参数都是值传递,如果对实参进⾏修改,值类型只是复制了⼀份值,对参数没有影响,指针就可以,⽽且指针指向的是⼀个地址,内存占⽤⼩,其实go⾥⾯不存在引⽤类型,⽐如map,slice,interface底层都是指针类型。内存泄漏场景由于逻辑问题导致资源⼀直⽆法被释放,增加阻塞。1.Goroutine12funcmainI(){ch:=make(chanint)3gofunc(){4ch<-15}()6//<-ch7time.Sleep(time.Second)}8解释:对于⾮缓冲管道,必须有接收者才能将数据放⼊管道,否则就会阻塞,从⽽导致内存泄漏。2.Http1http请求resp.Body,需要deferresp.Body.Close()除了加Mutex锁以外还有哪些⽅式安全读写共享变量可以通过Channel进⾏安全读写共享变量。Channel有⽆缓冲的区别⽆缓冲:发送和接收需要同步。有缓冲:不要求发送和接收同步,缓冲满时发送阻塞。CSP并发模型以通信的⽅式来共享内存,是通过Goroutine和Channel来实现的。Goroutinegoroutine是golang实际并发的实体,底层使⽤coroutine(协程)实现并发,coroutine是⼀种运⾏在⽤户态的⽤户线程,底层使⽤coroutine,是因为:⽤户空间,避免了内核态和⽤户态的切换导致的成本可以由语⾔和框架层进⾏调度。更⼩的栈空间允许创建⼤量的实例。特性:GMPG:GoroutineM:线程P:CPU运⾏:CPU开启⼀个线程执⾏Goroutine。单核情况,所有goroutine运⾏在同⼀个线程中,每个线程维护⼀个上下⽂,⼀个上下⽂只有⼀个goroutine,其他goroutine在runqueue中等待。多核情况,碰到goroutine阻塞,会再开启⼀个线程执⾏goroutine,以充分利⽤cpu资源。停⽌Goroutine1.runtime.Goexit()。2.通过channel传递退出信号(<-ch)(适⽤于单个goroutine)。3.使⽤waitgroup(Add、Wait、Done)。4.context⼿动cannel或超时控制()。常⽤的并发模型channle:⽆缓冲,同步。sync.WaitGroup:为了防⽌main函数结束结束goroutine,waitgroup会等待所有goroutine完成。contex:上下⽂。包括程序的运⾏环境、现场和快照,主要处理多个goroutine之间共享数据及多个goroutine的管理。Channel为什么是线程安全的原⼦性,底层实现互斥锁来保证线程安全,出队⼊队都加了锁。nilslice和空slicenilslice:未初始化的slice,不能赋值。空slice:初始化的slice。进程、线程、协程进程:系统进⾏资源分配和调度的基本单位,拥有独⽴的内存空间,不同进程通过进程间通信来通信,上下⽂切换开销(栈、寄存器、虚拟内存、⽂件句柄)⼤,相对⽐较安全。线程:轻量级进程,不拥有系统资源,共享进程全部资源,线程间通信主要通过共享内存,上下⽂切换开销⼩,相⽐进程容易丢失数据。协程:⼀种⽤户态的轻量级线程,由go运⾏时(runtime)管理,协程的调度完全由⽤户控制。协程拥有⾃⼰的寄存器上下⽂和栈。从单进程到多进程提⾼了CPU利⽤率;从进程到线程,降低了上下⽂切换的开销;从线程到协程,进⼀步降低了上下⽂切换的开销,使得⾼并发的服务可以使⽤简单的代码写出来。(进程拥有⾃⼰独⽴的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。线程拥有⾃⼰独⽴的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。协程和线程⼀样共享堆,不共享栈,协程由程序员在协程的代码显⽰调度)协程如何调度的协程调度切换时,将寄存器上下⽂和栈保存到其他地⽅,在切回来的时候,恢复先前保存的寄存器上下⽂和栈。进程和线程的操作是由程序触发系统接⼝,最后执⾏者是系统,协程的操作执⾏者则是⽤户⾃⾝程序。Buffer和Cache区别Buffer:⽤于存放将要输出到disk(块设备)的数据,进⾏流量整形,把突发的⼤数量较⼩规模的IO整理成平稳的⼩数量较⼤规模的IO,以减少响应次数。Cache:⽤于存放从disk上读出的数据,为了弥补⾼速设备和低速设备的鸿沟⽽引⼊的中间层,最终起到加速访问速度的作⽤。两者都是为提⾼IO性能⽽设计的。垃圾回收GC垃圾回收:指内存中不再使⽤的内存区域,⾃动发现和释放这种内存区域的过程就是垃圾回收。三⾊标记原理:初始所有对象⽩⾊。从根(root)出发(全局指针和goroutine栈上的指针)扫描所有可达对象,标记为灰⾊,放⼊待处理队列。从队列取出灰⾊对象,将其引⽤对象标记为灰⾊放⼊队列,⾃⾝标记为⿊⾊。重复3,直到灰⾊对象队列为空。此时⽩⾊对象即为垃圾,进⾏回收。原理:WB(writebarrier:写屏障,go1.7Dijkstra写屏障,只监控堆上指针数据的变动(增量式垃圾回收时,新增的指针对象会被初始为⽩⾊,需要标记为⿊⾊)re-scan。go1.8混合写屏障,只监控堆上指针数据的变动,⽆需re-scan)-STW(stoptheworld:暂停所有正在执⾏的⽤户线程/协程)-mark(三⾊标记,标记协程是并⾏的)-WB(关闭写屏障)-STW(挂起stw协程)-sweep(清除,唤醒清扫垃圾协程,该协程在应⽤启动后创建,创建后⽴即进⼊睡眠,被唤醒后把⽩⾊对象挨个清理掉,清扫协程和应⽤协程是并发进⾏的,清扫完成后再次进⼊睡眠状态)回收⽅式:⾮增量式:垃圾回收需要STW,在STW期间完成所有垃圾对象的标记,STW结束后慢慢的执⾏垃圾对象的处理。增量式:垃圾回收需要STW,在STW期间完成部分垃圾对象的标记,然后结束STW继续执⾏⽤户线程,⼀段时间后再次执⾏STW再标记部分垃圾对象,这个过程会多次重复执⾏,直到所有垃圾对象标记完成。GC触发条件:辅助GC:在分配内存时,会判断当前的Heap内存分配量是否达到了触发⼀轮GC的阈值(每轮GC完成后,该阈值会被动态设置),如果超过阈值,则启动⼀轮GC。调⽤runtime.GC()强制启动⼀轮GC。sysmon是运⾏时的守护进程,当超过forcegcperiod(2分钟)没有运⾏GC会启动⼀轮GC。常⽤的分布式锁数据库、缓存(redis、memcached、tair)、zookeeper。切⽚数据结构指向底层数组的指针、长度、容量。切⽚和数组区别数组:固定长度。切⽚:底层是指向数组的指针,动态。Slice扩容策略:⾸先判断,如果新申请容量⼤于2倍的旧容量,最终容量就是新申请的容量。否则判断,如果旧切⽚的长度⼩于1024,则最终容量就是旧容量的两倍。否则判断,如果旧切⽚长度⼤于等于1024,则最终容量从旧容量开始循环增加原来的1/4,直到最终容量⼤于等于新申请的容量。如果最终容量计算值溢出,则最终容量就是新申请容量。扩容前后的Slice是否相同?情况⼀:原数组还有容量可以扩容(实际容量没有填充完),这种情况下,扩容以后的数组还是指向原来的数组,对⼀个切⽚的操作可能影响多个指针指向相同地址的Slice。情况⼆:原来数组的容量已经达到了最⼤值,再想扩容,Go默认会先开⼀⽚内存区域,把原来的值拷贝过来,然后再执⾏append()操作。这种情况丝毫不影响原数组。要复制⼀个Slice,最好使⽤Copy函数。new和make区别new:new对象,分配零值内存,返回指针。make:makeslice、map、channel,分配⾮零值内存(并初始化成员结构),返回对象。map底层实现底层实现是⼀个散列表,主要有两结构体,hmap(aheaderforagomap)和bmap(abucketforagomap,bucket),bucket中存储key和value。哈希值按照⽤途⼀分为⼆:低位和⾼位,低位⽤于寻找当前key属于hmap中哪个bucket,⾼位⽤于寻找bmap中的key对应的value。map扩容当Go的map长度增长到⼤于加载因⼦所需的map长度时,Go语⾔就会将产⽣⼀个新的bucket数组,然后把旧的bucket数组移到⼀个属性字段oldbucket中。注意:不并是⽴刻把旧的数组中的元素转到新的bucket当中,⽽是,只有当访问到具体的某个bucket的时候,会把bucket中的数据转移到新的bucket中。defergo语⾔会将defer后⾯的语句进⾏延迟处理。编译时将defer后的语句追加到goroutine_defer链表最前⾯,运⾏goroutine_defer时从前到后依次执⾏。return后执⾏defer。⽤途:关闭⽂件句柄。锁资源释放。数据库连接释放。经典例⼦:12functest(){i:=03deferfmt.Println("defer:",i)4i++5returni6}7输出:defer:018functest(){9i:=0101112131415161718192021222324252627282930313233deferfunc(){fmt.Println("defer:",i)}()i++returni}输出:defer:11funcreturnValues()int{varresultintdeferfunc(){result++fmt.Println("defer:",result)}()returnresult}输出:defer:10funcreReturnValues()(resultint){deferfunc(){result++fmt.Println("defer:",result)}()returnresult}输出:defer:11sync.Once和initinit:⽂件包⾸次被加载的时候执⾏,且只执⾏⼀次。sync.Once:需要的时候执⾏,且只执⾏⼀次。反射反射机制是指在程序运⾏时动态地捕获甚⾄改变变量的类型信息和值,但是在编译时并不知道这些变量的具体类型。使⽤场景:1.有时你需要编写⼀个函数,但是并不知道传给你的参数类型是什么,可能是没约定好;也可能是传⼊的类型很多,这些类型并不能统⼀表⽰。这时反射就会⽤的上了。2.有时候需要根据某些条件决定调⽤哪个函数,⽐如根据⽤户的输⼊来决定。这时就需要对函数和函数的参数进⾏反射,在运⾏期间动态地执⾏函数。IDE代码⾃动补全、json序列化、fmt函数、DeepEqual等缺点:1.与反射相关的代码,经常是难以阅读的。在软件⼯程中,代码可读性也是⼀个⾮常重要的指标。2.Go语⾔作为⼀门静态语⾔,编码过程中,编译器能提前发现⼀些类型错误,但是对于反射代码是⽆能为⼒的。所以包含反射相关的代码,很可能会运⾏很久,才会出错,这时候经常是直接panic,可能会造成严重的后果。3.反射对性能影响还是⽐较⼤的,⽐正常代码运⾏速度慢⼀到两个数量级。所以,对于⼀个项⽬中处于运⾏效率关键位置的代码,尽量避免使⽤反射特性。interface:存储实体的类型信息。reflect:TypeOf()函数返回⼀个接⼝,这个接⼝定义了⼀系列⽅法,利⽤这些⽅法可以获取关于类型的所有信息;ValueOf()函数返回⼀个结构体变量,包含类型信息以及实际值。捕获异常123456789funcf(){deferfunc(){ifr:=recover();r!=nil{fmt.Println("recoveredinf:",r)}}()//异常g(0)}注:deferrecover机制只针对当前函数以及直接调⽤的函数可能产⽣的panic,⽆法处理调⽤产⽣的其他协程的panic,这⼀点和trycatch机制不⼀样。逃逸分析编译时分析变量是否被外部引⽤,被外部引⽤分配在堆上,否则分配在栈上。为什么要有堆栈:堆上动态内存分配⽐栈上静态内存分配,开销⼤很多。堆栈回收:堆上⼀般⼿动回收,⼀般语⾔有⾃动垃圾回收机制,栈上由操作系统⾃动(分配)回

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论