2022 面试记录
# 捷风数据
- (问答题)以任何语言打印一个矩阵,输入是矩阵的行数 rowNum 和矩阵的列数 colNum,这里保证输入的必然是自然数,且小于 Integer.MAX 要求在命令行打印一个如下的蛇形矩阵:
>>> 3*5
# 输出为:
1,6,7,12,13
2,5,8,11,14
3,4,9,10,15
>>> 2*2
# 输出为:
1, 4
2, 3
2
3
4
5
6
7
8
9
10
要求实现的人提供一个 JAR 包或者一个 python 文件, 在命令行使用
python test.py 34
java -jar xxxjar 3 4.
2
3
来打印一个 3*4 的矩阵
# 深光科技
- 索引原理,回表查询,b+树和 b 树区别?为什么不使用红黑树/二叉树,聚簇索引
- redis 使用,集群选举制度
- WSGI 实现了什么?
web 服务器在将请求转交给 web 应用程序之前,需要先将 http 报文转换为 WSGI 规定的格式。
WSGI 规定,Web 程序必须有一个可调用对象,且该可调用对象接收两个参数,返回一个可迭代对象:
environ
:字典,包含请求的所有信息
start_response
:在可调用对象中调用的函数,用来发起响应,参数包括状态码,headers 等
web.py、flask、django 技术选型为什么倾向于选用 flask,(面试官倾向于选择 Django)
事务
flask 运行机制
基类,在哪看到过?
脑裂是什么?
dict 的实现
celery 实际推荐使用 rabbitMQ,为什么?
redis 不是一个真正的消息队列,我们只是把 list 当作消息队列来使用,而 rabbitMQ 实现了消息队列中的更多功能。
# 二面
nginx 配置,字段含义
$host
,还有哪些关键字?iteritems,items 区别?
# python2 >>> a = {'a':1,'b':2} >>> b = a.iteritems() >>> type(b) <type 'dictionary-itemiterator'> >>> next(b) ('a', 1)
1
2
3
4
5
6
7在 python2 中
iteritems
返回的是迭代器。items
返回的是列表In [1]: a = {'a':1,'b':2} In [2]: b = a.items() In [3]: type(b) Out[3]: dict_items In [4]: next(b) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-4-adb3e17b0219> in <module> ----> 1 next(b) TypeError: 'dict_items' object is not an iterator In [6]: c = iter(b) In [7]: next(c) Out[7]: ('a', 1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18在 python3 中
items
返回的是可迭代对象。多线程多进程编程?进程通信方式,queue 无法跳出,遇到过吗,如何解决的?
数据库索引优化举例?
Python 底层加速
内存不足杀死进程如何调试?
埋点,打印 log、pdb 调试索引无效的情况都有哪些?
线上 linux 查看问题点的命令? netstat、top、lsof -i 、ps
各个 web 框架的区别或者优缺点?
# 集简慧通
- left join 与 right join
left join:顾名思义,就是“左连接”,表 1 左连接表 2,以左为主,表示以表 1 为主,关联上表 2 的数据,查出来的结果显示左边的所有数据,然后右边显示的是和左边有交集部分的数据。
right join:“右连接”,表 1 右连接表 2,以右为主,表示以表 2 为主,关联查询表 1 的数据,查出表 2 所有数据以及表 1 和表 2 有交集的数据。
join:其实就是“inner join”,为了简写才写成 join,两个是表示一个的,内连接,表示以两个表的交集为主,查出来是两个表有交集的部分,其余没有关联就不额外显示出来,这个用的情况也是挺多的。
- Django 的生命周期
- 静态方法和类方法的区别
- MySQL B+树索引和哈希索引的区别
B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过 1,而且同层级的节点间有指针相互链接。
在 B+树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高。
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似 B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
从示意图中也能看到,如果是范围查询检索,哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
同理,哈希索引也没办法利用索引完成排序,以及 like "xxx%"
这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
哈希索引也不支持多列联合索引的最左匹配规则;
B+树索引的关键字检索效率比较平均,不像 B 树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。
- 主键索引和唯一索引的区别
主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。 主键创建后一定包含一个唯一性索引,唯一性索引并不一定就是主键。 唯一性索引列允许空值,而主键列不允许为空值。 主键可以被其他表引用为外键,而唯一索引不能。
一个表最多只能创建一个主键,但可以创建多个唯一索引。
主键索引和普通索引是数据库中常见的两种索引类型,它们的区别主要体现在以下几个方面:
唯一性:主键索引要求索引列的值是唯一的,即每个索引值只能对应一条记录,而普通索引则允许索引列的值重复。
空值:主键索引不允许存在空值,即索引列的值不能为空,而普通索引可以包含空值。
表现形式:主键索引可以是聚集索引(clustered index)或非聚集索引(non-clustered index),而普通索引只能是非聚集索引。
存储方式:主键索引的存储方式与数据行的存储方式相同,即主键索引和数据行存储在同一位置,而普通索引则是独立存储的。在 MySQL 中,主键索引的叶子节点存储的是整个数据行的值,而普通索引的叶子节点存储的是索引字段的值和对应的主键值。
查询性能:主键索引的查询性能通常比普通索引更高,因为主键索引的唯一性和聚集性可以减少磁盘 I/O 的次数,提高查询效率。
总的来说,主键索引是一种特殊的索引类型,用于唯一标识一条记录,具有唯一性和非空性的特点;而普通索引是一种常见的索引类型,用于提高查询性能,可以包含重复值和空值。
__new__
和__init__
区别- Python 传值还是传引用
- Python 是否支持重载
不支持,但是也不影响。因为 Python 可以处理这种问题。 python-中重载
- 闭包
- 迭代器与生成器
- 下划线和双下划线的区别
Python 中没有真正的私有属性,但是可以用这种方式来表示私有;但是单下划线可以强制导入,而双下划线只能通过_ClassName__var
来获取。
# 龙创悦动
token 的工作机制
索引、事务/innoDB 和 MyISAM 的对比
事务:一个最小的不可再分的工作单元
web 框架的对比
# 大地量子
range 返回的对象类型
Python3
range()
函数返回的是一个可迭代对象(类型是对象),而不是列表类型, 所以打印的时候不会打印列表。Python3
list()
函数是对象迭代器,可以把range()
返回的可迭代对象转为一个列表,返回的变量类型为列表。python2 中
xrange
返回惰性可迭代对象,可以遍历而不消耗,类型为range
对象,而不是迭代器,无法使用next
取值!有len
方法python 版本问题
一行打印乘法口诀表
print('\n'.join(['\t'.join(f'{j} * {i} = {i*j}' for j in range(1,i+1)) for i in range(1,10)]))
1列表生成式
聚集索引的概念,每页数据为什么是 16k,可以是 32k 吗?可以使用 uuid 作为聚簇索引的主键吗?
聚集索引是一种特殊的数据库索引,在聚集索引中,数据按照索引的顺序存储,因此聚集索引决定了数据在磁盘上的物理排列方式。
每页数据 16k 是因为数据库管理系统(DBMS)在处理数据时,将数据分成固定大小的块,称为页面(page)。16k 是一种常见的页面大小,它是经过优化和平衡后的一个折衷选择。较小的页面大小可能会导致额外的磁盘 IO 操作,而较大的页面大小可能会浪费存储空间。因此,16k 被广泛应用于许多数据库系统。
虽然理论上可以使用 32k 作为页面大小,但是这种情况很少见。较大的页面大小可能会导致内存利用率下降,增加 IO 操作的开销,并且可能导致数据碎片化。
:::info 可以把页面大小的设计类比成一本书的纸张大小。页面太大,会降低书本的便携性;而页面太小,则会导致每页存储的有效信息太少,所以常见书本以 16 开或 32 开为主。 :::
可以使用 UUID 作为聚簇索引的主键,但是通常不建议这样做。UUID 是一个全局唯一的标识符,它的值是随机生成的。将 UUID 作为聚簇索引的主键会导致数据在磁盘上的随机分布,增加了插入和更新操作的开销,并且可能导致数据碎片化。在大规模的数据库系统中,通常会选择使用自增主键作为聚簇索引的主键,以提高性能和减少碎片化的可能性。
增加
page_size
的大小:如改为 32k,64k。数据库的页块大小改变需要改动源码。聚集索引的顺序问题
聚集索引不是物理上连续的,而是逻辑上连续的。(面试官怎么问这个问题的记不住了,好像是说先插入主键为 1,3,4,之后插入 2 是否可以插入?)
聚集索引是按照索引列的值进行物理排序的,它决定了数据在磁盘上的存储顺序。当你插入主键 id 为 1、2、4 的数据后,聚集索引会按照主键 id 的值进行排序,数据会按照 1、2、4 的顺序存储在磁盘上。
因此,你可以在插入主键 id 为 3 的数据,数据库会根据聚集索引的顺序将其插入到正确的位置,使得数据仍然保持有序。
事务四大特性,之间是否存在关联?
事务四大特性是指原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
这四个特性之间存在一定的关联。
原子性和一致性:原子性指事务中的操作要么全部成功执行,要么全部不执行,不会出现部分执行的情况。一致性指事务执行前后数据库的状态要保持一致。原子性和一致性是相互关联的,如果事务中的操作是原子性的,即要么全部成功执行,要么全部不执行,那么事务执行前后数据库的状态就能保持一致。
隔离性和一致性:隔离性指并发执行的事务之间是相互隔离的,每个事务都感觉不到其他事务的存在。一致性指事务执行前后数据库的状态要保持一致。隔离性和一致性是相互关联的,如果事务之间是相互隔离的,互不干扰,那么事务执行前后数据库的状态就能保持一致。
持久性和一致性:持久性指事务一旦提交,其结果就是永久性的,即使系统发生故障也不会丢失。一致性指事务执行前后数据库的状态要保持一致。持久性和一致性是相互关联的,如果事务一旦提交,其结果是永久性的,即使系统发生故障,也能保持事务执行前后数据库的状态一致。
综上所述,事务四大特性之间存在关联,彼此相互支持和保证数据库的一致性。
innoDB 和 MyISAM 的对比
InnoDB 引擎的四大特性是:
- 事务支持:InnoDB 引擎支持 ACID(原子性、一致性、隔离性和持久性)事务,可以确保数据的完整性和一致性。它支持提交(commit)和回滚(rollback)操作,可以保证在多个并发事务处理过程中的数据一致性。
- 行级锁定:InnoDB 引擎支持行级锁定,这意味着在同一时间可以对不同的行进行并发操作,提高了并发性能。与其他引擎如 MyISAM 相比,InnoDB 引擎的行级锁定能够减少锁冲突,提高并发性能。
- 外键约束:InnoDB 引擎支持外键约束,可以定义表与表之间的关系,保证数据的引用完整性。外键约束可以自动检查和维护数据的完整性,避免了数据不一致的问题。
- 高可靠性及快速回复数据:InnoDB 引擎具有高可靠性,支持数据的持久性存储和恢复。它使用 redo log 和 undo log 来记录和恢复数据的更改,可以在系统崩溃或断电后恢复数据的一致性。此外,InnoDB 引擎还支持自动崩溃恢复和自动故障切换,提高了数据库的稳定性和可靠性。
生成器取元素的方式,除了 next 还有哪些?
除了使用
next
方法取出生成器的下一个元素之外,还可以使用以下方法:- 使用
for
循环:可以直接在for
循环中迭代生成器,自动调用next
方法取出元素,直到生成器耗尽或达到停止条件。
for item in generator: # 处理元素
1
2- 使用
list
函数:可以将生成器转换为列表,通过一次性调用list
函数获取生成器的所有元素。
elements = list(generator)
1- 使用
iter
函数:可以将生成器转换为迭代器对象,然后使用iter
函数的__next__
方法取出元素。
iterator = iter(generator) element = iterator.__next__()
1
2- 使用
*
运算符:可以将生成器的所有元素解包到一个列表中。
elements = [*generator]
1需要注意的是,生成器是一种惰性计算的机制,只有在需要时才会生成元素。因此,每次取出元素时都会从生成器中消耗一个元素,直到生成器耗尽。如果生成器已经耗尽,再次调用
next
方法会引发StopIteration
异常。- 使用
新式类和经典类
# py2 class Py2Old: pass class Py2New(object): pass #py3 class Py3New: pass
1
2
3
4
5
6
7
8
9
10垃圾回收机制
gc.get_threshold()
(700, 10, 10)
2
700 代表新创建的对象减去从新创建的对象中回收的数量的差值大于 700 就进行一次 0 代回收当 0 代回收进行 10 次的时候就进行一代回收(并且一代回收的时候也进行 0 代回收),同理,当一代回收进行 10 次的时候就进行 2 代回收(并且二代回收的时候也进行 0 代回收和一代回收)
# 众趣科技
三次握手,四次挥手
索引的原理 顺序访问指针,所以查询快
怎么分析慢查询? explain 查询是否为全表扫描,是的话加索引
最左匹配原则?
MySQL 索引的最左匹配原则是指,在使用联合索引时,可以利用索引的最左边的列来进行查询,但是如果查询中没有使用到索引的最左边的列,那么该索引将不会被使用。
举个例子,假设有一个联合索引(col1, col2, col3),如果查询语句中只使用了 col2 和 col3 进行筛选,而没有使用 col1,那么该索引将不会被使用。因为 MySQL 的索引是按照索引列的顺序来存储和搜索的,如果查询中没有使用到索引的最左边的列,那么 MySQL 无法利用该索引进行快速搜索。
需要注意的是,最左匹配原则并不是说只能使用联合索引的最左边的列进行查询,而是指如果查询语句中没有使用到索引的最左边的列,那么该索引将不会被使用。也就是说,如果查询中使用了索引的最左边的列,那么 MySQL 可以利用该索引进行搜索,但是如果查询中没有使用到索引的最左边的列,那么该索引将不会被使用。
最左匹配原则在设计索引时非常重要,可以根据查询的特点来选择合适的索引列顺序,以提高查询性能。
乐观锁、悲观锁?
乐观锁和悲观锁是数据库中常用的并发控制机制。
乐观锁(Optimistic Locking)是一种乐观的并发控制策略。在乐观锁机制中,假设不会发生并发冲突,因此在读取数据的时候不会加锁,只有在更新数据的时候才会检查是否有其他事务修改了数据。乐观锁通常使用版本号或时间戳来实现。当一个事务要更新数据时,会先读取数据并记录版本号或时间戳,然后在更新时检查是否有其他事务修改了数据,如果有则回滚操作,重新读取数据并重新尝试更新。
悲观锁(Pessimistic Locking)是一种悲观的并发控制策略。在悲观锁机制中,假设会发生并发冲突,因此在读取数据的时候会加锁,确保其他事务无法修改数据。悲观锁通常使用行级锁或表级锁来实现。当一个事务要读取数据时,会先加锁,其他事务无法修改数据,直到该事务释放锁。这种机制可以防止并发冲突,但也会导致并发性能下降。
乐观锁适用于读操作较多的场景,因为读操作不会加锁,可以提高并发性能。悲观锁适用于写操作较多的场景,因为写操作需要加锁保证数据的一致性。选择乐观锁还是悲观锁取决于具体的业务需求和并发情况。
当涉及到实现乐观锁和悲观锁时,具体的实现方式会根据数据库的类型和使用的框架而有所不同。以下是一个使用 Python 和 MySQL 数据库的示例代码:
- 乐观锁的实现:
import pymysql def update_data(id, new_value, version): conn = pymysql.connect(host='localhost', user='root', password='password', db='test') cursor = conn.cursor() try: # 表中需要有版本号字段 cursor.execute("SELECT value, version FROM data WHERE id = %s", (id,)) row = cursor.fetchone() if row: current_value, current_version = row if current_version == version: cursor.execute("UPDATE data SET value = %s, version = version + 1 WHERE id = %s", (new_value, id)) conn.commit() print("Update successful!") else: print("Conflict detected! Retry the operation.") else: print("Data not found!") except Exception as e: print("Error:", e) conn.rollback() finally: cursor.close() conn.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26在上述代码中,首先通过
SELECT
语句获取要更新的数据的当前值和版本号。然后,检查当前版本号是否与传入的版本号相同。如果相同,则执行UPDATE
语句更新数据并增加版本号。如果版本号不相同,则说明有其他事务修改了数据,需要重新尝试更新。- 悲观锁的实现:
import pymysql def update_data(id, new_value): conn = pymysql.connect(host='localhost', user='root', password='password', db='test') cursor = conn.cursor() try: cursor.execute("SELECT * FROM data WHERE id = %s FOR UPDATE", (id,)) row = cursor.fetchone() if row: cursor.execute("UPDATE data SET value = %s WHERE id = %s", (new_value, id)) conn.commit() print("Update successful!") else: print("Data not found!") except Exception as e: print("Error:", e) conn.rollback() finally: cursor.close() conn.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21在上述代码中,通过
SELECT ... FOR UPDATE
语句获取要更新的数据并加锁,确保其他事务无法修改数据。然后,执行UPDATE
语句更新数据,并提交事务。需要注意的是,上述代码仅为示例,实际实现中需要根据具体的业务需求和数据库类型进行相应的调整。
深拷贝,浅拷贝? Python 全栈之路系列之深浅拷贝
gRPC 工作原理?
多态解释一下? A 类中的 walk 和 b 类中的 walk 方法可以接受不同类型的参数,但执行的逻辑不一样。 面向对象之封装、继承、多态 | 别院牧志知识库
# WSGI 与 ASGI
- 什么是 WSGI
先说一下CGI
,(通用网关接口, Common Gateway Interface/CGI),定义客户端与 Web 服务器的交流方式的一个程序。例如正常情况下客户端发来一个请求,根据HTTP
协议 Web 服务器将请求内容解析出来,经过计算后,再将内容封装好,例如服务器返回一个HTML
页面,并且根据HTTP
协议构建返回内容的响应格式。涉及到TCP
连接、HTTP
原始请求和相应格式的这些,都由一个软件来完成,这时,以上的工作需要一个程序来完成,而这个程序便是CGI
。
那什么是WSGI
呢?维基 (opens new window) 上的解释为,Web 服务器网关接口(Python Web Server Gateway Interface,WSGI),是为Python
语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。从语义上理解,貌似WSGI
就是Python
为了解决Web 服务器端与客户端之间的通信问题而产生的,并且WSGI
是基于现存的CGI
标准而设计的,同样是一种程序(或者Web
组件的接口规范?)。
WSGI (opens new window) 区分为两部分:一种为“服务器”或“网关”,另一种为“应用程序”或“应用框架”。
所谓的WSGI
中间件同时实现了API
的两方,即在WSGI
服务器和WSGI
应用之间起调解作用:从WSGI
服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。中间件具有的功能:
- 重写环境变量后,根据目标 URL,将请求消息路由到不同的应用对象。
- 允许在一个进程中同时运行多个应用程序或应用框架
- 负载均衡和远程处理,通过在网络上转发请求和相应消息。
- 进行内容后处理,例如应用
XSLT
样式表。(以上 from 维基)
看了这么多,总结一下,其实可以说WSGI
就是基于Python
的以CGI
为标准做一些扩展。
异步网关协议接口,一个介于网络协议服务和Python
应用之间的标准接口,能够处理多种通用的协议类型,包括HTTP
,HTTP2
和WebSocket
。
然而目前的常用的WSGI
主要是针对HTTP
风格的请求响应模型做的设计,并且越来越多的不遵循这种模式的协议逐渐成为Web
变成的标准之一,例如WebSocket
。
ASGI
尝试保持在一个简单的应用接口的前提下,提供允许数据能够在任意的时候、被任意应用进程发送和接受的抽象。并且同样描述了一个新的,兼容HTTP
请求响应以及WebSocket
数据帧的序列格式。允许这些协议能通过网络或本地socket
进行传输,以及让不同的协议被分配到不同的进程中。
- WSGI 和 ASGI 的区别在哪
以上,WSGI
是基于HTTP
协议模式的,不支持WebSocket
,而ASGI
的诞生则是为了解决Python
常用的WSGI
不支持当前Web
开发中的一些新的协议标准。同时,ASGI
对于WSGI
原有的模式的支持和WebSocket
的扩展,即ASGI
是WSGI
的扩展。
ref:WSGI&ASGI - 简书 (opens new window)
# redis 缓存热点数据,哪些数据属于热点数据
Redis 的过期策略有两种:定期删除和惰性删除。
定期删除: Redis 默认使用的是定期删除策略。它会以一定的频率(默认每秒钟 10 次)随机抽取一部分设置了过期时间的 key,并检查它们是否过期,如果过期则删除。这种策略的优点是简单高效,缺点是可能会造成大量过期 key 未被及时删除,占用过多内存。
惰性删除: 惰性删除是指在获取某个 key 时,Redis 会先检查该 key 是否过期,如果过期则删除。这种策略的优点是能够及时删除过期 key,节省内存空间,缺点是在获取过期 key 时会有一定的性能损耗。
Redis 还提供了一些配置参数来调整过期策略的行为,包括:
maxmemory-policy:指定内存达到上限时的淘汰策略,默认是 noeviction,表示不淘汰数据,直接返回错误。
redis的淘汰策略:默认是 noeviction noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错; allkeys-lru:尝试回收,最近未使用或者使用比较少的键。(范围是:所有的键) volatile-lru:尝试回收,最近未使用或者使用比较少的键。(范围是:设置了过期时间的键) allkeys-random:随机移除某个key。(范围是:所有的键) volatile-random:随机移除某个key。(范围是:设置了过期时间的键) volatile-ttl:回收过期时间较短的key。(范围是:设置了过期时间的键)
1
2
3
4
5
6
7maxmemory-samples:指定定期删除策略每次抽取的 key 数量,默认是 5。
lazyfree-lazy-eviction:是否开启惰性删除策略,默认是 no,表示关闭。
根据实际需求,可以根据以上配置参数来调整 Redis 的过期策略。
# 其他
- 已经有了 GIL 多线程为什么还要加锁?
float
相加精度问题- 为什么协程比线程快?切换为什么快?
futures
模块- 单进程里面的单进程和单进程消耗的资源哪个更多还是一样多?
- 设计模式
- redis 缓存使用?io 模型?多路复用解释一下?数据结构内部实现?