0%

笔试题

python基础

类型

  • a=(1), int

  • b=(1,),str

  • c=("1"),tuple

  • 列表推导式转生成器,[i for i in [1,2]]变更为(i, for i in [1,2])

集合

  1. set是一个无序、不重复元素序列;
  2. 创建一个空集合必须是set(),因为{}是一个字典;
  3. 不可索引、不可切片、不可修改元素;
  4. 可遍历
  5. 字典一样,即使用hash,不可放入可变对象,因为是使用hash计算存储,无法判断两个可变对象是否相等;
  6. 元素必须是可哈希的,即不可变对象;
  7. set()参数为字符串时,默认变更为集合;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
s1 = set()
s2 = set()

#交
s3 = s1 & s2
#并
s3 = s1 | s2
s3 = s1.union(s2)
#差
s3 = s1.difference(s2)
s3.add(1)
s3.pop()
s3.clear()
s3.remove(2)

列表

  • extend()两个列表合并,无返回值
  • 多列表合并
1
2
sum([[1,2],[3,4],[5,6]], [])
# [1,2,3,4,5,6]
  • 打乱列表元素,random.shuffle(list)
  • 通过切片获取列表可以超出长度,但通过索引获取元素不可以超过长度
  • 避免索引错误
    • 当s=''时,s[0]和s[-1]会报IndexError: string index out of range,但是s[:1])和s[-1:]不会。

元祖

不可变数据类型,不是真的不可变,是元素的地址不可变,比如如果元素是list,可以进行list的变更

排序

  • sort,会改变原对象,但会None
  • sorted,不改变原有对象,返回排序好的

其中key参数可以返回元祖,元祖的元素个数基本代表排序次数,默认小到大

key的元素或值:

​ True表示大值(要放后面),

​ False表示小值(要放前面),

​ 其他可排序的比如数字、字母就按照指定顺序排序

1

字典

  • 空间换时间,本质是数组,根据hash和偏移来存储和查询吗,当达到数2/3(不确定)时,会扩容
  • 查询快
  • 自定义key对象需要支持以下:
    • 支持hash()函数
    • 支持通过``方法检测相等
    • 若值相等,则hash()也相等;

什么样的语言能够用装饰器

函数可以作为参数传递的语言;

2和3的区别

  1. python3的print()是内置函数,pyhton
列项 2 3
print 语法结构 内置函数
range()返回值 列表 迭代器
默认编码 ascii utf-8
字符串序列 unicode str
字节序列 str byte
int 长度取决于系统位数 长整型
long 不受限制 没有,只有无限长度的长整型
类继承即__mro__顺序 深度优先 广度优先

正则

  1. re.compile()作用,编译成一个对象,加快速度,重复利用;
  2. (.*) 贪婪匹配,尽可能多的匹配;(.*?)非贪婪匹配,尽可能少的匹配;

函数

  • x.join(),参数是可迭代对象,x插入可迭代对象中间,形成字符串;
  • zip()
    • 以一个或多个序列(可迭代对象)做为参数,返回一个元组的列表。同时将这些序列中并排的元素配对。
    • 参数可以接受任何类型的序列,同时也可以有两个以上的参数;当传入参数的长度不同时,zip能自动以最短序列长度为准进行截取,获得元组。
  • sortsorted
    • sort,属于列表成员方法;无返回值,会更改列表
    • sorted,可以对可迭代对象进行排序,返回新的有序列表

传参

可以理解为引用传递;

​ 不可变类型因为不可变所以互不影响;

​ 可变需要注意;

编解码

字符串在python中是unicode编码,编码转换时是以unicode作为中间编码

  • encode,将unicode编码转换成其他编码的字符串
  • decode,将其他编码的字符串转换成unicode编码

字符串比较

1
2
3
4
5
a = '123'
b = '456'
'''
a和b为不可变类型,所以指向相同地址;
'''
  • is指地址相同
  • ==指值相同
  • 字符串相加还是字符串
  • 字符串不能与数字相加

LEGB

1
2
3
4
5
x = 1
def add(a):
x += 1
print(x)
add(x)

会报错,因为LEGB规定了查找一个名称的顺序为

  • L:local,即函数内部命名空间
  • E:encloseing function locals,外部嵌套函数的空间
  • G:global(module),模块空间
  • B:builtin,内置模块空间

如果不修改x,只去访问x,是合法的

类实例关系

  • isinstance(object, classinfo),用于判断object是否是classinfo的一个实例,或者object是否是classinfo类的子类的一个实例,如果是返回True.
  • issubclass(class, class),用于判断class是否是classinfo类的子类,如果是返回True.

__name__

  1. 定义在一个模块中,执行这个py文件时,值是__main__
  2. 该模块被其他模块import时,其值是运行的py文件的名称

