阿龙的学习笔记---2021上半年的记录

之前上半年1~4月集中看过一段时间的面经和资料,把不会的问题总结了,记在笔记本上,整理复习一下。


  • 缓存击穿+布隆过滤器
    • 缓存击穿:
      • Redis缓存,后台数据库。假如用户发来一个错误的key的条件的查询,则Redis中没有,则会去查询数据库,数据库返回空。但是如果一直有这样的请求,那么缓存本来是为了减轻数据库的负担,这样就没用了,每个这样的错误的查询都会去DB。
      • 那么一种解决方案是对于查询的错误的key,我将其存在Redis中,但是value为空或者设定&&之类的,那么重复查询这个key的时候,Redis中是这样特殊的结果,则返回空。(这个Key一定要设置过期时间)
      • 但是假如的无效的key每次都不一样的话,Redis中没有,那么还是会造成缓存击穿。那么就可以用布隆过滤器。
    • 布隆过滤器原理
      • 布隆过滤器我感觉类似是一种哈希表的方式,是以位图的方式表示一个元素key的存在与否,不过好像没有地方去存value吧。
      • 经过一次哈希函数之后,不同的值有可能产生相同的结果,则会产生哈希冲突(碰撞),那么布隆过滤器则在一定程度解决这个问题。
      • 如下图所示:假如我使用三个哈希函数f1,f2,f3,存储值的时候,我对这个值的三个哈希函数的位置都置位(置为1)。存储元素a则在红线的三个位置置位,b是绿色线,c是蓝色。
      • 那么在判断这个元素是否存在的时候,我们则对这个元素再经过三次哈希函数,然后看计算出的位置是否都置位了。如果有某一个位置没有置位,那么这个元素一定不存在。
      • 但是假如都置位了,也不代表这个元素一定存在,比如d元素的位置都置位了,这是误判,也叫假阳性 (False Positive Probability,FPP)。所以布隆过滤器适合在判断海量数据不存在的情况下。
        在这里插入图片描述
    • 缓存击穿中的应用:
      • 布隆过滤器可以有效的判断某个key是不是不存在,所以假如我们把数据库里所有的值都放进去,然后有请求时,首先经过布隆过滤器,如果这个值不存在则一定不存在,则返回。如果都置位了,则有可能存在,再去查询Redis缓存,再查数据库。
      • 布隆过滤器的设计:由于是位图,他是一个不可逆的过程。
      • 如果需要删除,位图不能直接置0,因为不知道其他元素是否会也占用这个1。那么可以让每个bit变成一个计数的数字,删除则减1,不过这样占用空间就会大很多了。
      • 如果扩容,无法将数据直接转移到更大的位图中去,那么在class内部新建一个过滤器,原先的保留,添加操作往新的里面添加,判断时,内部经过两个过滤器再输出结果。
    • 其他应用:
      • 去重:去除海量数据的重复,重复的url之类的,判断用户是否看过某个文章,不重复推荐看过的文章。
      • 缓存击穿

  • 协程Coroutine:是一种基于线程之上,但又比线程更加轻量级的存在

    • 线程的切换由操作系统负责调度,协程由用户自己进行调度,因此减少了上下文切换,提高了效率。
    • 线程的默认Stack大小是1M,而协程更轻量,接近1K。因此可以在相同的内存中开启更多的协程。
    • 适用于被阻塞的,且需要大量并发的场景,即IO密集。但不适用于大量计算的多线程,遇到此种情况,更好实用线程去解决。
    • 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了。
    • 协程是非抢占式的,运行完毕或主动阻塞,而线程是有调度算法。

  • Linux中的mmap()

    • 在<<深入理解计算机系统>>这本书中,mmap定义为:Linux通过将一个虚拟内存区域与一个磁盘上的对象(object)关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping)
    • 在mmap之后,只是在虚拟内存中分配了空间,并没有加载文件内容到物理内存,在你读的时候,这个虚拟内存页没有与物理页映射,那么产生一个“缺页”中断,然后操作系统再去加载到物理页并映射。
    • 在写操作时,对于mmap和直接write,mmap在小文件的写入有点优势,在每次写入大小达到一个页的大小时,几乎没差别。
    • 而在读操作时,mmap的优势明显,根据网上的测试,能在两个数量级的差距。因为mmap读取直接读取mmap的虚拟内容空间,而read则是要从内核空间拷贝到用户空间的buffer,多了一次拷贝
    • 可以作为进程间共享内存通信的方式。

  • 写时复制Copy on write(CoW):
    • 是一种优化思想,写时复制。大抵意思是说:在复制的时候,先做一个映射,让二者共享这个要被复制的东西,但不真正复制。而当要被二者其中的某一个改变(write)的时候,这时两个就不一样了,这时候再做真正的复制操作。

      • 比如在linux中。linux中物理内存和虚拟内存之间是有一层映射关系的。在进程复制fork()的时候,子进程的内存空间跟父进程的一样。所以复制的子进程的内存空间并没有开辟一片新的物理空间,而只是做了一个映射,实际的物理空间是跟父进程共享的。但当父子其中需要修改数据的时候,才将其拷贝出来。
      • JAVA中也用到了这个思想来解决并发读写的效率问题。在读的时候不加锁,而在写的时候,先复制出来一个,然后再使用原子操作的替换。
      • docker的设计中也有这个思想,在容器运行的时候,读取的内容是先从容器中读取,如果没有则会去镜像中寻找。如果要修改内容,则会从镜像中复制出来,然后添加到容器中,这样没有改动的部分,各个容器之间是共享镜像中的。

  • TCP连接的*IME_WAIT

    • 四次握手,主动的那一边会最后发送一次ack,在此之后需要等待2MSL。(MSL:maximum Segment Lifetime 最大数据段生存时间。)
    • 原因
      • 优雅关闭连接:尽量防止这一次的ack丢包,在2MSL之中,如果丢包,另一端会要求超时重传。
      • 让网络中的老旧数据包消失:这个ack最坏情况是1个MSL到达,这时另一端还会重复发送FIN,再等一个MSL可以保证这些FIN也在网络中消失殆尽,保证新建立的连接不会收到之前连接的包而受影响。
    • 服务端发生情况:
      • 在短连接情况下,服务端处理完成主动断开,则容易进入大量time_wait状态。
    • 系统中太多TIME_WAIT怎么办:
      • 根据上面的情况,短连接会有较多time_wait,长连接可以避免。
      • 打开 timewait 重用:直接使用time_wait状态的四元组,但要满足一定条件,初始序列号比之前的fin序列号大 或者 时间戳比之前的晚(为了保证之前的包不影响)。
        • 打开 timewait 快速回收:linux系统在很短的时间(一般一个重传时间)就回收而不是等待2MSL,但是会保留之前的一些状态,新的连接要保证一些条件才能连接。(感觉这两个有点像,主要原理都是靠一些条件保证之前的网络中的数据包不会影响新的连接)

  • malloc申请的是 虚拟内存 还是 物理内存
    • 答案是虚拟内存
    • 内核为进程维护页表,存储着每一页的虚拟地址,一些在物理内存中,一些不在。访问虚拟内存,缺页会触发中断然后加载到物理内存中。malloc申请的是进程空间内的内存,是虚拟内存,在heap区域分配一块内存。
    • 如果对这块内存进行memset,这时需要访问这块内存,访问时如果这个页在物理内存中则可以直接访问写入,如果不在则会触发缺页中断,内核将这块虚拟内存加载进物理内存。

  • 如何让UDP实现可靠传输(如何用UDP实现TCP)
    • UDP户资源消耗小、处理速度快,对传输时延、传输效率、传输成本有要求的选择UDP协议。
    • 在UDP协议上要实现可靠传输,则需要在应用层来保证可靠传输,需要实现类似TCP的一些功能来实现可靠传输。简单来讲就是 超时重传、有序接收、应答确认、滑动窗口流量控制、拥塞控制等。
    • 开源的方案有RUDP、UDT、RTP等。传输质量、传输成本、传输延迟三者之间相互制约。UDP实现的可靠传输会比复杂的TCP更适合一些有低延迟、实时性要求的场景。

  • undefined reference 怎么排错
    • 未定义的引用,在链接时出现的错误。
    • 自定义的函数没有声明。
    • 静态链接库没有找到,查找系统ld路径,添加路径或者安装库文件。
    • gcc调用时没有加 -l 选项指定静态库。
    • #ifdef等预编译处理的地方,没有编译到。

  • TCP 粘包和分包
    • 由于TCP是面向字节流的,没有边界限制,过长的包大于MTU会分包;或者由于Nagle算法(为了避免糊涂窗口)等可能会出现几个包一起发送而产生粘包。
    • 通过在应用层来解决这个问题。即定义特定的传输格式。
      • 固定长度包,定长协议。
      • 特殊字符分割,比如使用 ‘\r\n’ 来分割每个包。注意不能在正文中出现,否则会分错。
      • 变长协议,通过告诉对方包的长度。可以使用固定的帧头+长度+正文等方式。
    • 实际中会有一些,比如Java中的netty, netty封装了多种拆包粘包的方式。

  • 输入一个网址会发生什么:被问烂了,需要复习一下
    在这里插入图片描述
    1. DNS解析:输入一个网址并按回车的时候浏览器会根据输入的URL去查找对应的IP。查找浏览器缓存、操作系统缓存、路由器缓存上、DNS服务器、根域名服务器(顶级DNS服务器)
    2. 进行TCP连接:浏览器得到了IP,向服务器发送TCP连接,TCP连接经过三次握手。
    3. 浏览器发送HTTP请求:get请求包含了主机(Host)、Connection中的 keep-alive 表示浏览器告诉对方服务器在传输完现在请求的内容后不要断开连接,不断开的话下次继续连接速度就很快了。可能还会有 Cookies,保存了用户的登陆信息。
    4. 服务器处理请求:会解析这个请求(读请求头),然后生成一个响应头和具体响应内容。接着传回来一个响应头和一个响应,响应头告诉了浏览器一些必要的信息,例如重要的Status Code,2开头如200表示一切正常,3开头表示重定向,4开头是客户端错误,如404表示请求的资源不存在,5开头表示服务器端错误。响应就是具体的要请求的页面内容
    5. 浏览器解析渲染页面:当服务器返回响应之后,浏览器读取关于这个响应的HTML文件,然后开始解析这个响应并在页面上显示出来。嵌入在HTML中的对象 ( 图片,视频,css样式,JavaScript文件 ) 需要多次请求获取。
    6. 关闭TCP连接:需要经过4次握手。

  • 多核CPU如何保证不同CPU读取同一个变量是最新的
    • CPU如果每次都从内存中读,向内存中写,那么是一致的。
    • 但是CPU会有缓存,所以有维持CPU缓存一致性的方法,每个C
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值