Python技能点
数据类型
整型 int 纯数字 不可变类型
浮点型 float 带小数点的数字 不可变类型
字符串 str 用单引号或者双引号括起来的元素,元素可以是任意类型的数据 不可变类型
列表 list 用中括号括起来,元素和元素之间用逗号隔开,元素可以是任意类型的数据.是有索引的 可变类型
字典 dict 用大括号括起来,采用k-v键值对,k必须是字符串类型. 可变类型
布尔值 bool 只有True或者False 不可变类型
集合 set 用大括号括起来,元素和元素之间用逗号隔开,是无序的且不可重复,即没有索引,用迭代器取值. 可变类型
元祖 用小括号括起来,元素和元素之间用逗号隔开,是有序的. 用起来和列表一样,但是是不可变类型
可变: 集合 列表 字典
不可变: 整型 浮点型 字符串 布尔值 元祖
1.什么是内置函数
就是python提供的内置的函数,可以直接拿来用的.
比如
abs() # 返回数字的绝对值
all() # 判断所有元素都为true结果才是ture
bin() # 10进制转2
oct() # 10进制转8
hex() # 10进制转16 值得注意的是进制转换要从第三位开始转,因为python进制返回的都是字符串,且有0b ,0o,0x的前缀, [2:]索引2开始往后取就OK
bytes() # 转成字节
str() # 转成字符串
callable() # 判断加括号能不能调用
dir() # 查看当前对象可以调用的名字
sum() # 求和,只能求int类型
int() # 将字符串转换成数字,字符串必须是纯数字
map() # 映射
zip() # 拉链
# 常用内置模块
os,sys,time,json,logging,re,hashlib,
2.匿名函数
用lambda定义的匿名函数,这种函数可以用在任何普通函数可以使用的地方,但是定义时被限制为单一表达式,从语义上讲他只是普通函数的语法糖.组成接收返回值的变量名 = lambda 参数列表: [表达式] 变量
# 用处:
1、lambda 函数比较轻便,即用即仍,很适合需要完成一项功能,但是此功能只在此一处使用,
连名字都很随意的情况下;
2、匿名函数,一般用来给 filter, map 这样的函数式编程服务;
3、作为回调函数,传递给某些应用,比如消息处理.
def add(a, b=1)
return a + b
add_lambda = lambda a,b=1:a + b
age_dict = lambda li
3.函数递归
# 什么是函数递归?
函数在运行过程中,直接或者间接的调用了自身.
# 默认递归深度?
默认递归深度是1000,根据CPU不同一般在997-1000次 但是深度可以更改,没什么意义,一般情况下用不到那么深
# 递归的本质:
递推: 一层层往下推理.
回溯:根据最后的结果往前推导.
递归函数并非无限循环的过程,其有两个特点:
1.每次递归,复杂度必须降低.
2.一定要有明确的结束条件.
4.闭包函数
1.定义在函数内部的函数
2.内部函数使用了外部函数名称空间中的名字
只有符和上述2个特征的函数才可以称之为"闭包函数"
使用闭包的时候需要注意,闭包会使得函数内创建的那个对象不会被垃圾回收,可能会导致很大的内存开销,所以闭包一定不能滥用
5.反射机制
反射就是通过字符串的形式导入模块,通过字符串的形式去模块寻找指定的函数并执行,利用字符串的形式去对象中增删改查,是一种基于字符串的时间驱动、
是程序在运行过程中通过字符串来操作对象的属性和方法
# 我们常用的反射函数
在类里面定义
class A:
def __init__(self):
self.a=1
def getattr():
# 获取a的值的时候执行这个方法
def hasattr():
# 可以用来判断里面有没有a这个属性
def setattr():
# 修改a的时候触发他
def delattr():
# 删除a的时候触发他
6.装饰器
用处:给被装饰对象添加而外的功能,他遵循一个开放封闭的原则,即对扩展开放,对修改封闭
核心思想:在不改变"被装饰对象"内部代码和原有的调用方式的基础上添加额外的功能
# 装饰器语法糖书写规范
语法糖必须紧贴在被装饰对象的上方
# 装饰器语法糖内部原理
会自动将下面紧贴着的被装饰对象名字当做参数传给装饰器函数调用
# 固定模板
def outer(func):
def inner(*args, **kwargs):
print('执行函数之前可以添加的额外功能')
res = func(*args, **kwargs) # 执行被装饰的函数
print('执行函数之后可以添加的额外功能')
return res # 将被装饰函数执行之后的返回值返回
return inner
7.GC机制
GC机制即垃圾回收机制
Python的垃圾回收机制以引用计数为主,标记清除和分代回收为辅,引用计数最大的缺陷就是循环引用的问题(比如a=b,b=a这样循环引用)导致无法回收,所以python就会采用辅助的方法,即标记清除,但是标记清除会引发另外的问题,比如说一个新变量定义好以后啊没去使用,他不能一直占着这块内存空间啊,所以就再去引用分代回收,分代回收会更频繁的处理新对象,如果经过几个时间周期以后还存在的对象,再去进行迭代,也就是说满足一定条件才会扫描一次.
# 引用计数:
如果有新的引用指向对象,对象引用计数就加一,引用被销毁时,对象引用计数减一,当用户的引用计数为0时,该内存被释放
# 标记清除:
首先标记对象(垃圾检测),然后清除垃圾(垃圾回收)
首先初始所有对象标记为白色,并确定根节点对象(这些对象是不会被删除),标记它们为黑色(表示对象有效)。将有效对象引用的对象标记为灰色(表示对象可达,但它们所引用的对象还没检查),检查完灰色对象引用的对象后,将灰色标记为黑色。重复直到不存在灰色节点为止。最后白色结点都是需要清除的对象
# 分代回收:
算法原理是Python把对象的生命周期分为三代,分别是第0代、第1代、第2代。每一代使用双向链表来标记这些对象。每一代链表都有总数阈值,当达到阈值的时候就会出发GC回收,将需要清除的清除掉,不需要清除的移到下一代。以此类推,第2代中的对象存活周期最长的对象
8.OOP
OOP是一种编程思想,程序设计.
也就是面向对象编程思想.
核心:封装、继承、多态
- OOP 的核心是建模,将万事万物进行归类,抽象出属性和方法。
# python中一切皆对象的理解:
面向对象编程的主要概念包括以下几个方面:
类(Class):类是对象的抽象描述。它定义了对象的属性和方法。可以认为类就像是一个蓝图,用于创建实际的对象。
对象(Object):对象是类的一个实例。它具有类定义的属性和方法,并可以在运行时执行这些方法。
封装(Encapsulation):封装是将相关数据和方法组合到一个单独的单元中的过程。通过封装,对象的内部数据和实现细节被隐藏起来,只对外提供接口进行访问。
继承(Inheritance):继承是一种机制,允许一个类从另一个类派生出来并继承其属性和方法。通过继承,子类可以重用父类的代码,并具有自己独特的特性。
多态(Polymorphism):多态允许使用统一的接口来处理不同类型的对象,即同一个方法可以根据实际对象的类型执行不同的操作。
面向对象编程提供了许多优点,如代码可重用性、组织性、可维护性和扩展性。它使得程序更加模块化,易于理解和调试。Python 是一种支持面向对象编程的语言,在 Python 中可以使用类和对象来实现面向对象的设计。通过定义类和创建对象,可以利用面向对象编程的特性构建复杂的应用程序。
9.LEGB(函数作用域)
L(locals): 函数内部作用域
E(enclosing):函数内部和函数嵌套之间的作用域 # 即闭包作用域
G(global): 全局作用域
B(buil-in): 内置作用域
10.鸭子类型
# 鸭子类型是python语言面向对象中的一个概念
#面向对象三大特性
-继承
-封装
-多态
# 多态和多态性?
-多态同一类事物的多种形态
# 现实生活中
-水:冰,水,水蒸气
-动物:人,狗,猫,猪
# 程序中
-Animal类:子类人,子类狗,子类猫
-多态性:不考虑对象具体类型的情况下使用对象
# 程序中
-len()内置函数---》传参数:字符串对象,列表对象,字典对象
-len(字典)
# 为什么能这样用?就是因为多态的存在
字符串,列表,字典----》属于同一类事物---》有长度的这一类事物
# 鸭子类型
-走路像鸭子,说话像鸭子,我们就可以叫它叫鸭子
-解释:鸭子类型是python面向对象中描述接口的一个概念,区分与其他编程语言,
比如java:实现类型,必须显示的继承这个类
而python:实现类型,遵循鸭子类型,不需要显示的继承一个类,只要类中有对应的属性跟方法,我们就称这几个类的对象为同一种类型
鸭子类型:它是一种动态的编程风格,我们更多关注的是对象的行为,而不是对象的类型,我们不用在使用type或者isinstance查出它的类型了,允许我们在调用方法的时候,只要对象拥有这个方法,我们就可以当成参数传递过去,而无需关心类型
11.静态和类方法
静态方法就是类里面的方法,通过在方法名上面加个语法糖@staticmethod来定义,静态方法和在模块中定义普通函数没有区别,只不过静态方法放到了类的作用域里面,需要通过类调用
类方法是从属于"类对象"的方法.类方法通过装饰器@classmethod来定义.它必须位于方法上面一行.类方法中访问实例属性和数理方法会报错,子类继承父类方法时,传入的cls是子类对象,而非父类对象
@property 装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。被修饰的方法可以像属性一样访问。
12.偏函数
偏函数(Partial function)是Python的functools模块提供的一个很有用的功能。
简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
所以,当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
# functools.partial 凡客兔儿死.爬馊
13.魔法方法
在类里面前后用双下划线包裹的方法称之为魔法方法,例如有__init__,__new__,__getattr__系列,__iter__可迭代对象
# __init__和__new__的区别?
# __init__
__init__是初始化函数,不用返回值
# __new__
__new__是构造方法,__new__通常不用手动定义,一般在元类编程中使用.控制类的生成过程..执行的时候是第一个执行在init之前,并且必须要有返回值,返回该类的实例,他会被__init__函数接收,通常是叫self变量
14.进程, 线程, 协程
# 进程
一个程序运行起来后,代码+用到的资源称之为进程,他是操作系统分配资源的基本单元.多进程一般适用于cpu密集型任务上面,cpu密集型任务一般指需要大量的做逻辑运算,比如上亿次的加减乘除,使用多核cpu可以并发提高计算性能.
但是因为之前我做的都是to B项目,所以对于多进程其实能用的场景不多.不过多追求效率.
# 线程
是操作系统能够进行运算调度的最小单位.他被包含在进程中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程可以并发多个线程,每条线程并行执行不同的任务.
线程是独立调度和分派的基本单位.
一个进程可以有很多线程,每条线程并行执行不同的任务.
多线程适用在IO密集型任务上面,因为python有个GIL锁,他要求线程执行前,需要获取GIL锁,获得该锁的线程才可以执行,没有获得的只能等待,当具有GIL锁的线程运行完成后,其他等待的线程就会去争夺GIL锁,这就导致了在使用多线程时,同一时刻只有一个线程在运行,所以python多线程其实不是并行,而是并发.
# 协程
又叫微线程,纤程.
携程是python中另外一种实现多任务的方式,只不过比线程更小占用更少的资源.它自带cpu上下文,只要在合适的时机,我们可以把一个协程切换到另一个协程.只要这个过程中保存或者恢复CPU上下文那么程序还是可以运行的
通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换回原来执行的函数都是开发者决定的.
15.GIL锁
# 谈谈GIL是什么?
GIL锁是全局解释器锁,是一种互斥锁
他是cpython解释器的特性,不是python的特性,他要求线程执行前,需要获取GIL锁,获得该锁的线程才可以执行,没有获得的只能等待,当具有GIL锁的线程运行完成后,其他等待的线程就会去争夺GIL锁,这就导致了在使用多线程时,同一时刻只有一个线程在运行,所以python多线程其实不是并行,而是并发
# 为什么选择GIL这种方案?
设计者为了设计简单,规避内存管理的竞争风险.
易于使用,快速上手.
-1 gil锁是全局解释器锁,他的本质是互斥锁,只有CPython存在gli锁,他限制了线程只有获得了gil锁,才能执行,如果没有得到gil锁,不能执行
-2 解释器,有CPython pypy jython,ipython
-3 gil锁的作用是什么
-限制线程只有获得gil锁才能执行
-4 为什么要有gil锁
-当我们起一个垃圾回收的线程,起一个正常执行代码的线程,当垃圾回收线程还没有把垃圾回收完毕的时候,会出现抢占资源的情况,如果想要线程能够正常执行,那么这个线程需要拿到gil锁
-多线程不能利用多核优势,因为有gil锁,同一时刻只有一个线程在运行,如果想利用多核优势,可以开多进程
'''
# 为什么要有gil锁?
-python是动态强类型语言,因为有垃圾回收机制,如果同一个进程下有多个线程同时在执行,垃圾回收是垃圾回收线程【同一个进程下变量是共享的】,该线程做垃圾回收时,如果其他线程在运行,就可能会出并发安全的问题【数据安全的问题】,由于当时,只有单核cup【即便开启多线程,同一时刻,也只有一个线程在运行】,作者就强行做了一个GIL锁,保证在一个进程内,同一时刻只有一个线程执行,目的是为了防止垃圾回收线程做垃圾回收时,出现数据紊乱问题,所以加了gil锁
-垃圾回收是垃圾回收线程,它在执行的时候,其他线程是不能执行的,而限于当时的条件,只有单核cpu,所以作者直接做了个GIL锁,保证一个进程内同一时刻只有一个线程在执行
-python使用引用计数为主,标记清楚和隔代回收为辅来进行内存管理。所有python脚本中创建的对象,都会配备一个引用计数,来记录有多少个指针来指向它。当对象的引用技术为0时,会自动释放其所占用的内存。
假设有2个python线程同时引用一个数据(a=100,引用计数为1),
2个线程都会去操作该数据,由于多线程对同一个资源的竞争,实际上引用计数为3,
但是由于没有GIL锁,导致引用计数只增加1(引用计数为2)
这造成的后果是,当第1个线程结束时,会把引用计数减少为1;当第2个线程结束时,会把引用计数减少为0;
当下一个线程再次视图访问这个数据时,就无法找到有效的内存了
'''
16.生成器迭代器
闭包函数:1 定义在函数内部 2 对外部作用域有引用
闭包函数多了一种给函数传参的方式
典型应用就是装饰器
所有语言都有闭包函数---》所有语言就可以实现装饰器--》但是没有装饰器的语法糖
装饰器:本身是一个闭包函数,作用是在不改变 被装饰对象源代码和调用方式的基础上,为它加入新功能
flask的路由就是基于装饰器
django的信号也可以用装饰器方式注册
django中局部去除csrf认证
为接口记录访问日志
认证。。
迭代:一种不依赖于索引取值的方式,只要能够一个个取值,它就称之为迭代
for循环, next()
可迭代对象:可以迭代的(for,next取值的)python中的对象称之为可迭代对象
在python中可以被for循环或可以变量.next()取值的对象称之为可迭代对象
有 :字符串,列表,字典,元组,集合,文件对象
迭代器:可迭代对象调用__iter__,就得到了迭代器,迭代器有__iter__和__next__方法
自定义迭代器:写个类,类中重写__iter__和__next__方法,这个类的对象就是迭代器
生成器:生成器本质就是迭代器,迭代器不一定是生成器
函数中只要有 yield 关键字,这个函数被调用 函数(),它就变成了生成器
生成器表达式,也可以做出生成器 (i+1 for i in [1,2,3])
比如有一堆数据,要放到列表中,但 你没放,而放到了生成器中
for 循环生成器---》可以惰性取值,可以节省内存
在哪里用过生成器?
-读取文件,for循环内部其实就是在用生成器
-我猜测:django中orm 查询一个表所有内容 Book.objects.all()--->内部应该也是一个生成器
-redis hascan 和 hsacn_iter
-类似于这种场景我是可以用到它的:比如我要取数据,但是数据量比较大,不要一次性把把数据取到内存中,而是一点点取值,这样就可以把它做成一个生成器,可以节约内存
17.海象运算符
海象运算符是python3.8以后的一个新语法,是一个特殊的表达式,而不是一个赋值语句. 他是有返回值的.
我个人觉得他主要可以用在2个地方.
1.赋值给中间变量:求职过程中也赋值了新的中间变量.这个中间变量可以在代码块中被继续使用
2.简化列表解析
简单使用:
print(a=3.14)
# 报错 不可以直接赋值,要先赋值再打印
而3.8以后可以直接 print(a:=3.14)
复杂一点:
# 实现判断用户为空提示用户继续输入
value = input('please enter something:') # (1)
while value != "":
print('Nice! Again.')
value = input('please enter something:') # (2)
# 使用海象运算符
while value := input("please enter something:") != "":
print("nice again")
18.什么是继承?
当一个类继承自另一个类,它就被称为子类/派生类,继承自父类的类称为超类或者基类,它会继承这个类的所有类成员(属性和方法),继承能让我们重新复用代码,也能更容易的创建和维护应用,在python支持如下种类的继承:
单继承,多继承,多级继承,分层继承
19.nonlocal和global关键字区别?
相同点:
二者都是改变变量作用范围的关键字
它的区别在于:
1.两者的功能不同: global关键字修饰变量后标识该变量是全局变量,对该变量修改就是对全局修改,而nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级的函数中不存在该局部变量nonlocal位置会发生错误.
2.二者的使用范围不同:global可以在任何地方使用,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global关键字修饰后可以直接使用,而nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错乱.
20.send关键字和yeid区别?
send:调用了__next__ 2. 传参数
yeid:函数遇到yield不会结束,只会'停住'
yield关键字把函数变成了生成器,支持迭代取值了
21.pytest模块
Pytest是基于Python语言的单元测试框架,也是一个命令行的工具,比 unittest 测试框架更灵活。具有以下特点:
1. 入门简单,易上手,官方文档丰富而且使用广泛,有大量的参数例子。
2. unittest有的,它都有,而且支持更为复杂的功能测试
3. 支持大量第三方插件,如:失败重试、控制用例执行顺序等。
4. 基于配置文件可以很简单的集成CI(持续集成)工具中。
我之前做测开的时候用的loadrunner还有jmeter偏向接口,ui以及性能测试,pytest偏向单元测试,也就是我们常说的黑盒测试.
他的断言支持比unittest更符合我们Py开发,可以直接用assert进行断言
他支持参数化,跟unittest需要ddt不同,他可以直接在传参的时候做参数化
还提供了测试的脚手架fixture,在我们进行单元测试前,单元测试后进行一些测试前后的操作,由scope参数控制作用范围
(从大到小):session>module>class>function。
多个测试文件执行的时候调用一次>每个文件调用一次>每个类调用一次>每个函数调用一次
# fixture提供了autouse=True的参数选项,让我们可以不需要装饰,就可以直接自动执行。
而如果在完成接口测试或集成测试时,我们要控制测试用例的执行顺序,可以通过pytest的第三方模块pytest-ordering来实现。
pytest-xdist 可以用在并发执行测试用例,节约时间,属于进程级别的并发.
可以配合allure
Allure 是一款轻量级的开源自动化测试报告生成框架,Java语言开发出来的。它支持绝大部分测试框架,比如 pytest、unittest 等。
比起上面那些丑陋的测试报告生成,Allure是最漂亮的,而且还可以配合pytest与Jenkins实现CI持续集成。pytest+Allure+git+pycharm+Jenkins+gitlab/gitee/github CI持续集成
22.上下文管理器
一个类只要实现了 __enter__() 和 __exit__() 这个两个方法,通过该类创建的对象我们就称之为上下文管理器对象。
23.call trace
"call trace"(调用跟踪)是指跟踪程序在执行过程中函数调用的顺序和细节的技术或工具。当程序执行时,它会记录函数的调用关系、调用顺序以及函数参数和返回值等信息。
Python提供了几种方法来跟踪函数的调用。下面是其中两种常见的方法:
trace 模块:Python标准库中的trace模块提供了跟踪函数调用的功能。你可以使用trace模块来执行源代码文件,并生成一个跟踪报告。该报告将显示函数的调用路径、执行时间和执行次数等信息。
sys.settrace() 函数:Python的sys模块提供了settrace()函数,它允许你自定义一个追踪函数,并将其设置为解释器的追踪函数。当程序执行时,你自定义的追踪函数将被调用,并且你可以在其中获取函数的调用信息。
面试题
1 数据库三大范式是什么
-https://zhuanlan.zhihu.com/p/618012849
-# 第一范式:1NF 是指数据库表的每一列都是不可分割
-每列的值具有原子性,不可再分割。
-每个字段的值都只能是单一值
-# 第二范式(2NF)是在第一范式(1NF)的基础上建立起来得,满足第二范式(2NF)必须先满足第一范式(1NF)
如果表是单主键,那么主键以外的列必须完全依赖于主键,其它列需要跟主键有关系
如果表是复合主键,那么主键以外的列必须完全依赖于主键,不能仅依赖主键的一部分。
-# 第三范式(3NF)是在第二范式的基础上建立起来的,即满足第三范式必须要先满足第二范式
第三范式(3NF)要求:表中的非主键列必须和主键直接相关而不能间接相关;也就是说:非主键列之间不能相关依赖
不存在传递依赖
2 mysql有哪些索引类型,分别有什么作用
-主键索引(聚簇索引)---》主键,表不建立主键,也会有个隐藏字段是主键,是主键索引,mysql是基于主键索引构建的b+树,如果没有主键,如果按主键搜索,速度是最快的---》一定会有主键索引
-辅助索引(普通索引)-->咱们给某个自己加索引,django index=True,通过该字段查询,会提高速度,如果字段 变化小(性别,年龄),不要建立普通索引
-CREATE INDEX index_id ON tb_student(id);
-唯一索引(unique)
-不是为了提高访问速度,而是为了避免数据出现重复
-唯一索引通常使用 UNIQUE 关键字
-CREATE UNIQUE INDEX index_id ON tb_student(id);
-组合索引(联合索引)
-django 中:class Meta:
-CREATE INDEX index_name
ON table_name (column1, column2, column3);
-全文索引-->基本不用---》
全文索引主要用来查找文本中的关键字,只能在 CHAR、VARCHAR 或 TEXT 类型的列上创建。
在 MySQL 中只有 MyISAM 存储引擎支持全文索引。
全文索引允许在索引列中插入重复值和空值。
不过对于大容量的数据表,生成全文索引非常消耗时间和硬盘空间。
创建全文索引使用 FULLTEXT 关键字
3 事务的特性和隔离级别
-https://www.cnblogs.com/liuqingzheng/p/16480047.html
# 事务四大特性(ACID)
原子性(Atomicity):数据库把“要么全做,要么全部做”的这种规则称为原子性
隔离性(Isolation):事务之间相互隔离,不受影响,这与事务的隔离级别密切相关
-事务隔离级别
一致性(Consistency):事务执行前后的状态要一致,可理解为数据一致性
持久性(Durable):事务完成之后,她对数据的修改是永恒的,即时出现故障也能够正常保持
# 隔离级别---》为了保证四个特性的隔离性而有的
-Read uncommitted(读未提交)-ru :一个事物读到了另一个事务未提交的数据
-Read committed(读已提交)-rc:如果设置了这个级别一个事物读不到另一个事务未提交的数据
-写事务提交之前不允许其他事务的读操作
-Repeatable read(可重复读取)-rr:
-在开始读取数据(事务开启)时,不再允许修改操作,这样就可以在同一个事务内两次读到的数据是一样的,因此称为是可重复读隔离级别
-Serializable(串行化):求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行
1 读未提交 一个事务可以读取到其他事务未提交的数据,可能出现脏读、不可重复读和幻读的问题。
2 读已提交 一个事务只能读取到其他事务已提交的数据,避免了脏读的问题,但仍可能出现不可重复读和幻读的问题。
3 可重复读 在一个事务中,多次读取同一数据集的结果都是一致的,即使其他事务对数据进行了修改。避免了脏读和不可重复读的问题,但仍可能出现幻读的问题。
4 串行化 最高级别,事务依次串行执行,避免了脏读、不可重复读和幻读的问题。但会降低并发性能,可能产生较大的锁开销。
4 脏读,不可重复读,幻读 ,mysql5.7以后默认隔离级别是什么?
# 脏读,不可重复读,幻读 ,mysql5.7以后默认隔离级别是什么?
5.6:用的不多了
5.7:用的比较多
8.0:用的比较多
PostgreSQL:安装,插入数据,查询数据,集成到django
-# 脏读,不可重复读,幻读 出现的原因是什么?
-事务隔离级别不同,就会出现不同的问题
-#脏读:脏读指的是当前事务读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了不一定最终存在的数据,这就是脏读
-# 不可重复读:
-解释:不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况
-导致的原因:事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致
-# 幻读:解决了不可重复读的问题了
#幻读错误的理解
幻读是 事务A 执行两次 select 操作得到不同的数据集,即 select 1 得到 10 条记录,select 2 得到 15 条记录。 这其实并不是幻读,既然第一次和第二次读取的不一致,那不还是不可重复读吗,所以这是不可重复读的一种。
#正确的理解
幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。
更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读
# 每个隔离级别解决了什么问题
-Read uncommitted(读未提交)-ru:存在脏读,不可重复读,幻读
-Read committed(读已提交)-rc:解决了脏读,但是存在不可重复读和幻读
-Repeatable read(可重复读取)-rr:解决了脏读,不可重复读问题,存在幻读问题
-Serializable(串行化):解决了脏读,不可重复读和幻读问题,牺牲了效率
# mysql 5.7 默认隔离级别
-REPEATABLE READ(可重复读)-RR-解决了脏读和不可重复读,存在幻读
#Oracle仅支持两种隔离级别(默认基本为RC 存在不可重复读问题):
Read Committed:读已提交
Serializable:串行化
5 什么是qps,tps,并发量,pv,uv
# 什么是qps,tps,并发量,pv,uv
-# qps:Queries Per Second,每秒查询率,一台服务器每秒能够响应的查询次数,每秒的响应请求数
-如何估算自己项目的QPS?
-使用日志估算即可,比如在中间件里记录访问日志,最终统计1s内有多少个访问,qps就是多大;
-# TPS:Transactions Per Second,是每秒处理的事务数,包括一条消息入和一条消息出,加上一次用户数据库访问
TPS 的过程包括:客户端请求服务端、服务端内部处理、服务端返回客户端。
例如,访问一个 Index 页面会请求服务器 3 次,包括一次 html,一次 css,一次 js,那么访问这一个页面就会产生一个T,产生三个Q
-# 并发量
系统同时处理的请求或事务数,可以直接理解为:系统同时处理的请求数量
QPS = 并发量 / 平均响应时间
并发量 = QPS * 平均响应时间
例如当前系统QPS为1w,每个请求的响应时间都是2s,那么并发量就是2w
-# PV
PV(Page View):页面访问量,即页面浏览量或点击量,用户每次刷新即被计算一次。可以统计服务一天的访问日志得到。
-#UV
UV(Unique Visitor):独立访客,统计1天内访问某站点的用户数。可以统计服务一天的访问日志并根据用户的唯一标识去重得到。
-# DAU(日活)
DAU(Daily Active User),日活跃用户数量。常用于反映网站、app、网游的运营情况。
DAU通常统计一日(统计日)之内,登录或使用了某个产品的用户数(去除重复登录的用户),与UV概念相似
-# MAU(月活)
MAU(Month Active User):月活跃用户数量,指网站、app等去重后的月活跃用户数量
回表
当我们增加索引的字段查询时没有加索引,他会去大表中查询
路由匹配成功。封装request,三大认证,执行视图,出现异常,走全局异常捕获,返回给客服端
6什么是接口幂等性问题,如何解决?
# 什么是接口幂等性问题,如何解决?
-幂等:幂等(idempotent、idempotence)是一个数学与计算机学概念
-一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同
-接口幂等性:无论调用多少次,产生的效果是一样的
-get 获取数据天然幂等
-put 修改数据天然幂等
-修改库存(数字加减):不幂等
-delete 删除 天然幂等
-post 新增数据,会出现不幂等的情况,要把它做成幂等性的
# 解决方案:
# token机制
1、下单接口的前一个接口,只要一访问,后端生成一个随机字符串,存到redis中,把随机字符串返回给前端
2、然后调用业务接口请求时,把随机字符串携带过去,一般放在请求头部。
3、服务器判断随机字符串是否存在redis中,存在表示第一次请求,然后redis删除随机字符串,继续执行业务。
4、如果判断随机字符串不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执行
# 乐观锁机制---》更新的场景中
update t_goods set count = count -1 , version = version + 1 where good_id=2 and version = 1
# 唯一主键
这个机制是利用了数据库的主键唯一约束的特性,解决了在insert场景时幂等问题。但主键的要求不是自增的主键,这样就需要业务生成全局唯一的主键
# 唯一ID(unique)
调用接口时,生成一个唯一id,redis将数据保存到集合中(去重),存在即处理过
# 防重表
使用订单号orderNo做为去重表的唯一索引,把唯一索引插入去重表,再进行业务操作,且他们在同一个事务中。这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。
# 前端:
按钮只能点击一次
7什么是gil锁,有什么作用
1 什么是gil锁,有什么作用
# 1 GIL:Global Interpreter Lock又称全局解释器锁。本质就是一个互斥锁,
# 2 保证了cpython进程中得每个线程必须获得这把锁才能执行,不获得不能执行
# 3 使得在同一进程内任何时刻仅有一个线程在执行
# 4 gil锁只针对于cpython解释器----》
-JPython
-PyPy
-CPython
# 为什么要有gil锁?
-python是动态强类型语言,因为有垃圾回收机制,如果同一个进程下有多个线程同时在执行,垃圾回收是垃圾回收线程【同一个进程下变量是共享的】,该线程做垃圾回收时,如果其他线程在运行,就可能会出并发安全的问题【数据安全的问题】,由于当时,只有单核cup【即便开启多线程,同一时刻,也只有一个线程在运行】,作者就强行做了一个GIL锁,保证在一个进程内,同一时刻只有一个线程执行,目的是为了防止垃圾回收线程做垃圾回收时,出现数据紊乱问题,所以加了gil锁
-垃圾回收是垃圾回收线程,它在执行的时候,其他线程是不能执行的,而限于当时的条件,只有单核cpu,所以作者直接做了个GIL锁,保证一个进程内同一时刻只有一个线程在执行
-python使用引用计数为主,标记清楚和隔代回收为辅来进行内存管理。所有python脚本中创建的对象,都会配备一个引用计数,来记录有多少个指针来指向它。当对象的引用技术为0时,会自动释放其所占用的内存。
假设有2个python线程同时引用一个数据(a=100,引用计数为1),
2个线程都会去操作该数据,由于多线程对同一个资源的竞争,实际上引用计数为3,
但是由于没有GIL锁,导致引用计数只增加1(引用计数为2)
这造成的后果是,当第1个线程结束时,会把引用计数减少为1;当第2个线程结束时,会把引用计数减少为0;
当下一个线程再次视图访问这个数据时,就无法找到有效的内存了
8 python的垃圾回收机制是什么样的
2 python的垃圾回收机制是什么样的
-https://www.jianshu.com/p/52ab26890114
-什么是垃圾回收?
-编程语言在运行过程中会定义变量--->申请了内存空间---》后期变量不用了---》这个内存空间应该释放掉
-有些编程语言,这个操作,需要程序员自己做(c)
-像java,python,go这些语言,都自带垃圾回收机制,可以自动回收内存空间,gc机制
-不同语言垃圾回收的方式是不一样的,python是使用如下三种方式做gc,以引用计数为主,标记-清除和分代回收两个算法为辅
-(1)引用计数算法(reference counting);
-每个对象都有一个引用次数的计数属性,如果对象被引用了,那这个数就会 加1,如果引用被删除,引用计数就会 减1,那么当该对象的引用计数为0时,就说明这个对象没有被使用,垃圾回收线程就会把它回收掉,释放内存
-有问题:循环引用问题---》回收不了
-(2) 标记-清除算法(Mark and Sweep);
-解决引用计数无法回收循环引用的问题
-对象之间通过引用连在一起,节点就是各个对象,从一个根对象向下找对象,可以到达的标记为活动对象,不能到达的是非活动对象,而非活动对象就是需要被清除的
-(3) 分代回收算法(Generational garbage collector)
-分代回收是解决垃圾回收效率问题
-算法原理是Python把对象的生命周期分为三代,分别是第0代、第1代、第2代。每一代使用双向链表来标记这些对象。每一代链表都有总数阈值,当达到阈值的时候就会出发GC回收,将需要清除的清除掉,不需要清除的移到下一代。以此类推,第2代中的对象存活周期最长的对象
-python垃圾回收最核心是:引用计数----》标记清除解决引用计数的循环引用问题---》分代回收解决垃圾回收的效率问题
9 解释为什么计算密集型用多进程,io密集型用多线程---》只针对于cpython解释器(其他语言,都开多线程即可,能够利用多核优势)
3 解释为什么计算密集型用多进程,io密集型用多线程---》只针对于cpython解释器(其他语言,都开多线程即可,能够利用多核优势)
-计算是消耗cpu的:代码执行,算术,for都是计算
-io不消耗cpu:打开文件,写入文件,网络操作都是io
-如果遇到io,该线程会释放cpu的执行权限,cpu转而去执行别的线程
-由于python有gil锁,开启多条线程,统一时刻,只能有一条线程在执行
-如果是计算密集型,开了多线程,同一时刻,只有一个线程在执行
-多核cpu,就会浪费多核优势
-如果是计算密集型,我们希望,多个核(cpu),都干活,同一个进程下绕不过gil锁
-所以我们开启多进程,gil锁只能锁住某个进程中得线程,开启多个进程,就能利用多核优势
-io密集型---》只要遇到io,就会释放cpu执行权限
-进程内开了多个io线程,线程多半都在等待,开启多进程是不能提高效率的,反而开启进程很耗费资源,所以使用多线程即可
10 为什么有了gil锁还要互斥锁
# 1 为什么有了gil锁还要互斥锁
-并发和并行
-不能控制什么时候释放gil锁
-gil锁不能锁住共享资源
# 1 GIL 本身就是大的互斥锁
# 2 同一个进程下,资源是共享的----》多条线程可以操作同一个变量
# 3 多个线程操作同一个变量,就会出现数据安全问题
# 4 临界区:指一段代码或一段程序片段,需要在同一时间只能被一个线程执行---》多个线程操作临界区,会出现并发安全问题
# 错误的理解
-有了gil锁,同一时刻,只有一个线程执行,临界区代码,不也只有一个线程在执行吗?只有一个线程在执行,临界区就是安全的,不会出现数据错乱,所以没有必要再加互斥锁了
# 正确的解释:
gil锁释放不是我们控制的,比如在执行临界区中间,释放了,就会数据错乱问题
# 什么时候会释放gil锁?
-1 线程遇到io
-2 时间片轮转,时间片到了,就会自动切换
# 小案例解释
进程中有个变量a=0
临界区:a+=1
线程1要计算: a+=1
1 线程1 拿到gil
2 读取a=0
3 假设时间片到了,释放gil,释放cpu
4 等待下次被调度执行
10 轮到它了,获取gil锁
11 继续往下执行:计算a+1
12 把结果赋值给a ,a=1
13 释放gil锁
线程2要计算: a+=1
5 线程2获得了gil锁
6 读取a=0
7 计算a+1
8 把结果赋值给a ,a=1
9 释放gil锁
# 互斥锁保证数据安全
a=0
线程1要计算: a+=1
1 线程1 拿到gil
# 加锁
2 读取a=0
3 假设时间片到了,释放gil,释放cpu
4 等待下次被调度执行
7 轮到它了,获取gil锁
8 继续往下执行:计算a+1
9 把结果赋值给a ,a=1
10 释放gil锁
线程2要计算: a+=1
5 线程2获得了gil锁
#获取锁,获取不到
6 释放gil锁
11 获得gil锁
#加锁
12 读取a=0
13 计算a+1
14 把结果赋值给a ,a=1
15 释放锁
16 释放gil锁
# gil锁并不锁住临界区,临界区需要我们自己用互斥锁加锁
11 进程,线程和协程
# 进程间通信需要使用管道,消息队列实现
# 线程间通信用什么?
# 协程会出现并发安全的问题吗?
###### 进程线程协程概念
# 进程:是资源分配的最小单位,一个应用程序运行起来,至少有一个进程,进程管理器中就可以看到一个个的进程
# 线程:是cpu调度,执行的最小单位,一个进程下至少有一个线程
# 协程:单线程下的并发,程序层面控制的任务切换
#####如果具体使用
# 开启多进程两种方式
-1 写一个类,继承Process,重写类的run方法---》实例化得到对象,对象.start 开启了进程
-2 通过Process类实例化得到一个对象,传入任务 ,调用对象.start 开启了进程
# 开启线程两种方式
-1 写一个类,继承Thread,重写类的run方法---》实例化得到对象,对象.start 开启了进程
-2 通过Thread类实例化得到一个对象,传入任务 ,调用对象.start 开启了进程
# 开启协程
- 早期之前:借助于第三方gevent,基于greelet写的
- async 和 await 关键字,不借助于第三方,开启协程 asyncio 包
-必须写在一个函数前, async def task()----》这个函数执行的结果是协程函数
-await 只要是io操作的代码,前面必须加 await
async def task1():
a+=1
time.sleep()
a+=10
async def task2():
a+=2
await 遇到io
a+=20
# 什么场景用过
-我一般遇到计算密集型的操作,我会开多进程,io密集型的操作,我一般开多线程
-闲来无事,爬别人数据,喜欢开多线程,爬虫io居多
-程序中,异步做一件事情,也可以开多线程
比如一个视图函数,异步的吧数据写的文件中
异步的发送钉钉通知
异步的发送邮件
-但实际上,在项目中,不需要我们开启进程线程,可以借助于第三方的框架比如celery就可以做异步的操作
而celery的worker,就是进程线程架构
-django框架,是支持并发,我们没有开启多进程和多线程,但是符合uwsgi的web服务器在进入djagno框架之前,开启了进程和线程来执行视图函数
# 拓展
不同进程下,资源是隔离的
进程间通信:IPC 机制
-消息队列
12 什么是鸭子类型
# 鸭子类型是python语言面向对象中的一个概念
#面向对象三大特性
-继承
-封装
-多态
# 多态和多态性?
-多态同一类事物的多种形态
# 现实生活中
-水:冰,水,水蒸气
-动物:人,狗,猫,猪
# 程序中
-Animal类:子类人,子类狗,子类猫
-多态性:不考虑对象具体类型的情况下使用对象
# 程序中
-len()内置函数---》传参数:字符串对象,列表对象,字典对象
-len(字典)
# 为什么能这样用?就是因为多态的存在
字符串,列表,字典----》属于同一类事物---》有长度的这一类事物
# 鸭子类型
-走路像鸭子,说话像鸭子,我们就可以叫它叫鸭子
-解释:鸭子类型是python面向对象中描述接口的一个概念,区分与其他编程语言,
比如java:实现类型,必须显示的继承这个类
而python:实现类型,遵循鸭子类型,不需要显示的继承一个类,只要类中有对应的属性跟方法,我们就称这几个类的对象为同一种类型
13 什么是猴子补丁,有什么用途
# 解释
-Python猴子补丁(Monkey Patch)是一种在运行时动态修改代码的技术。通在不修改源代码的情况下,改变代码的执行方式或增加功能
-Monkey Patching是在 运行时(run time) 动态替换属性(attributes)或方法
-Python的类是可变的(mutable),方法(methods)只是类的属性(attributes);这允许我们在 运行时(run time) 修改其行为。这被称为猴子补丁(Monkey Patching), 它指的是偷偷地更改代码。
# 哪里用过
# 1 位置
import pymysql
pymysql.instal_as_mysqldb() # 动态的替换 原来使用 mysqldb链接数据库的类 conn
# 2 位置
gevent.monkey.patch_all() # 动态的替换 原来阻塞的io方法 ,全都替换成自己写的,不阻塞的 time,socket...
# 3 这种情况 ---》json ---》内置模板---》没关注性能高低
-如果有性能高的json转换模块---》想替换---》如果都换,每个地方都要改,太麻烦了
-借助于猴子补丁---》程序运行一开始,动态的把json替换成ujson
json.loads()
14 什么是反射,python中如何使用反射
# 反射就是通过字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员
# 反射:是程序在运行过程中通过字符串来操作对象的属性和方法
# 可使用反射的地方:
1、反射类中的变量 : 静态属性,类方法,静态方法
2、反射对象中的变量、对象属性、普通方法
3、 反射模块中的变量
4、反射本文件中的变量
# python 提供了一些反射方法
-getattr
-setattr
-hasattr
-delattr
# 例子
def run():
print('走狗')
d=Dog()
setattr(d,'run',run)
d.run()
15 http和https的区别
# https://zhuanlan.zhihu.com/p/561907474
# 名字
# HTTP协议,英文全称是Hyper Text Transfer Protocol,是超文本传输协议
# HTTPS协议,英文全称Hyper Text Transfer Protocol over SecureSocket Layer,超文本传输安全协议
# 默认端口
# HTTPS 和 HTTP 的默认端口不同(443和80)
# 核心区别
# HTTPS在 HTTP与 TCP 之间加入了一个加密/身份验证层,提供了身份验证与加密通讯
HTTP+ SSL / TLS,也就是在 http上又加了一层处理加密信息的模块,比 http安全,可防止数据在传输过程中被窃取、改变,确保数据的完整性
https=http+ssl
# HTTP 由于是明文传输,主要存在三大风险:窃听风险、篡改风险、冒充风险
16 从浏览器输入一个地址,到看到页面信息,经历的过程
1 从浏览器输入一个地址,到看到页面信息,经历的过程
1 在浏览器中输入的是:【地址,不带端口,默认是80端口】域名---》要做域名解析(DNS解析)---》把域名解析成ip地址+端口的形式---dns解析---》(浏览器缓存(一旦之前访问过这个地址,浏览器会自动加缓存,再访问--》直接从缓存中获取--》F5强制刷新或者浏览器有无痕)--》dns解析:先解析本地host文件,上一级递归解析服务 ,13台根dns)--》如果解析不到---》页面就会报错
2 解析完后,向解析出的域名和端口,准备建立TCP连接,可靠链接,tcp处于传输层,进行3次握手,链接简历
3 像解析出的地址发送http的get请求---》http协议又有很多东西,暂时先不说
4 如果后端服务是使用nginx转发,做反向代理服务器,nginx把http请求转发给web框架(django,flask)--》django请求生命周期---》分离项目和混合项目
5 后端服务器以http响应的形式返回给客户端浏览器
6 客户端浏览器把http响应体的内容展示在浏览器上,但是http响应还有:状态码,响应头。。
7 四次挥手断开tcp连接---》这个链接不一定会断开---》http协议版本
# https://blog.csdn.net/m0_52165864/article/details/126313277
17 左连接,右连接,内连接,全连接:MySQL不能直接支持
-数据通常不在同一张表中,这就涉及到连表操作,而表间连接方式有很多
-内连接:把两张表中共有的数据,连接到一起
-左连接:以左表为基准,把左表所有数据都展示,有可能右表没有,用空补齐
-右连接:以右表为基准,把右表所有数据都展示,有可能左表没有,用空补齐
-全连接:以左右两表数据作为基准,左右两表数据都展示,有可能左或表没有,用空补齐
# 笛卡尔积
select * from book,publish where book.publish_id=publish.id;
第一个表
id name age publish_id
1 xx 11 1
2 yy 12 1
第二个表
id name age
1 xx 11
2 yy 12
1 xx 11 1 1 xx 11
1 xx 11 2 yy 12
2 yy 12 1 1 xx 11
2 yy 12 1
# 左右键连接
2 左连接,右连接,内连接,全连接:MySQL不能直接支持
select * from book left join publish on book.publish_id=publish.id;
book.publish_id=99
publish.id没有99
18 union和union all的区别?
-作用:select 出来结果,union,union all都是对结果进行合并,并对结果进行排序,求并集,字段类型和列都要一致才能用
-union 会去除重复的数据,去重和排序操作
-union all 不会去除重复的数据,不会去重和排序
select name,id form user;
id name
1 lqz
2 zs
select name,id form book;
id name
1 lqz
2 西游记
select name,id form user union all select name,id form book;
id name
1 lqz
1 lqz
2 zs
2 西游记
select name,id form user union all select name,id form book;
id name
1 lqz
2 zs
2 西游记
19 一句sql查询慢,如何排查优化
# 1 orm 原生sql
# 2 接口响应速度慢---》定位到是sql问题
-索引优化:分析sql有没有走索引----》EXPLAIN SELECT * FROM orders WHERE name = 123;
在表中添加合适的索引可以显著提升查询效率。可以通过 EXPLAIN 命令来查看查询计划,判断是否使用了索引,如果没有使用索引,就需要考虑添加索引
-避免全表扫描:避免在大表上进行全表扫描,可以通过限制查询条件或者使用分页查询来解决
-使用分页,避免全表扫描
-优化查询语句:
EXPLAIN SELECT * FROM orders WHERE name like 123%;
-优化sql,不要写 前面的模糊查询
-尽量使用主键查询,尽量不模糊匹配
-数据库表结构优化
-大表拆成小表
-做数据库读写分离(https://blog.csdn.net/m0_60725291/article/details/120566113)
-分库分表
20 tcp 三次握手和四次挥手
# tcp协议---》处于osi7层协议的传输层,可靠连接,使用三次握手,四次挥手保证了可靠连接,数据不会丢失
-SYN:SYN=1 表示要建立连接
-ACK:ACK=1 表示我收到了,允许
-seq:随机数,建立连接无论客户端还是服务端要建立连接就要要携带
-ack:回应请求就要加1返回
-FIN:表示断开连接
-三次握手:
-第一次:喂(SYN=1),我是lqz(seq=随机数)
客户端:SYN_SEND状态
服务端:没收到:listen 状态,收到了是:SYN_RCVD状态
-第二次:收到(ACK=1),lqz啊(ack=随机数+1),喂(SYN=1),我是刘亦菲(seq=随机数1)
服务端:SYN_RCVD状态
客户端:没收到服务端返回的第二次:SYN_SEND状态,一旦收到就是established
-第三次:收到(ACK=1),刘亦菲你好(ack=随机数1+1)
客户端:连接建好的状态 established
服务端:收到后,处于established
-大白话:三次握手
第一次:客户端向服务端发送建立连接请求,【携带一个随机数】(SYN=1,seq=随机数)
第二次:服务端回应客户端的建立连接请求(ACK=1,ack=随机数+1),服务端发送建立连接请求(SYN=1,seq=另一个随机数)
第三次:客户端回应服务端的建立连接请求(ACK=1,ack=另一个随机数+1)
# 四次挥手:
第一次:客户端向服务端发起断开连接的请求(FIN=1,seq=随机数)
第二次:服务端收到后,回复这个请求(ACK=1,ack=随机数+1)
第三次:服务端向客户端发起断开连接的请求(FIN=1,seq=另一个随机数,ACK=1,ack=随机数+1)
第四次:客户端收到后,回复这个请求(ACK=1,ack=另一个随机数+1)
#建立和端开链接过程中服务端和客户端的状态
LISTEN:等待从任何远端TCP 和端口的连接请求
SYN_SENT:发送完一个连接请求后等待一个匹配的连接请求。
SYN_RECEIVED:发送连接请求并且接收到匹配的连接请求以后等待连接请求确认。
ESTABLISHED:表示一个打开的连接,接收到的数据可以被投递给用户。连接的数据传输阶段的正常状态。
FIN_WAIT_1:等待远端TCP 的连接终止请求,或者等待之前发送的连接终止请求的确认。
FIN_WAIT_2:等待远端TCP 的连接终止请求。
CLOSE_WAIT:等待本地用户的连接终止请求。
CLOSING:等待远端TCP 的连接终止请求确认。
LAST_ACK:等待先前发送给远端TCP 的连接终止请求的确认(包括它字节的连接终止请求的确认)
TIME_WAIT:等待足够的时间过去以确保远端TCP 接收到它的连接终止请求的确认。
# 洪水攻击 ddos:
同一时间有大量的客户端请求建立连接 会导致服务端一直处于SYN_RCVD状态,服务端接收到了大量的syn请求,处于rcvd状态
21 osi七层协议,哪七层,每层有哪些
# osi七层: 应用层 表示层 会话层 传输层 网络层 数据链路层 物理连接层
# 五层结构(TCP/IP五层协议): 应用层(三个合成一个) 传输层 网络层 数据链路层 物理连接层
# 应用层 表示层 会话层(应用层)
-应用层协议:HTTP,FTP,DNS, SNMP,SMTP,Telnet
# 表示层:https=http+ssl 的加密
# 会话层:负责建立、管理和终止表示层实体之间的会话连接
# 传输层(运输层):
-tcp协议:三次握手四次挥手可靠链接
-udp协议:不可靠传输
-端口:端口协议
# 网络层
-ip地址协议:ip,点分十进制表示
-icmp协议:ICMP(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议簇的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用
# 数据链路层:
-PPP:点对点协议(Point to Point Protocol,PPP)为在点对点连接上传输多协议数据包提供了一个标准方法
-ARP:地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议
-mac地址:以太网协议
-数据帧:电信号的分组方式
# 物理层:
-ISO2110,IEEE802,IEEE802.2
-物理介质
-网线
22 tcp和udp的区别?udp用在哪里了?
-tcp是面向连接的可靠协议
-udp无连接的不可靠协议
-都是处于传输层
-比如:
udp:一些聊天,dns协议用的udp协议
tcp:http mysql,redis客户端服务端通信
23 cgi fastcgi wsgi uwsgi uWSGI 分别是什么?
# CGI:通用网关接口(Common Gateway Interface/CGI),CGI描述了服务器(nginx,apache)和请求处理程序(django,flask,springboot web框架)之间传输数据的一种标准
# 所有bs架构软件都是遵循CGI协议的
# 一句话总结: 一个标准,定义了客户端服务器之间如何传数据
# FastCGI:快速通用网关接口(Fast Common Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本
# FastCGI致力于减少网页服务器与CGI程序之间互动的开销,从而使服务器可以同时处理更多的网页请求
# 常见的fastcgi服务器:Apache,Nginx,Microsoft IIS
# CGI的升级版
# WSGI:Python Web Server Gateway Interface,缩写为WSGI,Python定义的Web服务器和Web应用程序或框架之间的一种通用的接口
#一句话总结: 为Python定义的web服务器和web框架之间的接口标准
#uWSGI:符合wsgi协议的web服务器,用c写的,性能比较高,咱们通常用来部署django,flask
#一句话总结:一个Web Server(web服务器),即一个实现了WSGI协议的服务器,处理发来的请求及返回响应。
# xml:socket 标签
# uwsgi:uWSGI服务器实现的独有的协议,用于定义传输信息的类型,是用于前端服务器与 uwsgi 的通信规范
# 1、一句话总结: uWSGI自有的一个协议
uWSGI:web服务器,等同于wsgiref
uwsgi:uWSGI自有的协议
# 符合WSGI协议的web服务器
wsgiref,werkzeug(一个是符合wsgi协议的web服务器+工具包(封装了一些东西))
uWSGI 用c语言写的,性能比较高
gunicorn:python写的
web服务器到底是什么?服务器中间件
客户端(浏览器,app) 跟 服务端(web框架)之间的东西,服务器中间件
# nginx apache 是一类东西,就是做请求转发,符合fastcgi服务器
# uWSGI,gunicorn 只针对于python的web框架
# tomcat,jboss,weblogic 只针对java的web框架
# php-fpm 针对于php的框架
24 如何自定制上下文管理器
# python的一种编程模式,用于进入和退出之后自动执行一些代码的场景
# 一个对象如果实现了__enter__和___exit__方法,那么这个对象就支持上下文管理协议,即with语句
# 上下文管理协议适用于那些进入和退出之后自动执行一些代码的场景,比如文件、网络连接、数据库连接或使用锁,使用事务的编码场景等
# session--->创建session对象--->最后用完要调用commit,close
# 如何使用
from sqlalchemy.orm import scoped_session
from sqlalchemy import create_engine
from models import Base, User
from sqlalchemy.orm import sessionmaker
from models import User,Boy
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# Session = sessionmaker(bind=engine) # 把引擎传入
session = scoped_session(Session)
class ComonSession:
def __enter__(self):
print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量')
self.session=session
return self.session
def __exit__(self, exc_type, exc_val, exc_tb):
self.session.commit()
self.session.close()
with ComonSession() as a:
res=a.query(Boy).filter_by(name='lqz').all()
print(res)
print('我完事了')
# 我就用上下文管理器管理类 sqlalchemy 的链接对象session
25 Python是值传递还是引用传递
# 严格意义上来说,python既不是值传递,也不是引用传递,python是自己的传递方式,规则是:
如果传递的是不可变类型,在函数中修改,就不会影响原来的变量
如果传递的是可变数据类型,在函数中修改,不会影响原来的变量,因为这个是修改变量的值,而不是重新赋值,重新赋值的话会改变原来原始对象的内存地址
# python一切皆对象---》(你不要说)内部函数是一切皆引用(对象本质就是地址,就是引用)
# 什么是值,什么是引用
-值就是一个变量=具体的值(一块内存空间放着这个变量的值)
-引用是一个变量=内存地址(内存地址指向了值)
-所以python才有了可变和不可变类型
# 什么是值传递 什么是引用传递
-如果是值传递,函数中修改了传递的值,不会影响原来的
-如果是引用传递,函数中修改了传递的引用,就会影响原来的
26 什么是迭代器,生成器,装饰器,闭包函数
迭代:一种不依赖于索引取值的方式,我们不需要关注它的位置,只要能够一个个取值,它就称之为迭代
for循环, next()
可迭代对象:可以迭代的(for,next取值的)python中的对象称之为可迭代对象
在python中可以被for循环或可以变量.next()取值的对象称之为可迭代对象
有 :字典,列表,字符串,元组,集合,文件对象
迭代器:可迭代对象调用__iter__,就得到了迭代器,迭代器有__iter__和__next__方法
自定义迭代器:写个类,类中重写__iter__和__next__方法,这个类的对象就是迭代器
def add():
print(1)
yield
res=add()
生成器:生成器本质就是迭代器,迭代器不一定是生成器
函数中只要有 yield 关键字,这个函数被调用 函数(),它就变成了生成器
生成器表达式,也可以做出生成器 (i+1 for i in [1,2,3])
比如有一堆数据,要放到列表中,但 你没放,而放到了生成器中
for 循环生成器---》可以惰性取值,可以节省内存
在哪里用过生成器?
-读取文件,for循环内部其实就是在用生成器
-我猜测:django中orm 查询一个表所有内容 Book.objects.all()--->内部应该也是一个生成器
-redis hascan 和 hsacn_iter
-类似于这种场景我是可以用到它的:比如我要取数据,但是数据量比较大,不要一次性把把数据取到内存中,而是一点点取值,这样就可以把它做成一个生成器,可以节约内存
装饰器:本身是一个闭包函数,作用是在不改变 被装饰对象源代码和调用方式的基础上,为它加入新功能
flask的路由就是基于装饰器
django的信号也可以用装饰器方式注册
django中局部去除csrf认证
为接口记录访问日志
认证。。
闭包函数:1 定义在函数内部 2 对外部作用域有引用
多了一种给函数传参的方式
典型应用就是装饰器
所有语言都有闭包函数---》所有语言就可以实现装饰器--》但是没有装饰器的语法糖
def auth(a):
def inner():
print(1)
print(a)
# 装饰器
27 django的信号用过吗?如何用,干过什么
django提供的一种通知机制,他是设计模式观察者模式(发布订阅),在发生某种变化的时候,通知某个函数执行
-23种设计模式:https://www.cnblogs.com/liuqingzheng/p/10038958.html
内置信号:如果是内置信号用起来简单,只需要写个函数,跟内置信号绑定,当信号被触发,函数就会执行
-绑定信号,在django中有两种方式
@receiver
connect连接
自定义信号:就比内置信号多了两步:1 定义信号 2 触发信号 信号.send
使用场景:
记录日志(book插入一条数据就记录日志)
用户密码修改,发送邮件通知
一旦生成订单,干xx事
数据库中插入数据,把数据同步到别的位置
# 观察者模式
观察者模式也叫发布-订阅模式,其定义如下:
定义对象间一种一对多的依赖关系,使得当该对象状态改变时,所有依赖于它的对象都会得到通知,并被自动更新
28 什么是深拷贝,什么是浅拷贝,如何使用
# 无论深拷贝还是浅拷贝都是用来 复制对象的
# 如果是浅copy,只会复制一层,如果copy的对象中有可变数据类型,修改可变数据类型还会影响拷贝的对象
# 如果是深copy,完整复制,无论可变或不可变,都是创建出新的来,以后再改原对象,都不会对copy出的对象造成影响
29 并发 并行
# 并发:同一时间段内,执行多个任务的能力,所有操作系统都支持并发,单核cpu也可以并发
# 并行:同一时刻,执行多个任务的能力,并行必须是多cpu支持
# python 开启多线程,即便有多核cpu,它也只能并发,无法并行---》原因是有gil锁
# python 开启多进程+多核cpu,才能并行
# io密集型使用多线程(大部分都是io不消耗cpu),计算密集型使用多进程---》原因是什么
30 同步 异步
# 程序调用的角度
# 同步:同步是一件事一件事的做;只有执行完前一个任务,才会执行下一个任务。同步意味着有序
# 异步:当一个任务已经(开始)执行了,你无需等待该任务执行完成,就可以切换到另外一个任务上。异步意味着无序
# 我们用过的异步:
-借助于其他框架:scrapy,celery
-fastapi:异步web框架
-sanic:异步web框架
31 阻塞 非阻塞
# 程序执行的角度
# 阻塞:程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的
# 非阻塞:程序在等待某操作过程中,自身不被阻塞,可以继续运行干别的事情,则称该程序在该操作上是非阻塞的
# 同步阻塞
# 异步阻塞
# 同步非阻塞
# 异步非阻塞
32 什么是IPC,如何进行进程间通信
# 线程间通信:https://zhuanlan.zhihu.com/p/489305763
-共享内存(共享变量)---》进程间变量是共享的---》lock---》临界区---》数据错乱---》互斥锁---》死锁问题---》递归锁(可重入锁)---》各种各样的锁
-IPC:Inter-Process Communication,进程间通信
-两种情况:
-同一台机器上的两个进程通信
-不同机器上的两个进程进行通信
-如何通信:
-pyton queue可以做进程间通信
-消息队列: redis就可以做消息队列,rabbitmq,kafka
-socket套接字:(展现形式:1 服务和服务之间通过接口调用(http调佣) 2 RPC调用:远程过程调用 3 socket客户端和服务端)
33 正向代理,反向代理
https://www.cnblogs.com/liuqingzheng/p/10521675.html
代理其实就是一个中介,A和B本来可以直连,中间插入一个C,C就是中介
-正向代理:代理的是客户端 VPN 爬虫代理池
-反向代理:代理的是服务端 nginx
#看一下:https://blog.csdn.net/lsc_2019/article/details/124630025
34 什么是粘包
-tcp协议的现象
-因为TCP是流式协议,tcp客户端发送的多个数据包就会像水流一样流向TCP服务端,多个数据包就会'粘'在一起,区分不开是几个数据包,造成了粘包现象
-1 每个包设置结束标志 http协议采用这种 /r/n/r/n
-2 每个包设置固定大小的头,头中包含包的大小
35 http协议详情,http协议版本,http一些请求头
#1 http是超文本传输协议,它是基于Tcp之上的应用层协议(osi七层)
#2 特点:
1 基于请求响应--》服务端不能主动给客户端推送消息---》websocket协议
2 无状态无连接---》不能做会话保持---》才出现了cookie,session,token
3 基于tcp之上的应用层协议---》osi七层
#详情:
-请求协议:
请求首行:请求方式(get,post,delete),请求地址,请求http协议版本号/r/n
请求头:key:value (cookie,useragent,referer,x-forword-for)
请求体:编码方式
-响应协议:
响应首行:http协议版本,响应状态码(1xx,2xx),响应单词描述
响应头:key:value (cookie,响应编码。。。) 跨域问题的响应头(自己设置的响应头)
响应体:html格式:浏览器中看到的 json格式给客户端使用
# 协议版本
-0.9:
HTTP协议的最初版本,功能简陋,仅支持请求方式GET,并且仅能请求访问HTML格式的资源
-1.0:
但是1.0版本的工作方式是每次TCP连接只能发送一个请求(默认短链接),当服务器响应后就会关闭这次连接,下一个请求需要再次建立TCP连接,就是不支持keep-alive
-1.1:
引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。
-2.0:
多路复用:对于 HTTP/1.x,即使开启了长连接,请求的发送也是串行发送的,在带宽足够的情况下,对带宽的利用率不够,HTTP/2.0 采用了多路复用的方式,可以并行发送多个请求,提高对带宽的利用率
-3.0:
HTTP3.0又称为HTTP Over QUIC,其弃用TCP协议,改为使用基于UDP协议的QUIC协议来实现
36 GET请求和POST请求的区别
post更安全(不会作为url的一部分)
post发送的数据更大(get有url长度限制)
post能发送更多的数据类型(get只能发送ASCII字符)
post比get慢
post用于修改和写入数据,get一般用于搜索排序和筛选之类的操作
37 如何实现服务器给客户端发送消息,websocket是什么?用过吗
# 服务端主动向客户端发送消息
# http的无连接,基于请求响应
# 1 轮询:
客户端向服务端无限循环发送http请求,一旦服务端有最新消息,从当次http响应中带回,客户端就能收到变化
# 2 长轮回(web版微信采用此方式)
客户端和服务端保持一个长连接(http),等服务端有消息返回就断开,
如果没有消息,就会hold住,等待一定时间,然后再重新连接,也是个循环的过程
# 3 websocket协议
客户端发起HTTP握手,告诉服务端进行WebSocket协议通讯,并告知WebSocket协议版本。
服务端确认协议版本,升级为WebSocket协议。之后如果有数据需要推送,会主动推送给客户端
-websocket:一种通信协议,区别于http协议,可在单个TCP连接上进行全双工通信。允许服务端主动向客户端推送数据。浏览器和服务器只需要完成一次tcp连接,两者之间就可以建立持久性的连接,并进行双向数据传输
-没有跨域问题
-应用场景:
-只要涉及到服务端主动给客户端发送消息的场景,都可以用
-实时聊天
-服务端发生变化,主动向客户端推送一个通知
-监控服务端的内存使用情况
-djanog中使用channles模块实现
-flask中使用模块
-https://zhuanlan.zhihu.com/p/371500343
38 悲观锁乐观锁使用场景
#无论是悲观锁还是乐观锁,都是人们定义出来的概念,仅仅是一种思想,与语言无关
# 什么是并发控制:
-1 并发控制:当程序中出现并发问题时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的,这种手段就叫做并发控制
-2 没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题
# 悲观锁:
悲观锁:当要对一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发,让并行变成串行
-这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制【Pessimistic Concurrency Control,缩写PCC,又名悲观锁】
- 之所以叫做悲观锁,是因为这是一种对数据的修改持有悲观态度的并发控制方式。总是假设最坏的情况,每次读取数据的时候都默认其他线程会更改数据,因此需要进行加锁操作,当其他线程想要访问数据时,都需要阻塞挂起。
-悲观锁实现方式:
1 传统的关系型数据库(如mysql)使用这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁
2 编程语言中的线程锁,比如python的互斥锁
3 分布式锁:redis实现的分布式锁等
# 乐观锁:
通过程序实现(没有真正的一把锁),不会产生死锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,只在更新的时候会判断一下在此期间别人有没有去更新这个数据,如果更新了,我们就不做修改
-乐观锁实现方案:
1 CAS(Compare And Swap) 即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS 操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B),执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作
CAS会出现ABA问题,比如,你看到桌子上有100块钱,然后你去干其他事了,回来之后看到桌子上依然是100块钱,你就认为这100块没人动过,其实在你走的那段时间,别人已经拿走了100块,后来又还回来了。这就是ABA问题
解决ABA问题:既然有人动了,那我们对数据加一个版本控制字段,只要有人动过这个数据,就把版本进行增加,我们看到桌子上有100块钱版本是1,回来后发现桌子上100没变,但是版本却是2,就立马明白100块有人动过
2 版本号控制:在数据表中增加一个版本号字段,每次更新数据时将版本号加1,同时将当前版本号作为更新条件,如果当前版本号与更新时的版本号一致,则更新成功,否则更新失败
3 时间戳方式:在数据表中增加一个时间戳字段,每次更新数据时将时间戳更新为当前时间戳,同时将当前时间戳作为更新条件,如果当前时间戳与更新时的时间戳一致,则更新成功,否则更新失败
# 悲观锁乐观锁使用场景
并发量:如果并发量不大,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题, 建议乐观锁
响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁。乐观锁并未真正加锁,效率高
冲突频率:如果冲突频率非常高,建议采用悲观锁,保证成功率。冲突频率大,选择乐观锁会需要多次重试才能成功,代价比较大
重试代价:如果重试代价大,建议采用悲观锁。悲观锁依赖数据库锁,效率低。更新失败的概率比较低
读多写少: 乐观锁适用于读多写少的应用场景,这样可以提高并发粒度
#django中使用:下单,秒杀场景
# django中如何开启事务
-全局开启:每个http请求都在一个事务中
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'lqz',
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'lqz',
'PASSWORD': 'lqz123',
#全局开启事务,绑定的是http请求响应整个过程
'ATOMIC_REQUESTS': True,
}
}
-每个视图函数开启
from django.db import transaction
@transaction.atomic
def seckill(request):
-局部开启
from django.db import transaction
def seckill(request):
with transaction.atomic():
pass
return HttpResponse('秒杀成功')
-保存点,回滚保存点Savepoint
-设置回滚点:sid = transaction.savepoint()
-提交回滚点:transaction.savepoint_commit(sid) transaction.commit()
-回滚到回滚点:transaction.savepoint_rollback(sid) transaction.rollback()
- 事务提交后回调函数
transaction.on_commit(send_email)
#django中使用悲观锁
#django中使用乐观锁
object 和 type 联系和区别
1type是实例关系的顶端,所有对象都是它实例出来的,所有类对象的类型都是type;type也是由自己实例化而来的,type的类型也是type。
2、object是继承关系的顶端,所有数据类型的最顶层父类都是它,object无父类。
3、type的父类是object,object的类型是type