性能指标

  • QPS,每秒请求数
  • TPS,服务器每秒处理的事务数
    • 平均
    • 峰值
    • 最低
  • 响应时间
    • 平均
    • 最大
    • 平均

迭代器和生成器

生成器是通过函数的形式中调用yield或()的形式创建的。迭代器可以通过iter()内置函数创建。用法上:生成器在调用next()函数或for循环中,所有过程被执行,且返回值。而迭代器,所有值被返回,没有其他过程或动作

迭代器和迭代对象

  1. 迭代器是这样一个对象,实现了无参数__next__方法,返回序列中的下一个元素,如果没有元素了,那么抛出StopIteration异常。

  2. 迭代对象

    如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的。

    根据可迭代协议,__iter__方法实例化并返回一个迭代器,

    Python 中的迭代器还实现了__iter__方法,因此迭代器也可以迭代。

  • python迭代对象的流程
    在对一个可迭代对象迭代时,具体流程如下
  1. 检查对象是否实现了 __iter__方法,如果实现了就调用它,获取一个迭代器

  2. 如果没有实现__iter__方法,但是实现了__getitem__方法,
    python 会创建一个迭代器,尝试按顺序(从索引 0 开始)获取元素

  3. 如果尝试失败,python 抛出 TypeError 异常,通常会提示"x object is not iterable"

迭代器与可迭代对象的关系
python是通过可迭代对象来获取迭代器的。

闭包

在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

变量作用域请参考 LEGB

1
2
3
4
5
def mult():
return [lambda x:x*i for i in range(4)]

print([m(3) for m in mult()])
# [9, 9, 9, 9]

比好的后期绑定,闭包中的变量时在内部函数被调用的时候查找的

匿名函数

  • 通过lambda关键字来定义的函数称为匿名函数
  • 简化代码复杂性,提高代码可读性

提高效率

  • 使用生成器,减少内存使用
  • 关键功能用其他编写,比如C
  • 使用多进程、多线程
  • 等等

pep8

代码格式建议,增强代码可读性以及规范性

pylint,flake8, autopep8

python之禅

交互模式下import this

类型注解

因为python是解释型语言,没有参数等的类型声明,类比java等编译型语言,增加代码可读性

对象命名规则

  • 模板,小写+下划线
  • 类,大写驼峰
  • 函数:小写、下划线
  • 全局变量:全大写,下划线
  • _xxx:表明是一个受保护对象,原则上不允许直接访问,外部类还是可以访问,是一个约定:警告这是一个私有变量,不要去访问他
  • __xxx:双下划线开头,表示私有变量,只允许类本身访问,子类也不可以;
    • 直接调用是报错
    • 解释器会做名称修饰,防止被子类重写:可以使用_{ClassName}__xxx来访问
  • __xxx__:内置变量,可以直接访问
  • xxx_:避免于关键字冲突
  • _xxx():私有方法
  • __xxx():同__xxx

注释

  • 单行
  • 多行

import可以同时导入多个库,但不推荐

zip

​ zip用户将元素打包,已长度小的为准

1
2
3
4
5
6
7
8
9
10
r = zip(('a','b'),(1,2))
print(r)
r = zip(('a','b'),(1,2,3))
print(r)
r = zip(('a','b'),'1234')
print(r)

[('a', 1), ('b', 2)]
[('a', 1), ('b', 2)]
[('a', '1'), ('b', '2')]

对生成器实现列表的切片操作

itertools.islice(生成器对象,start_index, end_index)

类继承顺序

请参考 2和3的区别

2是深度优先,3是广度优先

reduce

一句话解决阶乘reduce(lambda x,y:x*y, rnage(1, 10))

描述符

https://iamfengdy.github.io/posts/20210507-bf389a0f.html

链接

复制

  1. 当复制对象是不可变对象时,复制的都是地址,即ID都一样;
  2. 当复制对象是可变对象时
    1. 若元素为不可变对象,则复制后元素改变互不影响;
    2. 若元素为可变对象,则复制后元素改变会共同影响;

网络

  1. tcp握手

缓存

雪崩

​ 主要是缓存大面积失效,像雪崩一样;

  • 简介

    指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉

  • 方案

    • 数据过期时间设置随机,防止同一时间大量数据过期现象发生;
    • 加锁排队,一般并发量不是特别多的时候,使用;
    • 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存

穿透

