面经-字节跳动-web后端开发实习生(一面凉经)
一面:55min
- 自我介绍
- 爬虫项目:
1. http常见的状态码
(1)200 OK:请求已正常处理
(2)204 No Content:请求处理成功,但没有任何资源可以返回给客户端
(3)206 Partial Content:是对资源某一部分的请求
(4)301 Moved Permanently:资源的uri已更新,你也更新下你的书签引用吧,永久性重定向
(5)302 Found:资源的URI已临时定位到其他位置了,姑且算你已经知道了这个情况了,临时性重定向
(6)304 Not Modified:资源已找到,但未符合条件请求
(7)400 Bad Request:服务器端无法理解客户端发送的请求,请求报文中可能存在语法错误
(8)401 Unauthorized:该状态码表示发送的请求需要有通过HTTP认证(BASIC认证,DIGEST认证)的认证信息
(9)403 Forbidden:不允许访问那个资源,该状态码表明对请求资源的访问被服务器拒绝了
(10)404 Not Found:服务器上没有请求的资源,路径错误等
(11)500 Internal Server Error:貌似内部资源出故障了
(12)503 Service Unavailable:抱歉,我现在正在忙着
2. requests库:
(1)GET:请求指定的页面信息,并返回实体主体
(2)HEAD: 只请求页面的首部
(3)POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体
(4)PUT: 从客户端向服务器传送的数据取代指定的文档的内容
(5)DELETE: 请求服务器删除指定的页面
(6)模拟浏览器访问:有些网站访问时必须带有浏览器等信息,如果不传入headers就会报错
(7)证书验证:urllib3.disable_warnings()这条命令主要用于消除警告信息,将verify设置位False即可
(8)代理设置:在进行爬虫爬取时,有时候爬虫会被服务器给屏蔽掉,这时采用的方法主要有降低访问时间,通过代理ip访问
(9)超时设置:访问有些网站时可能会超时,这时设置好timeout就可以解决这个问题
(10)认证设置:如果碰到需要认证的网站可以通过requests.auth模块实现
- Python
1. 解释装饰器、生成器、迭代器的概念
2. Python并发:Python的多线程因为GIL全局锁的存在,利用不了多核,但它在IO密集型场合下依旧是并发的,效率高于单线程。但是如果是CPU密集型,多线程效率会大幅度下降,需要采用多进程替换。
- 数据库
1. 数据库索引
(1)B+树是数据库实现索引的首选数据结构呢?——评价一个数据结构作为索引的指标就是在查找时IO操作的次数
(2)B+树非叶子节点只存储key,只有叶子结点存储value,叶子结点包含了这棵树的所有键值,每个叶子结点有一个指向相邻叶子结点的指针,这样可以降低B树的高度。
(3)B+树相比于B树的优势:(1)B+树单一节点存储更多的元素,使得查询的IO次数更少(2)B+树所有查询都要查找到叶子节点,查询效率更加稳定(3)B+树所有叶子节点形成有序链表,便于范围查询,更有利于对数据库的扫描
(4)索引字段要尽量的小:IO次数取决于b+数的高度h,h=log(m+1)N,数据量N一定的情况下,每个磁盘块数据项的数量m越大h越小,m=磁盘块的大小/数据项的大小,前者固定,后者越小越好
(5)索引的最左匹配特性(即从左往右匹配)
2. mysql索引:
(1)普通索引index,加速查找
(2)唯一索引:主键索引primary key和唯一索引unique
(3)联合索引:前三者对(id,name)复合数据项产生索引时
(4)其他:全文索引fulltext和空间索引spatial
3. 乐观锁和悲观锁
(1)乐观锁,简单地说,就是从应用系统层面上做并发控制,去加锁。
(2)实现乐观锁常见的方式:版本号version。实现方式,在数据表中增加版本号字段,每次对一条数据做更新之前,先查出该条数据的版本号,每次更新数据都会对版本号进行更新。在更新时,把之前查出的版本号跟库中数据的版本号进行比对,如果相同,则说明该条数据没有被修改过,执行更新。如果比对的结果是不一致的,则说明该条数据已经被其他人修改过了,则不更新,客户端进行相应的操作提醒。
(3)悲观锁,简单地说,就是从数据库层面上做并发控制,去加锁。
(4)悲观锁的实现方式有两种:共享锁(读锁)和排它锁(写锁)
(5)通过对比,发现for update的加锁方式无非是比lock in share mode的方式多阻塞了select...lock in share mode的查询方式,并不会阻塞快照读
(6)mysql InnoDB引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型。在Java中,synchronized的思想也是悲观锁。
(7)像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。
- 数据结构
1. 如何避免Hash碰撞
(1)开放地址法:开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入
(2)再哈希法:当哈希地址发生冲突用其他的函数计算另一个哈希函数地址,直到冲突不在产生为止
(3)链地址法:将哈希表的每个单元作为链表的头结点,所有哈希地址为 i 的元素构成一个同义词链表。即发生冲突时就把该关键字链在以该单元为头节点的链表的尾部
(4)建立公共溢出区:将哈希表分为基本表和溢出表两部分,发生冲突的元素都放在溢出表中