​ 一定要注意,就是缓存和数据库都没有;

  • 简介

    指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉

  • 方案

    • 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;

    • 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击;

    • 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力;

    • Bitmap:典型的就是哈希表

      • 缺点是,Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的空间、时间来完成了
    • 布隆过滤器(推荐)

      • 就是引入了k(k>1)k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。
        它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
        Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。
        Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。

        Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。

        最重要的是:

        • 一个元素如果判断结果为存在的时候元素不一定存在,但是判断结果为不存在的时候则一定不存在
        • 布隆过滤器可以添加元素,但是不能删除元素。因为删掉元素会导致误判率增加。

击穿

穿,表示只是缓存没有,数据库有,所以只能是穿而不是透,一般指同一条数据;

  • 简介
    • 缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库
  • 方案
    • 设置热点数据永远不过期
    • 加互斥锁,互斥锁

预热

  • 简介
    • 像汽车发动一样,系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据;
  • 方案
    • 直接写个缓存刷新页面,上线时手工操作一下;
    • 数据量不大,可以在项目启动的时候自动进行加载
    • 定时刷新;

降级

  • 简介

    当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

    最终目的:保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

    在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

    • 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
    • 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警
    • 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级
    • 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级;

    服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

热点key

缓存中的一个Key(比如一个促销商品),在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

  • 方案
    • 对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询

更新提交问题

567f8de2b2a442d88fe5b392484e8ef5

redis

简介

  • 优点

    • 数据机构丰富,键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合
    • 支持数据持久化:RDB(redis database),AOF(append only file)
    • 支持事务
    • 支持主从复制
  • 缺点

    • 受物理内存限制
    • 其他

为什么快

  • 完全基于内存,类似于HashMap,查找和操作的时间复杂度都是O(1);
  • 数据结构简单
  • 采用单线程,避免了上下文切换和竞争等带来的消耗
    • 是执行 Redis 命令的核心模块是单线程的,而不是整个 Redis 实例就一个线程,Redis 其他模块还有各自模块的线程的。
  • 使用非阻塞IO,即多路复用
  • 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求

持久化

​ 同时使用时AOP优先RDB进行恢复;

  • RDB模式(默认),redis-database
    • 一定时间将数据以快照(某一时刻的状态记录)的形式保存到硬盘,生成dumo.rdb
    • 优点
      • 只有一个文件,方便持久化
      • 性能最大化,fork子进程完成写,主进程继续处理命令(单独的子进程来进行持久化)
      • 比AOF的启动效率高
    • 数据安全性低,发生故障时会发生数据丢失,适合数据不严谨的时候;
  • AOF,append-only-file
    • 将每次执行的写命令记录到单独日志中,重启时恢复

    • 缺点

      • 文件大,恢复慢
      • 启动效率低

      优缺点是什么?
      RDB更新频率高,优先使用AOF还原数据。
      AOF比RDB更安全也更大
      RDB性能比AOF好
      如果两个都配了优先加载AOF

过期删除策略

  • 定时

    将数据创建一个定时器,会占用大量CPU资源去处理,从而影响效率

  • 惰性定期

    查询的时候才判断,过期则删除

  • 定期

    每隔一段时间清楚

Redis的内存淘汰策略

Redis的内存淘汰策略有哪些

Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。

  1. 全局的键空间选择性移除
    noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
    allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的)
    allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
  2. 设置过期时间的键空间选择性移除
    volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
    volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
    volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
  3. 总结
    Redis的内存淘汰策略的选取并不会影响过期的key的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据。

内存用完,会发生写错误,但读不影响

为什么使用单线程

因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)Redis利用队列技术将并发访问变为串行访问

  1. 绝大部分请求是纯粹的内存操作(非常快速)
  2. 采用单线程,避免了不必要的上下文切换和竞争条件
  3. 非阻塞IO优点

查找

  • keys命令会致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复;
  • scan可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长;

与memcache相比

bd7addd5cc0649d98d30c4c0fee42798

mysql

MyISAM 与 InnoDB 区别:

  1. InnoDB 支持事务,MyISAM 不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而 MyISAM

就不可以了;

  1. MyISAM 适合查询以及插入为主的应用,InnoDB 适合频繁修改以及涉及到安全性较高的应用;
  2. InnoDB 支持外键,MyISAM 不支持;
  3. 对于自增长的字段,InnoDB 中必须包含只有该字段的索引,但是在 MyISAM表中可以和其他字段一起建立联合索引;
  4. 清空整个表时,InnoDB 是一行一行的删除,效率非常慢。MyISAM 则会重建表;

乐观锁和悲观锁

悲观锁, 就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制,乐观锁适用于多读的应用类型,这样可以提高吞吐量

算法

8大算法

https://iamfengdy.github.io/categories/algorithm/sorting/

设计模式

https://iamfengdy.github.io/categories/design/java-design-patterns/

数据结构

https://iamfengdy.github.io/posts/20210218-7b216a3b.html

红黑树 https://blog.csdn.net/u012414189/article/details/83832161