文章目录
整体
1、用过什么框架
Django、Flask、fastapi
2、Django、Flask、fastapi区别
1 Django
Django,大而全(重量级的框架),里面自带了很多的功能和模块,,如ORM(对象关系映射)、表单处理、认证授权、管理后台等。Django采用了"约定优于配置"的原则,提供了强大的开箱即用能力,适用于中大型项目、复杂业务逻辑以及需要完善的安全性和扩展性的应用。
2 Flask
特点:小而精(轻量级的框架),自身并没有多少的文件,它也一样可以做django能做的事。开发一些小的项目就可以使用flask。测试一个新的技术,也可以使用flask。
Flask是一个轻量级的微框架,注重简洁性和灵活性。它提供了基本的路由、请求响应处理和模板引擎等核心功能,其他功能通过插件进行扩展。Flask的设计思想是尽可能保持简单和可扩展,用户可以根据自己的需求选择和集成各种第三方库。因此,Flask适合小型项目、简单应用或对框架控制度要求较高的开发者。
3 FastAPI
FastAPI是一个基于异步编程的现代Web框架,注重性能和开发效率。它建立在Starlette框架之上,利用Python 3.7+的新特性(如类型注解和异步/await)提供了高性能的API开发体验。FastAPI通过自动生成API文档和强大的类型检查功能,简化了开发过程,并提供了高度并发处理能力。FastAPI适用于构建高性能的Web服务、微服务和实时应用。
3、设计模式(23种设计模式)
# 单例模式
确保一个类只有一个实例(也就是类的对象),并且提供一个全局的访问点(外部通过这个访问点来访问该类的唯一实例)。通俗的说就是,无论类实例化多少次,都只有一个相同的对象产生,并且可以通过一个具柄去访问这个这个唯一实例。
-应用:
应用框架的配置文件config(如flask,django框架的配置文件)
线程池,数据库连接池的创建
应用程序的日志应用
# 工厂模式
定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。其通用类图如下。其产品类定义产品的公共属性和接口,工厂类定义产品实例化的“方式”。
# 代理模式
定义如下:为某对象提供一个代理,以控制对此对象的访问和控制。代理模式在使用过程中,应尽量对抽象主题类进行代理,而尽量不要对加过修饰和方法的子类代理。
应用场景:flask框架中的request对象就是代理模式,真正的request对象隐藏起来了。
# 装饰器模式
就是我们学习的装饰器,python的装饰器就是在用装饰器模式
# 观察者模式
观察者模式也叫发布-订阅模式,其定义如下:
定义对象间一种一对多的依赖关系,使得当该对象状态改变时,所有依赖于它的对象都会得到通知,并被自动更新。(只要你发生了变化,所有的对象都会被通知,自动产生改变)
使用场景:Django信号机制
# 迭代器模式
它提供一种方法,访问一个容器对象中各个元素,而又不需要暴露对象的内部细节。
就是python中的迭代器跟生成器
Python
OOP(面向对象)
面向对象是python中的一种编程思想
python的编程思想有面向过程、面向对象两种
面向过程是指按照固定的流程解决问题。它的核心是过程二字。意思是先干什么,再干什么,最后干什么的流程,也就是机械化的思维方式。
面向对象编程的核心是对象二字,对象就是一个用来盛放数据与功能的容器,基于该思想编写程序就是创造一个个的容器,其优点是扩展性强,但是它也有缺点,就是提高了编程的复杂度。
在程序中,是先定义类,后产生对象,这里的对象就是容器,是用来存放数据与功能的,而类也是"容器",用在存放同类对象共有的数据与功能。
面向对象有三大特征:封装、继承、多态
封装:封装指的就是把数据与功能都整合到一起,所有类的对象都可以对其调用。针对封装到对象或者类中的属性,我们还可以严格控制对他们的访问,分别是隐藏与开放接口,隐藏属性是采用用下划线开头的方式将属性隐藏起来,设置成私有的,仅仅是一种变形操作,类里定义属性就是为了使用,隐藏并不是目的,外部是可以通过'_类名__属性名'的方式调用,但是设置了隐藏属性就是限制了类外部对数据的直接操作,类内应该提供相应的接口来允许类外部间接的操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格的控制。
继承:实现代码的重用、继承父类、重用父类的属性和方法、减少代码的冗余
多态:多态指的是一类事物有多种形态,多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象、但是需要依赖封装和继承。
在python中推崇鸭子类型:不需要显示的继承一个类,只要类中有对应的属性跟方法,我们就称这几个类的对象为同一种类型。
# 抽象类
综上我们得知,多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象,可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
'''
需要掌握:
1.如何限制抽象类
2.抽象类的特点:只能被继承了,不能被实例化(面试题)
3.抽象类可以限制子类里面必须要有父类定义的方法,
'''
import abc # abstract class 抽象类
'''抽象类的特点:只能被继承了,不能被实例化'''
class Animal(metaclass=abc.ABCMeta): # 这个类就变成了抽象类
@abc.abstractmethod
def speak(self): # 类里面的方法就变成了抽象方法
pass
绑定方法和非绑定方法
类中定义的函数分为两大类:绑定方法和非绑定方法
# 绑定给对象的方法,在类中直接定义的方法就是绑定给对象的方法。
1.定义时函数内部第一个参数写self,
2.函数内部使用对象名.属性名
3.调用时,使用 对象.方法名 来调用
4.特殊之处在于,对象来调用会把对象自己当成第一个参数自动传给方法的第一个形参
5.之后有几个参数传几个参数就可以了
6.类也可以调用,但是括号中第一个参数需要把对象传入
7.通过对象得到类:cls = self.__class__
# 绑定给类的方法:被@classmethod修饰的函数
1.定义函数时加一个内置装饰器:classmethod
2.函数内部第一个参数使用cls
3.调用时,用类.函数名()来调用
4.特殊之处在于,把类名当成第一个参数传给方法的第一个形参
5.之后有几个参数传几个参数就可以了
6.对象也可以调用,不需要传第一个参数,但是设计的是类调用,就不要用对象来调用了
# 非绑定方法,又叫静态方法:加上装饰器@staticmethod后
1.定义函数时加一个内置装饰器:staticmethod
2.这样函数内部就不需要参数了(原来需要一个参数来接收对象名或者类名)
3.谁来调用都可以,函数中缺几个参数就传几个值
3.函数定义初始化__init__中使用self.方法名(),赋值给对象属性
迭代器,生成器,装饰器
# 迭代器
迭代:一种不依赖于索引取值的方式,我们不需要关注它的位置,只要能够一个个取值,它就称之为迭代
可迭代对象:可以迭代的(for,next取值的)python中的对象称之为可迭代对象
-在python中可以被for循环或可以变量.next()取值的对象称之为可迭代对象
-有 :字典,列表,字符串,元组,集合,文件对象
迭代器:可迭代对象调用__iter__,就得到了迭代器,迭代器有__iter__和__next__方法
自定义迭代器:写个类,类中重写__iter__和__next__方法,这个类的对象就是迭代器
'''不依赖与索引取值'''
优点:迭代器对象表示的是一个数据流,可以在需要时才去调用next来获取一个值;但他只能按照顺序一个一个取值。
# 生成器
生成器:生成器本质就是迭代器,迭代器不一定是生成器
函数中只要有 yield 关键字,这个函数被调用 函数(),它就变成了生成器
生成器表达式,也可以做出生成器 (i+1 for i in [1,2,3])
比如有一堆数据,要放到列表中,但 你没放,而放到了生成器中
for 循环生成器--->可以# 惰性取值,可以节省内存
(作用:对象中的元素是按照某种算法推算出来的,在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。)
# 在哪里用过生成器?
-读取文件,for循环内部其实就是在用生成器
-我猜测:django中orm 查询一个表所有内容 Book.objects.all()--->内部应该也是一个生成器
-redis hascan 和 hsacn_iter
-类似于这种场景我是可以用到它的:比如我要取数据,但是数据量比较大,不要一次性把把数据取到内存中,而是一点点取值,这样就可以把它做成一个生成器,可以节约内存
# 装饰器
# 闭包函数:1 定义在函数内部 2 对外部作用域有引用
-多了一种给函数传参的方式
-典型应用就是装饰器
-所有语言都有闭包函数--->所有语言就可以实现装饰器-->但是没有装饰器的语法糖
# 装饰器是闭包函数的一种特殊应用
本身是一个闭包函数,作用是 在不改变被装饰对象"内部代码"和"原有调用方式"的基础之上添加额外的功能。
使用场景:
-flask的路由就是基于装饰器
-django的信号也可以用装饰器方式注册
-django中局部去除csrf认证
-为接口记录访问日志
-插入日志、缓存、权限校验、认证...
拓展:
1. @语法糖的本质是:test=add(test)
2. @后面放个对象,这样就会直接触发类中的__call__的执行。
3. 类作为装饰器: add=Foo(add) # 调用类,产生一个对象,实际执行add()就是执行类中的__call__方法。类作为装饰器是一个类,实现了 __call__() 方法,并且可以接受一个函数作为参数,在 __call__() 方法内部对函数进行装饰。
4. 装饰类的装饰器:它接受一个类作为参数并返回修改后的类。装饰器内部可以对类进行任何需要的操作,例如添加新的属性、方法、修改现有的属性、方法等。
# 2类的对象作为装饰器
class DecoratorObject:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
# 添加额外的逻辑,比如打印日志、计时等
print("Executing before function call")
result = self.func(*args, **kwargs)
print("Executing after function call")
return result
p=DecoratorObject()
@p # my_function=p(my_function)
def my_function():
print("Hello, world!")
my_function()
# 3类作为装饰器
class DecoratorClass:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
# 添加额外的逻辑,比如打印日志、计时等
print("Executing before function call")
result = self.func(*args, **kwargs)
print("Executing after function call")
return result
@DecoratorClass
def my_function():
print("Hello, world!")
my_function()
### 4 装饰类
def decorator(cls):
# 在这里可以对类进行修改或增强
# 可以添加新的属性、方法或修改现有的属性、方法等
# 这里仅作为示例,做了一个简单的装饰:添加一个新的属性和方法
cls.new_attribute = "Added by decorator"
def new_method(self):
print("This is a new method added by decorator")
cls.new_method = new_method
return cls
@decorator
class MyClass:
def __init__(self):
self.attribute = "Original attribute"
def original_method(self):
print("This is the original method")
# 测试装饰后的类
obj = MyClass()
print(obj.attribute) # 输出:Original attribute
print(obj.new_attribute) # 输出:Added by decorator
obj.original_method() # 输出:This is the original method
obj.new_method() # 输出:This is a new method added by decorator
加密方法
加密方式有3种,对称加密算法,非对称加密算法和 摘要算法
摘要算法:用户通过摘要算法对于目标信息生成一段特定长度的值。常见的摘要算法有:md5、sha系列(sha1、sha2、sha224、sha256)。jwt的签名就是使用的摘要算法,这样就可以生成一个固定长度的值。
对称加密:使用的密钥只有一个,发送和接收双方都使用这个秘钥进行加密和解密。
非对称加密:它需要两个密钥,一个称为公开密钥 (public key),即公钥,另一个称为私有密钥 (private key),即私钥。
魔术方法
Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发。
1. __init__方法:初始化方法,调用类的时候自动触发,里面有一个self参数,用来接收对象的。
2. __str__方法,__repr__方法:对象被打印时自动触发,print功能打印的就是它的返回值,我们通常基于方法来定制对象的打印信息,该方法必须返回字符串类型。__repr__方法相同。当__str__和__repr__方法同时存在的时候,__str__的优先级高于__repr__方法
3. __del__方法:在对象被删除时自动触发。当程序全部执行完毕,也会自动调用触发。可以用于回收系统资源。
4.__enter__方法,__exit__方法:上下文管理协议,即with语句中使用。__enter__执行with语句时触发,有返回值则赋值给as声明的变量。with语句中的代码执行完毕后会执行__exit__方法。__exit__中的三个参数分别代表异常类型(exc_type),异常值(exc_val)和追溯信息(exc_tb)。如果__exit__()返回值为True,那么异常会被清空,with后的语句正常执行。 '---> 上下文管理器'
5.__setattr__,__getattr__(),__delattr__:__getattr__只有在使用点调用属性且属性不存在的时候才会触发。__setattr__添加/修改属性会触发,__delattr__删除属性的时候会触发。
6.__setitem__,__getitem__,__delitem__方法: __getitem__对象通过中括号取值的时候会触发它的执行,键存在,则调用。键不存在,则报错。__setitem__对象中括号添加/修改属性的时候触发,键存在,值改变,键不存在,值增加。__delitem__对象中括号删除属性的时候,会触发它的执行。键存在,则删除。键不存在,则报错。
7.__call__方法:对象后面加括号,触发执行。
双下划 new init call
new:是创建出一个新对象,它必须有返回值,返回的值就是我创建的新对象。
如果什么都没有返回,就不会调用init方法。
init:是对于初始对象赋值,往对象中放属性,不需要返回任何内容
call:是对象加括号的时候才会触发,类内部写了self(),self就是对象本身,它就会触发call的执行
我们一般使用init比较多,因为new会调用父类的new,不需要我来创建这个对象了,但是有时候我想要控制我这个类创建出的对象,不是我这个类的的对象,而是别的类的对象,就可以重写__new__方法来控制。new执行完成后才会执行init,对对象初始化。所以只要是进入了init方法,new方法一定执行完成了,空对象已经存在了
# 序列化类(instance=模型类, many=True)
序列化组件中的serizalizer,单个对象和多个对象中就是重写了__new__,如果参数中有many,就会类来调用many_init方法。
def __new__(cls, *args, **kwargs):
# 弹出many对应的值,没有many就返回False
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
return super().__new__(cls, *args, **kwargs)
-如果传了many=True,序列化多条----> 得到的对象不是BookSerializer对象,而是ListSerialzier的对象中套了一个个的BookSerializer对象
-如果传了many=False,序列化单条---> 得到的对象就是BookSerializer的对象
元类
元类就是创建类的类,默认的元类是type
type:所有的类都是 type类的对象
object:所有的类都继承 object
上下文管理器
# python的一种编程模式,用于进入和退出之后自动执行一些代码的场景。比如文件、网络连接、数据库连接或使用锁,使用事务的编码场景等
# 一个对象如果实现了__enter__和___exit__方法,那么这个对象就支持上下文管理协议,即with语句
示例:
# session--->flask中创建session对象--->最后用完要调用commit,close
__enter__中创建一个self.session = session,并且把它返回了(return self.session)。正常调用as后面的变量进行操作(去数据库中查值),在__exit__中
self.session.commit(),self.session.close()。就实现了用上下文管理器管理session对象。
# 如何使用
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
反射
是程序在运行过程中通过字符串来操作对象的属性和方法。
反射方法
- getattr:获取对象属性,当获取的属性不存在的时候,会报错。 如果给了第三个参数,当属性不存在的时候,第三个参数就是默认值,但不会改变对象字典
- setattr:给对象增加属性, 属性名不存在,增加属性。存在,修改。
- hasattr:判断某个函数或者变量是否存在
- delattr:删除模块中某个变量或者函数
反射在Python中的应用场景非常广泛,例如:
- 动态加载模块和类:使用反射可以在运行时动态加载模块和类,以便于程序更加灵活和可扩展。
- 动态修改对象属性和方法:使用反射可以在运行时动态修改对象的属性和方法,以便于程序更加灵活。
- 实现插件系统:使用反射可以实现插件系统,允许程序在运行时动态加载和卸载插件。
- 实现ORM框架:使用反射可以实现ORM框架,允许程序在运行时动态地将Python对象映射到数据库中的表格。
实际操作中的应用:
-
应用一: 视图类的源码中,先判断浏览器的请求是不是8中请求中的一种,然后利用反射,调用类中与请求方式同名的方法名。 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) View的dispatch和APIView的dispatch方法都是这么写的
-
应用二: drf封装了新的request为什么还能使用老的request的属性和方法,通过反射获取老的request中的属性 return getattr(self._request, attr)
-
应用三: 反序列化校验中局部钩子中需要重写validate_ 字段方法,内部就是使用getattr让序列化对象调用局部钩子,在字段的校验通过后直接加括号执行自己的序列化类中的这个validate_ 字段名方法,并把经过校验的字段传入进去。
validate_method = getattr(self, 'validate_' + field.field_name, None)
-
应用四:反序列化的保存 要重写update方法,这个里面就可以直接使用setattr,校验完成的字段,需要写入表中。
def update(self, instance, validated_data): for item in validated_data: # {"name":"jinping","price":55} setattr(instance, item, validated_data[item]) instance.save() return instance
Python是值传递还是引用传递
# 严格意义上来说,python既不是值传递,也不是引用传递,python是自己的传递方式,规则是:
-如果传递的是不可变类型,在函数中修改,就不会影响原来的变量
-如果传递的是可变数据类型,在函数中修改,不会影响原来的变量,修改,而不是重新赋值
# 什么是值,什么是引用
-值就是一个变量=具体的值(一块内存空间放着这个变量的值)
-引用是一个变量=内存地址(内存地址指向了值)
-所有python才有了可变和不可变类型
# 什么是值传递 什么是引用传递
-如果是值传递,函数中修改了传递的值,不会影响原来的
-如果是引用传递,函数中修改了传递的引用,就会影响原来的
什么是深拷贝,什么是浅拷贝,如何使用
# 无论深拷贝还是浅拷贝都是用来 复制对象的
# 浅拷贝
是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
# 深拷贝
不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象
# 赋值
变量直接赋值,b就是直接赋值给a的内存地址了。
- 如果是浅copy,只会复制一层,如果copy的对象中有可变数据类型,修改可变数据类型还会影响拷贝的对象
- 如果是深copy,完整复制,无论可变或不可变,都是创建出新的来,以后再改原对象,都不会对copy出的对象造成影响
综上所述:浅拷贝是只拷贝对象本身,拷贝出的对象仍然引用原来的引用
深拷贝是拷贝对象本身,包括对象的引用的对象也都要复制一份。
GIL锁,有什么作用
- 1 GIL:Global Interpreter Lock又称全局解释器锁。本质就是一个互斥锁
- 2 保证了cpython解释器一个进程中的每个线程必须获得这把锁才能执行,不获得不能执行
- 3 使得在同一进程内任何时刻仅有一个线程在执行
- 4 gil锁只针对于cpython解释器。解释器有JPython、PyPy、CPython、IPython
只需要记住:同一时刻多个线程只能有一个线程在运行,其他线程都处于等待状态
-
为什么要使用Cpython解释器?
Python是一门解释器语言,代码想运行,必须通过解释器执行。python的底层代码是用c语言写的,而Cpython解释器也是用C语言开发的,它的兼容性最好。CPython 的生态系统最为丰富,Python 的第三方库和框架大多数都是以 CPython 为基础开发的。
-
为什么要有gil锁?
python是动态强类型语言,因为有垃圾回收机制,如果同一个进程下有多个线程同时在执行,垃圾回收是垃圾回收线程【同一个进程下变量是共享的】,该线程做垃圾回收时,如果其他线程在运行,就可能会出并发安全的问题【数据安全的问题】,由于当时,只有单核cpu【即便开启多线程,同一时刻,也只有一个线程在运行】,作者就强行做了一个GIL锁,保证在一个进程内,同一时刻只有一个线程执行,目的是为了防止垃圾回收线程做垃圾回收时,出现数据紊乱问题,所以加了gil锁。
python的垃圾回收机制
编程语言在运行过程中会定义变量—>申请了内存空间—>后期变量不用了—>这个内存空间应该释放掉
python是使用如下三种方式做gc,以引用计数为主,标记-清除和分代回收两个算法为辅。
- 引用计数算法:每个对象都有一个引用次数的计数属性,如果对象被引用了,那这个数就会 加1,如果引用被删除,引用计数就会 减1,那么当该对象的引用计数为0时,就说明这个对象没有被使用,垃圾回收线程就会把它回收掉,释放内存 -有问题:循环引用问题—>回收不了
- 标记-清除算法:是为了解决引用计数无法回收循环引用的问题 -对象之间通过引用连在一起,节点就是各个对象,从一个根对象向下找对象,可以到达的标记为活动对象,不能到达的是非活动对象,而非活动对象就是需要被清除的
- 分代回收算法:分代回收是解决垃圾回收效率问题 -算法原理是Python把对象的生命周期分为三代,分别是第0代、第1代、第2代。每一代使用双向链表来标记这些对象。每一代链表都有总数阈值,当达到阈值的时候就会出发GC回收,将需要清除的清除掉,不需要清除的移到下一代。以此类推,第2代中的对象存活周期最长的对象
网络编程
osi七层协议,哪七层,每层有哪些
# osi七层: 应用层 表示层 会话层 传输层 网络层 数据链路层 物理连接层
# 五层结构(TCP/IP五层协议): 应用层(三个合成一个) 传输层 网络层 数据链路层 物理连接层
# 应用层 表示层 会话层(应用层)
-应用层协议:HTTP,FTP,DNS, SNMP,SMTP,Telnet
# 表示层:https=http+ssl 的加密
# 会话层:负责建立、管理和终止表示层实体之间的会话连接
# 传输层(运输层):
-tcp协议:三次握手四次挥手可靠链接
-udp协议:不可靠传输
-端口:端口协议,一个端口代表一个软件。
0-1024,系统软件。1024-8000,常用软件使用。8000之后的,就是程序员使用的了。
# 网络层
-ip地址协议:ip,点分十进制表示
# 数据链路层:IP协议,数据包
-mac地址:以太网协议。
使用物理地址(MAC地址)来标识网络中的设备,通过物理地址来进行网络数据传输。
'以太网协议'的优点是传输速度快、网络设备成本低、安装和使用简单。
-'数据帧':电信号的分组方式。数据帧是由head+data组成
-必须规定电信号多少位一组,每组什么意思,对于数据的分组是由数据链路层来进行的。
-ARP:地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议
# 物理层:
-二进制,比特位(bit)
-物理介质
-网线
-基于电信号特性发送高低电频,高电压对应二进制1,低电压对应二进制0
TCP 三次握手和四次挥手
三次握手
- 第一次:客户端向服务端发起建立连接请求(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)
dos 跟 ddos攻击
DoS攻击是通过单个来源向目标系统发送大量请求或占用资源,而DDoS攻击则利用多个分布在不同地理位置的"僵尸"计算机来同时发起大规模的攻击。
- DoS(拒绝服务)攻击:
- DoS攻击是指攻击者通过向目标系统发送大量的请求或占用其资源,以消耗目标系统的计算能力、内存、带宽等资源,导致正常用户无法访问或使用该系统。
- DoS攻击通常由单个来源发起,攻击者可能使用低级别技术,如通过使用脚本或工具发送大量的请求或进行资源耗尽。
- DDoS(分布式拒绝服务)攻击:
- DDoS攻击是指攻击者利用多个分布在不同地理位置的"僵尸"计算机(也称为“肉鸡”)来同时向目标系统发送大量的流量,从而超过目标系统的处理能力。
- 这些"僵尸"计算机可能被感染的恶意软件控制,形成一个庞大的网络(也称为“僵尸网络”或“Botnet”),攻击者通过控制这个网络来协调并分配攻击流量,使得攻击更加难以防御。
解决方式:
- 流量过滤:使用防火墙、入侵检测系统(IDS)或入侵防御系统(IPS),对进入系统的流量进行实时监测和过滤。这些设备可以识别并丢弃异常或恶意的流量,从而减轻洪水攻击带来的压力。
- 带宽管理:增加网络带宽以承受更大的流量负荷,或者使用流量限制和调整策略来限制每个用户或IP地址的带宽使用。这样可以分散攻击者的资源消耗,降低攻击效果。
- 负载均衡:使用负载均衡技术将流量均匀地分发到多个服务器上,避免单一服务器成为攻击目标。当一个服务器受到攻击时,其他服务器可以继续提供服务,从而保证系统的可用性。
- CDN(内容分发网络):借助CDN提供商的全球分布式网络,可以将流量分发到多个节点上,减轻单个服务器的压力。CDN可以缓存和分发静态内容,同时具备抗DDoS攻击的能力。
- 云防火墙:将系统部署在云上,使用云服务提供商的防火墙功能来过滤恶意流量。云防火墙具有强大的处理能力和自动化防护机制,可有效应对洪水攻击等各种网络威胁。
- 安全策略优化:定期审查和更新系统的安全策略,包括访问控制、认证机制、密码策略等,以减少潜在的漏洞和风险。同时,加强对系统的监控和日志记录,及时发现和响应异常情况。
tcp和udp的区别?
-
TCP协议又称为流式协议
是客户端和服务端建立双向通道,并且有反馈机制,比如,客户端向服务端发送消息时,客户端会先向服务端发送请求链接,服务端同意并且返回消息后,客户端才会开始发送消息。这样就使得TCP的数据通信更加可靠。
-
UDP协议
UDP协议是不需要建立双向链接的,客户端向服务端发送消息,是直接发送,不会在意是否发送成功的,不可靠协议
udp:一些聊天,dns协议用的udp协议
tcp:http mysql,redis客户端服务端通信
什么是粘包
是tcp协议的一种现象。
因为TCP是流式协议,tcp客户端发送的多个数据包就会像水流一样流向TCP服务端,多个数据包就会’粘’在一起,区分不开是几个数据包,造成了粘包现象
解决方式:
- 1 每个包设置结束标志 http协议采用这种 /r/n/r/n
- 2 每个包设置固定大小的头,头中包含包的大小
http和https的区别
- HTTP协议,是超文本传输协议 ,HTTPS协议,超文本传输安全协议
- HTTP是明文传输协议,数据以纯文本形式传输,不经过加密。这就意味着敏感信息(如登录凭证、支付信息等)在传输过程中容易被截获和窃取。----- HTTPS通过使用SSL(Secure Sockets Layer)或TLS(Transport Layer Security)协议,在HTTP的基础上增加了数据加密和身份验证的功能。客户端和服务器之间的通信通过加密进行,提供了更高的安全性,使得数据难以被窃取或篡改。
- HTTP使用默认端口80进行通信。 ------- HTTPS使用默认端口443进行通信。
- HTTP不需要证书,因为没有加密和身份验证的要求。 -------HTTPS需要使用数字证书,用于验证服务器的身份。证书由受信任的第三方机构颁发,用于加密通信和证明服务器身份的真实性。
- HTTPS需要使用数字证书,用于验证服务器的身份。证书由受信任的第三方机构颁发,用于加密通信和证明服务器身份的真实性。-------- HTTPS的配置和维护相对复杂,涉及证书的申请、安装、更新等过程。此外,加密通信的过程会稍微增加服务器的负担,有可能导致一定的性能损失。
总的来说,HTTP适用于对安全性要求较低的场景,如普通网页浏览,因为它的通信速度较快、部署简单;而HTTPS适用于对数据安全要求较高的场景,如在线支付、敏感数据传输,因为它提供了加密和身份验证的保护。随着互联网安全意识的提高,越来越多的网站和应用程序选择使用HTTPS来确保数据的安全传输
从浏览器输入一个地址,到看到页面信息,经历的过程(经典)
- 1 在浏览器中输入的是:【地址,不带端口,默认是80端口】—> 查看浏览器缓存(一旦之前访问过这个地址,浏览器会自动加缓存,再访问–>直接从缓存中获取–>F5强制刷新或者浏览器有无痕) ---->域名—>要做域名解析(DNS解析)—>把域名解析成ip地址+端口的形式 —>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协议版本
并发编程
进程,线程和协程
- 进程:是资源分配的最小单位,一个应用程序运行起来,至少有一个进程,进程管理器中就可以看到一个个的进程
- 线程:是cpu调度,执行的最小单位,一个进程下至少有一个线程
- 协程:单线程下的并发,程序层面控制的任务切换(程序层面耗费的资源,要比操作系统层面耗费的资源小,所以使用协程的速度要比切换进程要快一些)
开启多进程两种方式
- 1 写一个类,继承Process,重写类的run方法—>实例化得到对象,对象.start 开启了进程
- 2 通过Process类实例化得到一个对象,传入任务 ,调用对象.start 开启了进程
开启线程两种方式
- 1 写一个类,继承Thread,重写类的run方法—>实例化得到对象,对象.start 开启了进程
- 2 通过Thread类实例化得到一个对象,传入任务 ,调用对象.start 开启了进程
开启协程
- 1 早期之前:借助于第三方gevent,基于greelet写的
- 2 定义协程函数: async 和 await 关键字,不借助于第三方,开启协程 asyncio 包(必须写在一个函数前, async def task()---->这个函数执行的结果是协程函数 ,await 只要是io操作的代码,前面必须加 await)
使用场景
io密集型任务,一般开启多线程。异步操作,开启多线程(异步把数据写入文件中,异步发送钉钉通知,异步发送邮件)
猴子补丁
- Python猴子补丁(Monkey Patch)是一种在运行时动态修改代码的技术。通在不修改源代码的情况下,改变代码的执行方式或增加功能
- Monkey Patching是在运行时动态替换属性或方法
- Python的类是可变的,方法只是类的属性;这允许我们在运行时修改其行为。这被称为猴子补丁(Monkey Patching), 它指的是偷偷地更改代码。
应用场景
- 很多用到import json, 后来发现ujson性能更高,如果觉得把每个文件的import json改成import ujson as json成本较高, 或者说想测试一下ujson替换是否符合预期, 只需要在入口加上猴子补丁即可
- 使用pymysql时,需要加pymysql.install_as_MySQLdb() 就是在使用猴子补丁,将pymysql模块的功能封装到MySQLdb模块中
解释为什么计算密集型用多进程,io密集型用多线程
计算密集型用多进程,io密集型用多线程 —> 只针对于cpython解释器(其他语言,都开多线程即可,能够利用多核优势)
- 计算是消耗cpu的:代码执行,算术,for都是计算
- io不消耗cpu:读写文件,读写数据库,网络操作都是io
- 如果遇到io,该线程会释放cpu的执行权限,cpu转而去执行别的线程
由于python有gil锁,开启多条线程,同一时刻,只能有一条线程在执行
-
如果是计算密集型,开了多线程,同一时刻,只有一个线程在执行
- 多核cpu,就会浪费多核优势
- 如果是计算密集型,我们希望,多个核(cpu),都干活,同一个进程下绕不过gil锁
- 所以我们开启多进程,gil锁只能锁住某个进程中得线程,开启多个进程,就能利用多核优势
-
io密集型—>只要遇到io,cpu就会释放执行权限
- 进程内开了多个io线程,线程多半都在等待,开启多进程是不能提高效率的,反而开启进程很耗费资源,所以使用多线程即可
为什么有了gil锁还要互斥锁
- GIL锁太大了,不能单独锁住共享资源
- gil锁释放不是我们控制的,比如在执行临界区中间,释放了,就会数据错乱问题
- 多个线程操作同一个变量,就会出现数据安全问题
- 互斥锁能够防止多个线程同时修改共享数据导致的数据冲突问题。
临界区:指一段代码或一段程序片段,需要在同一时间只能被一个线程执行—>多个线程操作临界区,会出现并发安全问题
什么时候会释放gil锁?
- 1 线程遇到io
- 2 时间片轮转,时间片到了,就会自动切换
多个线程的运行,会抢GIL锁,抢到锁的线程(假设该线程是线程1)会执行自己的代码。
遇到阻塞,CPU会切换到下一个线程,此时GIL锁会自动释放。
所以任务中间有阻塞的情况,GIL锁并不能够保证会一次性执行完内部的代码。
所以想要保证代码层面的数据安全,就需要自己加锁。
# 加了互斥锁后
多个线程的运行,会抢GIL锁,抢到锁的线程(假设该线程是线程1)会执行自己的代码。
第一行代码就是上锁,'互斥锁'。之后的代码遇到阻塞,
CPU会切换到下一个线程,此时GIL锁会自动释放,
但是'互斥锁'还在,不会执行其他线程。
只有这个线程执行结束,互斥锁释放,才会执行下一个进程。
所以想要保证代码层面的数据安全,就需要自己加锁
IPC 机制(进程间通信)
IPC(Inter-Process Communication),是指在多个进程之间传递信息或共享资源的一种方式。在单个计算机上同时运行多个进程,它们可能需要相互协作来完成某个任务,或者需要共享某些资源,例如文件、内存、网络资源等。因此,进程间通信是实现多进程编程的重要手段。
两种情况:
-同一台机器上的两个进程通信
-不同机器上的两个进程进行通信
如何通信呢?
pyton 中的queue可以做进程间通信,from multiprocessing import Queue,创建一个q对象,put放值,get取值
消息队列: redis就可以做消息队列,rabbitmq,kafka '---> rabbitmq'
socket套接字:
展现形式: 1 服务和服务之间通过接口调用(http调用,浏览器访问服务器,本质上就是进程间的通信,浏览器的进程,访问服务器的进程)
2 RPC调用:远程过程调用
3 socket客户端和服务端,一个机器启动了客户端,一个机器启动了服务端
如何提高项目的并发量?
https://www.cnblogs.com/zjyao/p/17654359.html
并发 并行 / 同步 异步 / 阻塞 非阻塞
# 并行和并发是处理多任务的两个关键概念。
并行:
-并行是指两者同时执行,比如赛跑,两个人都在不停的往前跑。是在同一时刻,执行多个任务,每个任务都有自己的处理器(cpu),它们之间是独立的,不会相互干扰。单个CPU无法实现并行,必须是多核CPU才能实现并行。
-作用:可以在同一时间内获得更高的运行效率。应用是使用multiprocessing创建多进程,处理计算密集型任务。
并发:
-并发是指资源有限的情况下,两者交替轮流使用资源。是指在一段时间内,执行多个任务,每个任务都只分配到一定的时间片,然后被暂停,等待下一个时间片的到来,再继续执行。单个CPU可以实现 多个CPU肯定也可以。
-作用:并发适用于需要同时处理大量 I/O 密集型任务的场景,例如网络请求、数据库访问、打开文件等。并发可以开启threading (线程)和 asyncio(异步协程)。# IO使用多线程
在 Python 中,需要根据实际需求选择合适的方式。在使用并行和并发的时候,需要注意性能和资源占用等问题,以避免出现过度消耗 CPU 和内存等问题。同时也需要注意线程安全和数据共享等问题,以避免出现死锁和数据竞争等问题。
# 同步和异步是 程序调用的角度
同步:同步是一件事一件事的做;只有执行完前一个任务,才会执行下一个任务。同步意味着有序
异步:当一个任务已经(开始)执行了,你无需等待该任务执行完成,就可以切换到另外一个任务上。异步意味着无序
用过的异步:
-线程中的异步回调机制、上传文件
-借助于其他框架:scrapy,celery
-fastapi:异步web框架
-sanic:异步web框架
# 阻塞 非阻塞 程序执行的角度
阻塞:程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的
非阻塞:程序在等待某操作过程中,自身不被阻塞,可以继续运行干别的事情,则称该程序在该操作上是非阻塞的
-应用
阻塞:查询数据库,读取文件,
非阻塞:高并发服务器,图形化界面,多线程
# 结合
同步阻塞 ---------------> 效率最低
同步非阻塞
异步阻塞
异步非阻塞----------------> 效率最高,使用率高
MySQL
数据库三大范式是什么
关系型数据库中,关于数据表设计的基本原则,规则就称为范式,范式是我们在设计数据库结构过程中需要遵循的规则和指导方法。
- 第一范式:1NF 是指数据库表的每一列都是不可分割
- 每列的值具有原子性,不可再分割。
- 每个字段的值都只能是单一值。
- 第二范式(2NF)是在第一范式(1NF)的基础上建立起来得,满足第二范式(2NF)必须先满足第一范式(1NF)
- 如果表是单主键,那么主键以外的列必须完全依赖于主键,其它列需要跟主键有直接关系。
- 如果表是复合主键(复合主键),那么主键以外的列必须完全依赖于主键,不能仅依赖主键的一部分。
# 解释
表的主键是学生id,有一栏是学校地址,它跟学校有直接关系,但是跟学生id没有直接关联,是间接关联,不符合2NF。
学生id和课程id联合主键,非主键列【分数】完全依赖于主键【学生id和课程id】,学生id和课程id两个值才能决定score的值。 但是【课程名字】只依赖于课程id,与学生id没有依赖关系,它不完全依赖于主键,只依赖于主键的一部分,不符合2NF。
- 第三范式(3NF)是在第二范式的基础上建立起来的,即满足第三范式必须要先满足第二范式
- 第三范式(3NF)要求:表中的非主键列必须和主键直接相关而不能间接相关(第三范式是对第二范式的补充);也就是说:非主键列之间不能相关依赖,不存在传递依赖
### 解释
表中【部门名称】依赖于【部门id】,而【部门id】依赖于【id主键】,从而推出【部门名称】依赖于id(主键)
【部门名称】不直接依赖于主键,而是通过依赖于非主键列而依赖于主键,属于传递依赖,不符合3NF。应该拆开分成两个表,一张人员表,一张部门表。外键关联。# 建立外键的关系就是为了符合第三范式。
在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有⾜够的理由。⽐如性能。事实上我们经常会为了性能⽽妥协数据库的设计。
mysql的存储引擎
- MyISAM(重点) :MySQL5.5及之前的版本默认的存储引擎。存取数据的速度快,但是功能较少,安全性较低。它支持表锁。不支持事务,行锁,外键;
- InnoDB(重点) :MySQL5.6及之后的版本默认的存储引擎 存取速度没有MyISAM快,但是相对MyISAM安全性更高 它支持事务,行锁,外键;
- MEMORY :数据存放在内存中,一旦断电,数据立马丢失,重启服务端数据就没了,不能长期保存数据,仅用于临时表数据存取
- BlackHole :任何写入进去的数据都会立刻丢失,使用很少
了解不同存储引擎底层文件个数:
1. MyISAM引擎 产生3个文件
.frm >>> 表结构
.MYD >>> 存数据 d-> data
.MYI >>> 存索引 >>> 看成是目录 i-> index
2. InnoDB 产生2个文件
.frm >>> 表结构
.ibd >>> 表结构+数据
3. MEMORY 产生1个文件
.frm >>> 表结构
数据是保存在内存中,所以磁盘中没有。
4. BlackHole 产生1个文件
.frm >>> 表结构
数据给了我之后,下一秒就删除了
mysql有哪些索引类型,分别有什么作用
-
主键索引(聚簇索引)
主键,即便表不建立主键,也会有个隐藏字段是主键,是主键索引,mysql是基于 主键索引构建的b+树,如果按主键搜索,速度是最快的—>一定会有主键索引
-
辅助索引(普通索引)
咱们给某个自己加索引,django index=True,它没有任何限制,唯一的任务就 是加快系统对数据的访问速度。通过该字段查询,会提高速度,如果字段 变化小(性别, 年龄),不要建立普通索引,(性别只有两种状态,建立普通索引,没有任何用处)
-
唯一索引(unique)
不是为了提高访问速度,而是为了避免数据出现重复
-
组合索引(联合索引)
由多个列构成的索引。sql语句中使用index关键字,后面选择多个列就行。django中要在Meta中,定义一个index_together = [“store”, “sensor”]
-
全文索引,基本不用
全文索引主要用来查找文本中的关键字,只能在 CHAR、VARCHAR 或 TEXT 类型的列上创建。创建全文索引使用 FULLTEXT 关键字。
事务的隔离级别
事务是⼀个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使 数据库从⼀种⼀致性状态变到另⼀种⼀致性状态。事务是逻辑上的⼀组操作,要么都执行,要么都不执行。
事务的四大特性
- 原子性(Atomicity):事务是最⼩的执⾏单位,不允许分割。数据库把’要么全做,要么全不做’ 的这种规则称为原子性
- 一致性(Consistency):执⾏事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结果是相同的,可以并发着执行多个事务
- 隔离性(Isolation):并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发事务之 间数据库是独⽴的。事务之间相互隔离,不受影响,这与事务的隔离级别密切相关
- 持久性(Durable):⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发⽣故 障也不应该对其有任何影响。
隔离级别
- Read uncommitted(读未提交)-ru :一个事物读到了另一个事务未提交的数据,最低的隔离级别。— 存在脏读,不可重复读,幻读
- Read committed(读已提交)-rc:如果设置了这个级别一个事物读不到另一个事务未提交的数据。 写事务提交之前不允许其他事务的读操作(加锁来保证) — 解决了脏读,但是存在不可重复读和幻读
- Repeatable read(可重复读取)-rr:在开始读取数据(事务开启)时,不再允许修改操作,这样 就可以在同一个事务内两次读到的数据是一样的,因此称为是可重复读隔离级别 —解决了脏读,不可重复读问题,存在幻读问题
- Serializable(串行化):求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行,效率比较低。 — 解决了脏读,不可重复读和幻读问题,牺牲了效率,效率就太低了
Mysql 默认采⽤的 REPEATABLE_READ(可重复读)隔离级别。
Oracle 默认采⽤的 READ_COMMITTED(读取已提交)隔离级别。
脏读、不可重复读、幻读
-
脏读指的是读当前事务到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据(如果没有回滚,就是存在的数据)。读到了’并不一定 最终存在’的数据,这就是脏读。
-
不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一 批数据出现不一致的情况 ----导致的原因:事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致
-
幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。(第一次查询的时候,没有id为3的,想要插入一条id为3的 数据,结果插入不了,报错了,说已经存在了# 事务2已经插入了id为3的数据)
左连接,右连接,内连接,全连接
数据通常不在同一张表中,这就涉及到连表操作,而表间连接方式有很多
- 内连接(inner join):把两张表中共有的数据,连接到一起
- 左连接(left join):以左表为基准,把左表所有数据都展示,有可能右表没有,用空补齐
- 右连接(right join):以右表为基准,把右表所有数据都展示,有可能左表没有,用空补齐
- 全连接(union):以左右两表数据作为基准,左右两表数据都展示,有可能左或表没有,用空补齐。mysql是不支持全连接的
union和union all的区别?
select 出来结果,union,union all都是对结果进行合并,并对结果进行排序,求并集,字段类型和列都要一致才能用。
- union 会去除重复的数据,去重和排序操作
- union all 不会去除重复的数据,不会去重和排序
SQL执行计划
SQL执行计划(SQL Execution Plan)是数据库系统中用于执行和优化SQL查询语句的重要概念。它描述了数据库系统在执行一个SQL查询时所采取的具体操作步骤以及执行顺序。
SQL执行计划由数据库查询优化器生成,通过优化器对SQL查询语句进行解析、分析和优化,确定最佳的查询执行计划。执行计划通常以树状结构的形式展示,每个节点代表一个具体的操作,如表扫描、索引查找、连接操作等。
SQL执行计划提供了以下信息:
- 执行顺序:SQL查询涉及多个表或数据源时,执行计划会显示操作的执行顺序,决定了各个操作的依赖关系和执行方式。
- 访问方法:执行计划显示了每个操作使用的具体访问方法,如全表扫描、索引扫描、索引覆盖扫描等。
- 操作类型:执行计划指明了每个操作的类型,比如连接操作(Join)、排序(Sort)、聚合(Aggregate)等,这有助于理解查询的逻辑和处理方式。
- 成本估算:执行计划还提供了每个操作的成本估算信息,用于评估查询性能,并帮助优化器选择最优的执行计划。
SQL优化
1、explain 输出执行计划
在select语句前加上explain就可以了(MySQL 5.6开始,不仅仅支持select )能够简单分析sql的执行情况,是否走索引等。
2、少用select *,指明字段
SELECT语句务必指明字段名称,select * 增加很多不必要的消耗(CPU、IO、内存、网络带宽);
3、使用EXISTS替代IN:
在某些情况下,使用EXISTS判断条件是否成立比使用IN语句更高效,因为EXISTS只需要查找一次记录,而IN则需要查找多次。
4、使用UNION ALL替代UNION:
如果需要合并多个查询结果,应该尽量使用UNION ALL而不是UNION,因为UNION会对结果进行排序和去重,而UNION ALL不需要进行排序和去重,因此更高效。
5、避免使用LIKE '%xxx%':
尽量避免在搜索中使用通配符,特别是在LIKE语句中使用'%xxx%'这样的模糊查询,因为这种查询会导致全表扫描,降低查询效率。
6、将多次插入换成批量Insert插入
减少数据库访问次数可以提高SQL语句的执行效率。例如,可以使用批量更新和批量插入,减少单条数据的操作。此外,可以使用缓存来避免频繁的数据库查询。
7、善用limit 1 分页查找
这是为了使explain中type列达到const类型。当只需要一条数据的时候,使用limit 1,如果加上limit1,查找到就不用继续往后找了。
8、 order by字段建索引
避免全表扫描,首先应考虑在 where 及 order by涉及的列上建立索引,如果排序字段没有用到索引,就尽量少排序,可以在程序中排序。
9、避免使用 or
原因:
1)索引无法使用:
如果使用 OR 运算符连接多个条件,则可能会阻止优化器使用索引进行查询。例如,如果使用 OR 运算符连接两个列的条件,则优化器无法使用这两个列的索引。
2)扫描整个表:
如果使用 OR 运算符连接多个条件,则可能需要扫描整个表来找到满足条件的行,这可能会导致查询速度变慢。
3)查询计算成本高:
某些查询可能需要进行非常复杂的计算,这可能会导致查询速度变慢。
因此,为了提高查询效率,通常建议尽可能避免使用 OR 运算符。如果需要使用 OR 运算符,请确保使用的条件可以使用索引,并尽可能使用简单的条件。另外,可以考虑使用 UNION 或 UNION ALL 运算符替代 OR 运算符,以提高查询效率。
10、in 和 not in 要慎用
SQL语句中IN包含的值不应过多,MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。
对于连续的数值,能用 between 就不要用 in 了
再或者使用连接来替换。
11、count(*)推荐使用
count(*)、count(主键 id) 和 count(1) 都表示返回满足条件的结果集的总行数;而 count(字段),则表示返回满足条件的数据行里面,参数"字段"不为 NULL 的总个数。
count(*) 是例外,并不会把全部字段取出来,而是专门做了优化,不取值。count(*) 肯定不是 null,按行累加。
按照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(*),推荐使用count(*)
12、选择重复值较低的字段建索引
一句sql查询慢,如何排查优化
接口响应速度慢--->定位到是sql问题
1 索引优化:
分析sql有没有走索引---->EXPLAIN SELECT * FROM orders WHERE name = 123;
在表中添加合适的索引可以显著提升查询效率。可以通过 EXPLAIN 命令来查看查询计划,
判断是否使用了索引,如果没有使用索引,就需要考虑添加索引
2 避免全表扫描:避免在大表上进行全表扫描,可以通过限制查询条件或者使用分页查询来解决
-使用分页,避免全表搜索,扫码
3 优化查询语句:
EXPLAIN SELECT * FROM orders WHERE name like 123%;
-优化sql,不要写 前面的模糊查询
-尽量使用主键查询,尽量不模糊匹配
4 数据库表结构优化
-大表拆成小表
5 做数据库读写分离,做主从搭建,主库负责写,从库负责读
6 分库分表:1垂直分表(数据比较杂乱的时候),2 水平分表(数据量比较大的时候)
悲观锁和乐观锁
什么是并发控制:
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 时间戳方式:在数据表中增加一个时间戳字段,每次更新数据时将时间戳更新为当前时间戳,同时将当前时间戳作为更新条件,如果当前时间戳与更新时的时间戳一致,则更新成功,否则更新失败
悲观锁乐观锁使用场景
并发量:如果并发量不大,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题, 建议乐观锁
响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁。乐观锁并未真正加锁,效率高
冲突频率:如果冲突频率非常高,建议采用悲观锁,保证成功率。冲突频率大,选择乐观锁会需要多次重试才能成功,代价比较大
重试代价:如果重试代价大,建议采用悲观锁。悲观锁依赖数据库锁,效率低。更新失败的概率比较低
读多写少: 乐观锁适用于读多写少的应用场景,这样可以提高并发粒度
mysql主从搭建
原理
- 步骤一:主库db的更新事件(update、insert、delete)被写到binlog(二进制日志)
- 步骤二:从库发起连接,连接到主库
- 步骤三:此时主库创建一个binlog dump thread线程,把binlog的内容发送到从库
- 步骤四:从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log(中继日志).
- 步骤五:还会创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db
配置文件加
server-id=100 # 主库跟从库都需要写一个server-id号,且需要不同
log-bin=mysql-bin # 主库的binlog二进制日志
server-id=101 # 从库中的server-id号
log-bin=mysql-slave-bin # 从库的binlog二进制日志
relay_log=edu-mysql-relay-bin # 中继日志
优化查询过程中的数据访问
- 访问数据太多导致查询性能下降
- 确定应⽤程序是否在检索⼤量超过需要的数据,可能是太多⾏或列
- 确认MySQL服务器是否在分析⼤量不必要的数据⾏
- 避免犯如下SQL语句错误
- 查询不需要的数据。解决办法:使⽤limit解决
- 多表关联返回全部列。解决办法:指定列名
- 总是返回全部列。解决办法:避免使⽤SELECT *
- 重复查询相同的数据。解决办法:可以缓存数据,下次直接读取缓存
- 是否在扫描额外的记录。解决办法:
- 使⽤explain进⾏分析,如果发现查询需要扫描⼤量的数据,但只返回少数的⾏,可以通过如下技巧去优化:
- 使⽤索引覆盖扫描,把所有的列都放到索引中,这样存储引擎不需要回表获取对应⾏就可以返回结果。
- 改变数据库和表的结构,修改数据表范式
- 重写SQL语句,让优化器可以以更优的⽅式执⾏查询。
redis
redis为什么速度快?
1.redis是纯内存操作。2.使用了IO多路复用的网络模型,3.数据操作是单线程,避免了线程间切换,而且没有锁,也不会数据错乱
redis慢查询
配置一个时间,如果查询时间超过了我们设置的时间,我们就认为这是一个慢查询。
配置的慢查询,只在命令执行阶段
它有两个配置项,一个是日志的时间阈值(slowlog-log-slower-than),正数是超过时间才会记录这条命令。负数是禁用慢查询, 0是记录所有命令。一个是日志长度(slowlog-max-len) 最小值为零,默认 128,当记录新命令并且当前慢日志已达到最大长度时,最旧的一条记录将被删除。
用config set来设置。查看是用slowlog 查看,有slowlog get [n]查询第几条。slowlog len,获取慢查询队列长度。slowlog reset,清空慢查询队列。
示例
# 设置记录所有命令
CONFIG SET slowlog-log-slower-than 0
# 最多记录100条
config set slowlog-max-len 100
# 持久化到本地配置文件
config rewrite
查看慢查询队列
slowlog get [n]
slowlog len,获取慢查询队列长度
slowlog reset,清空慢查询队列
pipline与事务
python实现pipline:
先创建一个Redis对象,Redis对象点pipline(transaction=True)赋值给一个对象pipe,
开启事务pipe.multi(),往管道中放数据(set值),pipe.execute()执行事务。
原生redis操作
multi # 开启事务
set name lqz
set age 18
exec
发布订阅
发布者发布了消息,所有的订阅者都可以收到,就是生产者消费者模型(后订阅了,无法获取历史消息)
publish lqz "hello world" # 发布消息
subscribe lqz # 订阅消息
bitmap位图
位图是一种特殊的散列表。申请一个大小为1亿、布尔类型(true或者false)的数组。需要保存时,将对应的数组值设置成true。
set hello big #放入key为hello 值为big的字符串
getbit hello 0 #取位图的第0个位置,返回0
getbit hello 1 #取位图的第1个位置,返回1 如上图
# 设置比特位
etbit hello 7 1 #把hello的第7个bit位置设为1 这样,big就变成了cig
# 获取指定字节范围内,有几个1
bitcount key 0 3 # 数字指的是字节
作用:可以用于独立用户统计,或者较大数据的统计,它可以统计总的数据,但是不能取值
HyperLogLog算法
HyperLogLog只能统计基数的大小(也就是数据集的大小,集合的个数)。它是有错误率的。
pfadd uuids "uuid1" "uuid2" "uuid3" "uuid4" # 增加值
pfcount uuids # 统计个数
redis持久化
redis的所有数据都保存在内存中,持久化是指把数据保存到硬盘上。有三种方案:rdb方案、aof方案、混合持久化
1 rdb方案:在某一刻完成备份
按照⼀定的时间将内存的数据以快照的形式保存到硬盘中,对应产⽣的数据⽂件为dump.rdb。通过配置⽂件中的save参数来定义快照的周期
- 方式一:save(同步)。
- 方式二:bgsave(异步)。
- 方式三:配置文件中:save 秒数 变化条数
在这个时间段内产生了这么多条,就持久化到硬盘中。
2 aof方案:
是将Redis执⾏的每次写命令记录到单独的⽇志⽂件中,当重启Redis会重新将持久化的⽇志中⽂件恢复数据。在配置文件中设置
appendonly yes # 开启aof持久化
appendfilename "appendonly.aof" # 持久化的保存文件
appendfsync everysec # 每秒钟强制写入磁盘一次。
dir /root/lqz/redis/data # 配置持久化文件存放位置
有3中刷写模式:
appendfsync always # 每次收到写命令就立即强制写入磁盘,是最有保证的完全的持久化,但速度也是最慢的,一般不推荐使用。
appendfsync everysec # 每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,是受推荐的方式。
appendfsync no # 完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不被推荐。
3 混合持久化:rdb+aof
必须先开启AOF,再加入一条配置 aof-use-rdb-preamble yes
主从复制
1.1 主从复制:一主一从,一主多从
做读写分离,提高并发量
做数据副本
扩展数据性能
1.2 特点
一个master可以有多个slave
一个slave只能有一个master
数据流向是单向的,从master到slave
主从原理
- 副本库(从库)通过 slaveof 127.0.0.1 6379 命令,连接主库,并发送SYNC给主库
- 主库收到SYNC,会立即触发BGSAVE,后台保存RDB,发送给副本库
- 副本库接收后会应用RDB快照
- 主库会陆续将中间产生的新的操作,保存并发送给副本库
- 到此,我们主复制集就正常工作了
- 再此以后,主库只要发生新的操作,都会以命令传播的形式自动发送给副本库.
- 所有复制相关信息,从info信息中都可以查到。即使重启任何节点,他的主从关系依然都在。
- 如果发生主从关系断开时,从库数据没有任何损坏,在下次重连之后,从库发送PSYNC给主库
- 主库只会将从库缺失部分的数据同步给从库应用,达到快速恢复主从的目的
(一定会有标识符,记录是从哪里断开的)
主从搭建
3.1 方式一
启动一个主库,在从库中执行slaveof ip port(异步操作),就开启了主从关系
断开主从:在从库上:slaveof no one
3.2 配置文件搭建
从库配置文件中加
slaveof ip 6379
slave-read-only yes
哨兵
为了更好的服务,实现高可用,我们可以使用哨兵,当主节点发生故障时,可以让其中一个slave变成master,我们再手动查看故障和解决故障。
哨兵原理:sentinel 翻译过来的
- 1 多个sentinel发现并确认master有问题
- 2 选举触一个sentinel作为领导(raft算法)
- 3 选取一个slave作为新的master
- 4 通知其余slave成为新的master的slave
- 5 通知客户端主从变化
- 6 等待老的master复活成为新master的slave
集群
加集群解决并发量比较高的问题。
https://www.cnblogs.com/zjyao/p/17664789.html
缓存优化
···python
缓存的数据满了,应该删除那一条?
LRU -Least Recently Used 没有被使用时间最长的
LFU -Least Frequenty User 一定时间段内使用次数最少的
FIFO -First In First Out 先进先出,最早放的先删除
缓存穿透、缓存击穿、缓存雪崩
2.1 缓存穿透
#描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为"-1"的数据
或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
#解决方案:
1 接口层增加校验,如用户鉴权校验(认证类中增加用户权限的校验),
id做基础校验,id<=0的直接拦截;
2 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,
缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。
这样可以防止攻击用户反复用同一个id暴力攻击
3 通过布隆过滤器实现【这个具体流程要写出来】
2.2 缓存击穿
#描述:
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,
同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
#解决方案:
设置热点数据永远不过期。
2.3 缓存雪崩
#描述:
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。
和缓存击穿不同的是,缓存击穿 指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查
缓存而查不到,从而查数据库。
# 解决方案:
1 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
2 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
3 设置热点数据永远不过期。
django,drf
APIview的执行流程
用户从浏览器中发送请求后,进入路由组件,到URL对应的视图类中。进入as_view方法中,先根据请求方式的不同,调用类中同名的方法。 1 去除了csrf的校验。2 包装了新的request,self._request()就是老的request对象。3 执行了三大认证(认证、频率、权限)。4 期间出了错误就会被全局异常捕获。
Cookie、Session和Token
Cookie、Session和Token是常用的身份验证和状态管理机制。
- HTTP协议是无状态的,每次的请求都是一个全新的请求,它不会保存数据,但是我们需要浏览器来保存一定的数据,就产生了cookie。
Cookie是key-value结构,随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器,服务端接收浏览器传过来的用户名和密码再进行验证。 - Cookie以文本的形式保存在本地,自身安全性较差,就产生了session。session是把数据保存在服务端的,在用户第一次访问时,服务器创建一个唯一的会话标识符(Session ID)并将其发送给浏览器。浏览器将Session ID存储在Cookie中,第二次请求的时候作为URL参数传递给服务器。服务端再用户表中查询用户信息是否存在。
- session把数据保存在数据表里了,但是,当数据量特别大的时候,查询效率肯定会降低,为了解决session对服务器的压力,就可以利用token。
- token是服务端生成的加密字符串,用户登录之后把字符串发送给客户端。客户端将Token保存在本地(例如LocalStorage或SessionStorage)。每次请求时,客户端将Token作为请求的一部分发送给服务器进行验证。服务器使用密钥来验证和解析Token,并根据Token中的信息判断用户身份和权限。 ‘—> JWT签发认证’
jwt原理
JWT就是一段字符串,由三段信息构成的,第一部分我们称它为头部(header),第二部分我们称其为载荷(payload),第三部分是签证(signature).
- 头部有:声明加密的算法 通常直接使用 HMAC SHA256、公司信息
- 荷载:存放有效信息的地方。用户名,用户id,exp: jwt的过期时间
- 签证:这个部分需要base64加密后的 header 和base64加密后的 payload 使用.连接组成的字符串,然后通过header中声明的加密方式(加盐secret),组合加密
然后就构成了jwt的第三部分。
开发流程
- 1)根据提交的用户、客户端信息,产生token,将token直接返回给客户端,存储客户端的cookies中(签发)
- 2)从请求中拿到携带的token,利用签发token的逆运算代码解析token,得到user对象,存储到request.user中,提供给之后的服务器代码使用(校验)
高级别安全管理(RBAC)
常见的权限模型有:ACL、RBAC
- ACL(访问控制列表),是针对互联网用户的产品,是用户直接跟权限做绑定。
- RBAC(Role-Based Access Control)基于角色的访问控制:针对于公司内部项目
权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
Django使用信号
django自带一套信号机制来帮助我们在框架的不同位置之间传递信息。
简单的说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将通知或信号(siganls)发送给一组接收者(receivers)。有内置信号(执行构造方法前,对象保存前,请求来前)和自定义信号两种方式
- 内置信号:signals.内置信号.connect(触发的函数)
- 自定义信号: 1,定义一个自定义信号,信号名=django.dispatch.Signal(providing_args=[“参数”]), 2,写个函数, 3,函数跟自己定义信号绑定,信号名.connect(函数名), 4,触发信号,先导入信号名,信号名.send()
Django开启事务三种方式
- 1.全局开启事务:数据库配置中加个设置
DATABASES = {
'default': {
#全局开启事务,绑定的是http请求响应整个过程
'ATOMIC_REQUESTS': True,
}
}
- 2.一个视图函数在一个事物中:在视图函数中加个装饰器 @transaction.atomic
- 3.局部使用事务
with transaction.atomic():
pass # 都在一个事物中
事务操作
transaction.atomic() # 开启事务
transaction.commit() # 提交事务
transaction.rollback() # 回滚事务
Python执行原生SQL
https://www.cnblogs.com/zjyao/p/17299003.html#python操作mysql
需要借助于第三方模块,pymysql模块。
- 1.获取连接对象,conn=pymysql.connect(ip,port,用户,密码,库,字符编码),
- 2.获取游标对象cursor = conn.cursor(),
- 3.cursor.execute(‘sql语句’),这个值是影响的行数,
- 4.获取执行结果:cursor.fetchall(),或者 4.提交:conn.commit()
前端,View
BOM和DOM
BOM(Browser Object Model) ----> 浏览器对象模型 ----> 就是用js操作浏览器
DOM(Document Object Model) ----> 文档对象模型 ----> 就是用js操作html标签样式
BOM对象
-window对象
window.open() - 打开新窗口
window.close() - 关闭当前窗口
-navigator对象:浏览器对象,包含浏览器的相关信息
navigator.userAgent # 客户端绝大部分信息 # 需要掌握,它代表了是不是浏览器(爬虫)
navigator.platform # 浏览器运行所在的操作系统
-screen对象:屏幕对象
screen.availWidth # 可用的屏幕宽度
screen.availHeight # 可用的屏幕高度
-history对象:浏览器的历史
history.forward() # 前进一页
history.back() # 后退一页
-location对象:当前页面对象
location.href # 获取当前页面URL
location.href="URL" # 跳转到指定页面
location.reload() # 重新加载页面, 刷新页面
# DOM(文档对象模型)
查找标签
操作css样式
自动触发的事件
生命周期钩子
钩子函数 | 描述 |
---|---|
beforeCreate | 创建Vue实例之前调用 |
created | 创建Vue实例成功后调用(可以在此处发送异步请求后端数据) |
beforeMount | 渲染DOM之前调用 |
mounted | 渲染DOM之后调用 |
beforeUpdate | 重新渲染之前调用(数据更新等操作时,控制DOM重新渲染) |
updated | 重新渲染完成之后调用 |
beforeDestroy | 销毁之前调用 (可以在此做资源清理工作) |
destroyed | 销毁之后调用 |
view3 |
beforeCreate===>setup()
created=======>setup()
beforeMount ===>onBeforeMount
mounted=======>onMounted
beforeUpdate===>onBeforeUpdate
updated =======>onUpdated
beforeUnmount ==>onBeforeUnmount
unmounted =====>onUnmounted
vuex通信
状态管理器,在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
vue-cli
Vue-CLI(Vue Command Line Interface)是一个用于快速搭建 Vue.js 项目的脚手架工具。它是由 Vue.js 官方团队提供的命令行工具,旨在简化 Vue.js 项目的开发流程。
git
常用命令
-
git init — 初始化,在当前文件夹下就会创建出 .git 文件夹,这个就会被git管理
-
git status — 红 绿
- 如果是红色,表明是在工作发生了变化,没有提交到暂存区
- 如果是绿色:表明,暂存区数据没有提交到版本库
-
git add — 把工作区变更,提交到暂存区
-
git commit -m ‘我的第一次提交’ ---- 把暂存区内容,提交到版本库
-
git remote add 远程仓库名字 远程仓库地址 — 添加远程仓库
-
git push origin master ---- 把本地仓库中所有的内容,提交到远程仓库
-
git pull origin master ---- 拉去仓库中最新的代码,否则提交不了
-
git log ---- 查看版本信息(提交过哪些版本,注释是什么)
-
git reflog
-
忽略文件 .gitignore 在里面写忽略的文件或文件夹
-
git冲突解决,有两种情况
- 多人在同一分支,修改了同一个地方的代码,出现的冲突。git pull下来后出现冲突了,就会在自己电脑上显示出不同,选择要保留哪些代码,删除哪些代码。然后git add,git commit -m,git pull,git push。所以在平时的时候,要不停的拉代码。避免冲突。
- 分支合并(git merge)时出现冲突。直接解决就行。然后选择继续在分支中写代码,或者是提交到远程仓库中。
分支合并
git branch dev # 创建分支
git branch # 查看分支,本地
git branch -a # 本地和远程
git checkout 分支名字 # 切换分支
git branch -d 分支名 # 删除分支
git merge dev # 现在一个分支上,把dev分支合并
git pull,git fetch和变基
-git pull 从远程仓库拉取代码:从远程获取最新版本并merge到本地
-git fetch 从远程仓库拉取代码:会将数据拉取到本地仓库 - 它并不会自动合并或修改当前的工作。git pull = git fetch + merge
-变基rebase:先开了一个分支,又开了一个分支,两个分支都进行了改动,现在只想要将第二个分支的内容合并到主分支上去,分支1不变。
docker
Docker是一种开源的容器化平台,用于构建、打包和运行应用程序。
镜像操作、容器操作
docker search 镜像名称 # 搜索镜像
docker pull centos:7 # 拉取镜像,如果不指定标签,会下载最新
docker images # 查看本地镜像
docker rmi 镜像ID # 删除镜像,可以同时删多个,多个空格隔开
docker rmi `docker images -q` # 删除所有镜像
# 容器操作
docker ps docker ps # 查看正在运行的容器
docker ps -a # 查看所有容器
docker ps -l # 查看最后一次运行的容器
docker start id/名字 # 启动停止的容器
docker stop 7d5e # 停止运行的容器
docker stop `docker ps -q` # 停止所有在运行的容器
docker run -di 镜像名:版本号 # 把镜像运行成容器
-id # 不会进入到容器内部
-it # 会进入到容器内部
-v 宿主机目录:容器目录 # 目录映射
-p 3307:3306 # 端口映射
docker exec -it 容器id 命令 # 进入到容器内部
docker rm 容器id # 删除容器
docker cp 目录:目录 # 拷贝文件
docker inspect 容器名称 # 查看容器的详细描述
# 重点命令
docker commit 容器名/id 镜像名/id # 把容器保存为镜像
docker save -o 压缩文件名称.tar 镜像名字 # 保存镜像为压缩文件,备份镜像
docker load -i centos_vim_image.tar # 把备份的镜像恢复
docker tag 镜像名/镜像id 指定的远程仓库地址:版本 # 先给镜像打标签
docker push liuqingzheng/lqz_books:v1 # 上传镜像到私有仓库
docker build -t='镜像名' . # 基于dockerfile构建镜像
docker-file
- docker build -t=‘镜像名’ .
Dockerfile是由一系列命令和参数构成的脚本文件,这些命令应用于基础镜像并最终创建一个新的镜像
案例:构建一个djagno项目
第一步:有一个项目,pycharm开发着,开发完后
第二步:在项目路径下新建Dockerfile,写入
FROM python:3.8 # 基于哪个基础镜像来构建
MAINTAINER lqz # 声明镜像的创建者,maintainer,维修工,维护员
WORKDIR /soft # 设置工作目录,来到容器内部就是/soft下
EXPOSE 8080 # 暴露的端口
COPY ./requirements.txt /soft/requirements.txt # 将./re文件复制一份到soft/re.txt中
RUN pip install -r requirements.txt -i https://pypi.doubanio.com/simple # 执行命令,安装第三方模块的命令
CMD ["python","manage.py","runserver","0.0.0.0:8080"] # 容器启动时需要执行的命令,没有设置cmd命令,那么进入容器后,就会执行centos:7的cmd命令(bin/bash)
第三步:把代码提交到git
------------------
第四步:上线人员:在上线机器上,把代码啦下来
git clone git@gitee.com:zh-jyao/books.git
第五步:构建镜像
cd books
docker build -t='django_books' .
第六步:运行容器
docker run -id --name=books -v /root/lqz/books:/soft -p 8080:8080 django_books:latest
docker run -id
--name=books # 名字
-v /root/lqz/books:/soft # 目录映射
-p 8080:8080 # 端口映射
django_books:latest # 镜像名字
第七步:其它人访问宿主机的8080端口就能看到项目了
---------
第八步:开发人员继续提交代码
第九步:运维人员pull代码,重启容器,用户就可以看到最新的了
git pull origin master
重启docker容器即可(第三方依赖变了)-->重写构建镜像,运行容器
docker restart books
docker-compose
Docker Compose是一个能一次性定义和管理多个Docker容器的工具,单机容器编排【定义和管理】
docker-compose部署项目步骤
第一步:写完一个项目
第二步:编写Dockerfile
第三步:编写docker-compose.yml文件,几个镜像写几个服务
第四步:提交到git中
-----
第五步:远端机器中,把项目git clone 下来
第六步:docker-compose up
docker容器起不来,怎么办?
一 以前的项目,之前能够跑起来,现在跑不起来了
1.命令敲错了2.查看日志3.把之前的镜像的跑起来,之前肯定是有备份的。
二 新的容器直接跑不起来了,前两步骤检查,然后可能就是容器本身的问题。
nginx
https://www.cnblogs.com/zjyao/p/17665797.html
linux
常用shell命令
cd:是切换到你要的目录里去
ls:是显示文件夹里面有些什么文件
mkdir:是创建文件夹,-v 显示详细信息 -p 递归创建目录
touch:是创建文件
mv:是移动某个文件到对应的文件夹下
echo:相当于python的print,是输出的打印的意思
rm:是删除,-r: 递归 -f: 强制删除 -v: 详细过程
cat:查看文件
cp:复制文件
pwd:查看当前文件夹的路径
ps aux |grep sshd # 查看进程
kill -9 进程id号 # 杀进程
netstat -lntp |grep 软件 # 查看端口
vim:是用来做文本编辑的
tree:是显示目录层级
hostname # 查看主机名
useradd lqz # 在/home 路径下创建一个根用户同名的文件夹--->这个用户家路径 $普通用户
history # 可以查看命令历史,上下键可以快速选择
ip addr # 查看本机ip
chmod +x test.sh(文件名) #加入执行权限
文件管理
/bin, 普通用户使用的命令
/sbin,管理员使用的命令
/home,普通用户的家目录, 默认为/home/lqz
/root,超级管理员root的家目录, 普通用户无权操作
/usr,系统文件目录
/boot,启动目录,存放的系统启动相关的文件,例如:kernel,grub(引导装载程序)
/etc,配置文件目录
/var,存放一些变化文件,比如/var/log/下的日志文件,登陆日志
/var/tmp,进程产生的临时文件
/tmp,系统临时目录(类似于公共厕所),谁都可以使用
/dev,存放设备文件,比如硬盘,硬盘分区,光驱,等等
/proc,虚拟的文件系统,对应的进程停止则/proc下对应目录则会被删除
文件编辑
wget、curl,下载文件
rz,上传文件
sz,下载文件
ln -s,软连接
tar xf ,解压
tar czf 压缩包名 压缩后包名,压缩
软件管理
rpm:本地部署,编译好的文件包。
yum:既能够本地安装,又能联网安装。
Flask
路由系统
flask路由系统是基于装饰器的。
@app.route(rule, options):这是Flask中常用的装饰器,用于将一个函数注册为指定URL规则的处理函数。
rule参数指定了请求的路径。例如"/"表示根路径,"/hello"表示/hello路径。
rule:/detail/<int:nid>是可变的路由,有指定的转换器。
-'any': 匹配给定的选项之一。
-'path': 匹配包括斜杠在内的任何文本。
-'int': 将值转换为整数
-'float': 将值转换为浮点数。
options参数是可选的,用于传递其他配置选项。
-methods:限定处理函数接受的HTTP方法。
-endpoint:反向解析时的路由名字
jinja2
是flask的模板语法。它是一个第三方的模板,是可以单独使用的。
支持()调用,支持 字典[] 取值---->模板中写原来python的语法都支持
请求扩展
是在请求来了,或者请求走了,做一些拦截。
1 before_request
2 after_request
3 before_first_request
4 teardown_request
5 errorhandler
6 template_global
7 template_filter
1 before_request:
请求来了执行。可以写多个,从上往下依次执行。在函数上加装饰器@app.before_request。
返回None,会执行下一个请求扩展,如果返回4件套(字符串、html、路由),直接就返回了,不再往下执行了
2 after_request(response)
请求走了时执行。传入响应对象,一定要返回response对象。从下往上执行after_request。
4 teardown_request
每一个请求之后绑定一个函数,即使遇到了异常,不能做统一异常处理--->一般用来记录日志
5 errorhandler:可以做统一的异常处理
6 template_global:全局标签,所有页面都可以用
7 template_filter:全局过滤器
闪现
闪现(Flash)是一种临时存储消息的机制,用于在请求之间传递消息或数据。它通常用于在重定向后将消息传递给下一个请求,例如在表单提交后显示成功或错误消息。取完不用手动删除,会自己删除的。
# 如何设置
flash('aaa')
# 如何取
get_flashed_message()
# 分类放
flash('超时错误', category="x1")
# 分类取
data = get_flashed_messages(category_filter=['x1'])
闪现功能可以方便地将消息或数据在请求之间进行传递,并且只在接下来的请求中可见。它通常用于显示成功、错误或其他提示信息给用户。
蓝图
django中它路由,视图,模板都有自己的文件位置。flask就是用使用蓝图来划分目录。
-1 实例化得到一个蓝图对象
order_blue=Blueprint('order',__name__,template_folder='../templates')
-2 以后注册路由,写请求扩展,都使用蓝图
@order_blue.route('/register')
@order_blue.before_request
-3 在app中注册蓝图
from . import user
app = Flask(__name__)
app.register_blueprint(user.order_blue)
链接池
安装DBUtils,
1 创建一个池对象app = PooledDB,传入一堆参数(连接数据库的模块pymysql,最大连接数,host,port,user,password,database,charset)
2 从池对象中,取出一个链接使用
conn = PYMYSQL_POOL.connection()
3 使用
@app.route('/')
def index():
conn = PYMYSQL_POOL.connection() # 从池中拿一个链接
cursor = conn.cursor(cursor=DictCursor) # 默认元组套元组,设置DictCursor就是列表套字典
cursor.execute('select id,title from news where id<10 SQL语句')
res1 = cursor.fetchall()
cursor.close()
conn.close()
return jsonify(res1)
flask 信号
信号是一种事件触发和处理的机制,允许应用程序对特定事件进行响应。我们可以在特定的时刻执行自定义的代码。
# 内置信号
from flask import Flask, signals
def test(app, **kwargs):
print(app)
print(type(kwargs))
第二步:跟内置信号绑定
signals.before_render_template.connect(test)
-request_started:请求到来前执行
-request_finished :请求结束后执行
-before_render_template:模板渲染前执行
-template_rendered :模板渲染后执行
-got_request_exception :请求执行出现异常时执行
# 自定义信号
1 第一步:定义一个自定义 信号
from flask.signals import _signals
session_input = _signals.signal('session_input')
2 第二步:写个函数
def test2(*args, **kwargs):
print(args) # app
print(kwargs) # {session kk}
3 第三步:函数跟自己定义信号绑定
session_input.connect(test2)
4 第四步:触发自定义信号--->我们做
session_input.send(app, session=session, kk={'name': 'uu'})
# 作用:
记录日志:只要是张三用户,访问index页面,就记录日志
只要用户表中,插入一条记录,就给用户发个短信通知
flask-script实现命令
安装flask-script
1 导入,创建manager对象
from flask_script import Manager, Command
manager=Manager(app)
2 定义自定义命令:
class MyCustomCommand(Command):
def run(self):
# 自定义命令逻辑
print("Running my custom command...")
3 将自定义命令添加到Manager对象:
manager.add_command('mycommand', MyCustomCommand())
4 运行自定义命令:
python manage.py mycommand
SQLAlchemy
https://www.cnblogs.com/zjyao/p/17649589.html
SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,
使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,
然后使用数据API执行SQL并获取执行结果。
session对象
flask中得session,没有在服务端存储数据的,数据保存到djagno-session表中
后期扩展,可也把session存到redis中
# 全局session
-放值:session['key']=value
-取值:session.get('key')
-删除值:session.pop('username', None)
# session的运行机制
django
1 生成一个随机字符串
2 把数据保存到djagno-session表中
3 把随机字符串返回给前端-->当cookie存到浏览器中了-->浏览器再发请求,携带cookie过来
4 根据随机字符串去表中查--->转到request.session中
flask
1 把数据加密转成字符串: eyJuYW1lIjoibHF6In0.ZMnbJw.ZUceSaD0kGnU97tu9ZWm3380r00
2 以cookie形式返回给前端--->保存到浏览器中
3 浏览器再发请求,携带cookie过来
4 加密符串--->解密--->放到session对象中
SQLAlchemy执行原生SQL
https://www.cnblogs.com/zjyao/p/17649615.html
- 第一步:创建engine对象:engine = create_engine()
- 第二步:通过engine获得链接
conn=engine.raw_connection()
cursor = conn.cursor()
cursor.execute(
"select * from news"
)
result = cursor.fetchall()
- 关闭
cursor.close()
conn.close()
rabbitmq
rabbitmq:https://www.cnblogs.com/zjyao/p/17654382.html
RPC调用:
其他重点
http协议详情,http协议版本,http一些请求头
http是超文本传输协议,它是基于Tcp之上的应用层协议(osi七层)。
特点:
- 1 基于请求响应–>服务端不能主动给客户端推送消息----websocket协议
- 2 无状态—>不记录用户的状态,不能做会话保持—>才出现了cookie,session,token。 eg:我在购物车加了商品,下次想要下单,不知道谁下的单,所以不能做会话保持
- 3 短链接/无连接 客户端和服务端建立链接之后,客户端发送一些请求,服务端响应一次,然后断开
- 4 基于tcp之上的应用层协议 —> osi七层
请求协议
- 请求首行:请求方式(get,post,delete),请求地址,请求http协议版本号/r/n —> 请求方式,请求头
- 请求头:key:value形式。
- 请求体:编码方式 —> 编码方式
响应协议
- 响应首行:http协议版本,响应状态码(1xx,2xx),响应单词描述 —> 响应状态码,‘单词描述有什么,可以简单说一下’
- 响应头:key:value (cookie,响应编码Content-Type…)跨域问题的响应头(自己设置的响应头) —>‘自己设置的响应头这个看一下’
- 响应体:html格式:浏览器中看到的 json格式给客户端使用
协议版本
- 0.9:HTTP协议的最初版本,功能简陋,仅支持请求方式GET,并且仅能请求访问HTML格式的资源
- 1.0:但是1.0版本的工作方式是每次TCP连接只能发送一个请求(默认短链接),当服务器响应后就会关闭这次连接,下一个请求需要再次建立TCP连接,就是不支持keep-alive
- 1.1: 引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用(多个请求同时用一个TCP),不用声明Connection: keep-alive。客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。
- 2.0: 多路复用:对于 HTTP/1.x,即使开启了长连接,请求的发送也是串行发送的,在带宽足够的情况下,对带宽的利用率不够,HTTP/2.0 采用了多路复用的方式,可以并行发送多个请求,提高对带宽的利用率
- 3.0:(蓝图) HTTP3.0又称为HTTP Over QUIC,其弃用TCP协议,改为使用基于UDP协议的QUIC协议来实现
请求头
- User-Agent:标识客户设备类型的字段
- Content-Type:请求的编码格式
- Authorization:jwt的身份验证的token串
- Accept:指定客户端能够接收的内容类型。
- Cookie
- Referer:表示当前请求的来源页面的URL,用于追踪用户的访问路径。
请求方式
get post put delete option预检请求
预检请求(Preflight request)是在进行跨域请求时,浏览器发送的一种 OPTIONS 请求,
用于确认实际请求是否可以安全地进行。预检请求通常会在执行实际请求之前发送,
以确保服务器支持该跨域请求并具有相应的安全设置
# get请求和post请求的区别
post更安全(不会作为url的一部分)
post发送的数据更大(get有url长度限制)
post能发送更多的数据类型(get只能发送ASCII字符)
post比get慢
post用于修改和写入数据,get一般用于搜索排序和筛选之类的操作
编码格式/方式:
- urlencoded:传普通数据
- form-data:传普通数据、文件
- json
响应状态码
- 1xx 请求正在处理
- 2xx 正常响应:表示请求已成功被服务器接收、理解和处理。
- 200 OK:请求成功,返回正常结果。
- 201 Created:请求已经被服务器处理,并创建了新资源。
- 204 No Content:服务器成功处理了请求,但没有返回任何内容。
- 3xx 重定向响应:
- 301 Moved Permanently:永久重定向
- 302 Found:暂时重定向
- 4xx 客户端错误:
- 403 Forbidden:请求无权限
- 404 Not Found:请求路径不存在。未找到
- 405 Method Not Allowed:请求方法不存在
- 5xx 表示服务器在处理请求时发生了错误:
- 500 Internal Server Error:服务器异常
websocket协议
实现服务器主动向客户端发送消息
http的无连接,基于请求响应
-
1 轮询:
客户端向服务端无限循环发送http请求,一旦服务端有最新消息,从当次http响应中带回,客户端就能收到变化 -
2 长轮回(web版微信采用此方式)
客户端和服务端保持一个长连接(http),等服务端有消息返回就断开,
如果没有消息,就会hold住,等待一定时间,然后再重新连接,也是个循环的过程
(之前秒杀向后端发起请求后,前端起了一个定时任务,每3秒向后端发起一个请求,有了响应就展示到页面上) -
不好实现http主动向客户端发送请求,才出现的websocket协议
-
3 websocket协议
客户端发起HTTP握手,告诉服务端进行WebSocket协议通讯,并告知WebSocket协议版本。
服务端确认协议版本,升级为WebSocket协议。之后如果有数据需要推送,会主动推送给客户端
websocket:一种通信协议,区别于http协议,可在单个TCP连接上进行全双工通信(双工通信:两方都能够发送消息。单工通信:只能一方给另一方发送消息)。允许服务端主动向客户端推送数据。浏览器和服务器只需要完成一次tcp连接,两者之间就可以建立持久性的连接,并进行双向数据传输
- 没有跨域问题
- 应用场景:
- 只要涉及到服务端主动给客户端发送消息的场景,都可以用
- 实时聊天
- 服务端发生变化,主动向客户端推送一个通知
- 监控服务端的内存使用情况
- djanog中使用channles模块实现
- flask中使用模块
paramiko模块
- ssh链接操作linux机器,远程执行命令
- 上传、下载文件
https://www.cnblogs.com/zjyao/p/17663099.html
celery框架
https://www.cnblogs.com/zjyao/p/17527187.html
celery是一个分布式的异步任务框架。可以用来做异步任务、延时任务、定时任务。
celery架构有 1消息中间件(broker):消息队列:可以使用redis,rabbitmq,
2任务执行单元(worker):真正的执行,提交的任务,
3任务执行结果存储(banckend):可以使用mysql,redis
使用:安装Celery、eventlet(win上运行需要eventlet支持)
app = Celery(__name__,
broker=broker, # 消息中间件
backend=backend, # 结果存储中间件
include=['celery_task.course_task']) # 任务模块
函数上加@app.task装饰器,这个任务就变成celery的任务了。
异步任务:add.delay('参数1','参数2')
延时任务:函数.apply_async(kwargs={'mobile':'1896334234','code':8888},eta=时间对象)
定时任务:在app所在的文件下配置。
启动worker,启动beat
celery -A celery_task worker -l info -P eventlet # 执行任务
celery -A celery_task beat -l info # 提交延时或定时任务的
APScheduler框架
https://www.cnblogs.com/zjyao/p/17666797.html
其他
什么是qps,tps,并发量,pv,uv
# qps:
Queries Per Second,每秒查询率,一台服务器每秒能够响应的查询次数,每秒的响应请求数
是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。
它是衡量系统吞吐量的一个常用指标,即服务器在一秒的时间内处理了多少个请求。
QPS 的值越大表示服务器的吞吐量越大,同时服务器的负荷往往也越高。
-如何估算自己项目的QPS?
使用日志估算即可,比如在中间件里记录访问日志,最终统计1s内有多少个访问,qps就是多大;
# TPS:
# transaction处理,会报;汇报
Transactions Per Second,是每秒处理的事务数,包括一条消息入和一条消息出,加上一次用户数据库访问
TPS 的过程包括:客户端请求服务端、服务端内部处理、服务端返回客户端。
例如,访问一个 Index 页面会请求服务器 3 次,包括一次 html,一次 css,一次 js,那么访问这一个页面就会产生一个T,产生三个Q
# 并发量
系统同时处理的请求或事务数,可以直接理解为:系统同时处理的请求数量
QPS = 并发量 / 平均响应时间
例如当前系统QPS为1w,每个请求的响应时间都是2s,那么并发量就是2w
# PV
# view阅读,看
PV(Page View):页面访问量,即页面浏览量或点击量,用户每次刷新即被计算一次。可以统计服务一天的访问日志得到。
# UV
# unique 独一无二的,独特的;非常特别的
UV(Unique Visitor):独立访客,统计1天内访问某站点的用户数。可以统计服务一天的访问日志并根据用户的唯一标识去重得到。
# DAU(日活)
DAU(Daily Active User),日活跃用户数量。常用于反映网站、app、网游的运营情况。
DAU通常统计一日(统计日)之内,登录或使用了某个产品的用户数(去除重复登录的用户),与UV概念相似
# MAU(月活)
MAU(Month Active User):月活跃用户数量,指网站、app等去重后的月活跃用户数量
什么是接口幂等性问题,如何解决?
-幂等:幂等(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
根据version版本,也就是在操作库存前先获取当前商品的version版本号,然后操作的时候带上此version号。我们梳理下,我们第一次操作库存时,得到version为1,调用库存服务version变成了2;但返回给订单服务出现了问题,订单服务又一次发起调用库存服务,当订单服务传入的version还是1,再执行上面的sql语句时,就不会执行;因为version已经变为2了,where条件就不成立。这样就保证了不管调用几次,只会真正的处理一次。
'不懂乐观锁的情况下就不要说'
# 唯一主键
这个机制是利用了数据库的主键唯一约束的特性,解决了在 insert 场景时幂等问题。但主键的要求不是自增的主键,这样就需要业务生成全局唯一的主键
# 唯一ID(unique)
调用接口时,生成一个唯一id,redis 将数据保存到集合中(去重),存在即处理过
# 防重表
使用订单号 orderNo 做为去重表的唯一索引,把唯一索引插入去重表,再进行业务操作,且他们在同一个事务中。这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。
# 前端:
按钮只能点击一次
python第三方库用过哪些
Django:用于开发Web应用程序的Python Web框架。
Flask:用于构建轻量级Web应用程序的Python Web框架。
Django ORM:用于与数据库交互的ORM库。
hashlib、md5、sha:用于加密的库。
celery框架:
APScheduler定时任务框架:
rabbitmq
Scrapy:Scrapy是一个基于Python的高级网络爬虫框架,主要用于抓取大规模的网站数据。
BeautifulSoup:爬虫种用于解析HTML和XML文档的库。
lxml:lxml是一个快速、简单易用的库,支持HTML和XML的解析
Selenium:用于自动化Web浏览器测试的库,可以直接操作浏览器。爬虫
Requests:用于发送HTTP请求的库。
NumPy:用于处理多维数组和矩阵的库。
Pandas:用于数据分析和数据处理的库。
SciPy:SciPy是一个开源的Python算法库和数学工具包,包含最优化、线性代数、积分、插值、特殊函数、快速傅里叶变换、信号处理和图像处理、常微分方程求解等模块
Matplotlib:用于绘制图表和可视化数据的库。
Scikit-learn:用于机器学习和数据挖掘的库。
TensorFlow:用于深度学习和人工智能的库。
Keras:用于构建、训练和调试深度学习模型的库。
PyTorch:用于计算机视觉、自然语言处理和其他深度学习任务的库。
内置模块
re:
time:计算机能够看懂的时间
datetime:我们可以看懂的时间
random:随机数
os:与操作系统交互。文件、路径
sys:与python解释器交互。sys.path
json:序列化模块,dump()序列化,把字符格式转成json格式。load()
pickle:python的数据类型间的转换。把python数据类型序列化。
hashlib:加密模块
logging:日志模块
subprocess:用python代码实行远程命令
数据处理的第三方库:正则
正则是:匹配字符串内容的一种规则。
.:匹配除换行符以外的任意字符
*:匹配零次或多次,默认是多次(无穷次)
?:匹配零次或一次
re模块
re.findall('字符',要匹配的长字符)返回列表形式,匹配不到,返回[]
re.search()只要找到第一个符合条件的数据就返回一个包含匹配信息的对象,对象可以通过调用group()方法得到匹配的字符串。没有返回None
re.match()匹配开头符合条件的数据,一个就结束。成功返回,没有None
PEP8规范
# 良好的代码风格
- 函数和类的定义,代码前后都要用两个空行进行分隔。
- 在同一个类中,各个方法之间应该用一个空行进行分隔。
- 类和异常的命名,应该每个单词首字母大写。
- 类的实例方法,应该把第一个参数命名为self以表示对象自身。
- 类的类方法,应该把第一个参数命名为cls以表示该类自身。
- 如果有多个import语句,应该将其分为三部分,从上到下分别是Python标准模块、第三方模块和自定义模块,每个部分内部应该按照模块名称的字母表顺序来排列。
restful规范
# AIP接口的编写规范
1 url链接一般都采用https协议进行传输,数据的安全保障
2 url地址中带接口标识:https://api.baidu.com
看到api字眼,就代表该请求url链接是完成前后台数据交互的
3 多数据版本共存,url地址中带版本信息
https://api.baidu.com/v1/login/
4 数据即是资源,均使用名词(可复数)
url地址尽量使用名词。接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
5 资源操作由请求方式决定(method)
操作资源一般都会涉及到增删改查,我们提供请求方式来标识增删改查动作
https://api.baidu.com/books - get请求:获取所有书
https://api.baidu.com/books/1 - get请求:获取主键为1的书
https://api.baidu.com/books - post请求:新增一本书书
https://api.baidu.com/books/1 - put请求:整体修改主键为1的书
https://api.baidu.com/books/1 - delete请求:删除主键为1的书
6 url地址中带过滤条件 ?后带过滤条件
7 响应状态码
4.7.1 http的响应状态码
请求正在处理:1xx
正常响应:2xx
200:常规请求
201:创建成功
重定向响应:3xx
301:永久重定向
302:暂时重定向
客户端错误:4xx
403:请求无权限
404:请求路径不存在
405:请求方法不存在
服务器异常:5xx
500:服务器异常
4.7.2 http的响应的数据中带状态码(公司自己规定的)
{code:100}
8 返回的数据中带错误信息
{code:101,massige:用户名或密码错误}
9 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
返回的数据是个字符串,只是有不同的规范。
* GET /collection:返回资源对象的列表(数组)
- [{name:红楼梦,price:88},{name:西游记,price:88}]
* GET /collection/resource:返回单个资源对象
- \{name:红楼梦,price:88\}
* POST /collection:返回新生成的资源对象
- {id:4,name:红楼梦,price:88}
* PUT /collection/resource:返回完整的资源对象
- {id:4,name:红楼梦,price:188}
* DELETE /collection/resource:返回一个空文档
我们自己的规范是:
* GET /books:
- {code:100,msg:成功,data:[{name:红楼梦,price:88},{name:西游记,price:88}]}
* GET /books/1:
- {code:100,msg:成功,data:{name:红楼梦,price:88}}
* POST /books:
- {code:100,msg:成功}
* PUT /books/4:
- {code:100,msg:修改成功}
* DELETE /books/4:
- {code:100,msg:删除成功}
10 需要url请求的资源需要访问资源的请求链接,eg:图片地址
个人问题
个人技术提升,从哪方面提升
1.公司中项目的新技术,直接百度搜索,知乎,直接看官方文档。
2.会在稀土、博客园、CSDN博客,上看一些感兴趣的文章。
3.有时会去gitee、github上看开源代码
4.觉得对哪些语言感兴趣,或者是觉得哪个方面比较好,也会直接搜索。或者去哔哩哔哩上找视频
没有写的知识点
numpy使用过吗
ndarray和list区别
python错误处理 /异常处理
try except异常捕捉需要注意
1. 捕捉异常尽量的少用
2. try里面被监测的代码尽量少
3. 在明显没有错误的代码不要被捕捉
万能异常 Exception/BaseException
else:try的子代码正常运行结束没有任何的报错后 再执行else子代码,try中有错误else则不会执行
finally:无论try的子代码是否报错 最后都要执行finally子代码
支付宝支付
数据结构:链表双链表
django性能优化
orm性能优化
redis缓存双写一致性
redis主从(从节点坏掉了 数据跑哪了),主库里面有完整的数据,从库中做了持久化,就会保存起来。
mysql多对多的原理
1.django、flask、fastapi三者的区别
2.进程、线程、协程讲一下
3.装饰器用过哪几种?
方法注册、和记录日志、一些格式的转换这些装饰器,是怎么实现的?
例如:写了个定时器的模块,用装饰器的方法将其他人写的功能注册到被修饰的方法内,怎么做?
5.restful规范认证的话用什么方案,token还是session?
7.flask中的蓝图是个什么概念
8.MySQL中automatic和auto…是控制什么的
10.场景:程序定期查询所有节点状态、节点大概1w个,时间大概五分钟一次,对于这种场景设计一个方案。
用线程池的话,详细讲一下,怎么做
websocket和http的区别
jwt 的原理
redis和rabbitmq用哪个做消息队列 为什么用redis不用rabbitmq 它有什么缺点
django数据库迁移命令 为什么执行两遍第二遍会跟你说没有数据要修改 django怎么做的
http keepalive 是什么
http是基于tcp/ip协议 但是tcp又是有状态的 为什么http是无状态无连接的
一个主进程下有4个子进程 那80端口是在主进程还是子进程
怎么查看哪个进程占用的哪个端口
docker如果数据放在容器中 容器删了 那数据就销毁了 这个怎么搞
django的优缺点
django的生命周期
django一般用的有哪些开发模型
djangoorm里的queryset有哪些方法
orm去增删改查数据
celery的特性
celery怎么实现异步处理的,怎么处理进程的协作,状态怎么控制
celery里面有什么组件
django里面有哪几种减少数据库查询的方法
什么数据适合放在redis里面呢
django的模型继承有哪几种方式
数据库结构设计在django本身做还是在数据库层面做
jenkins
gitlab代码迭代是怎么迭代的
celery的原理
linux常用命令
如何在linux查看程序运行
django类方法调用底层的as_view底层的原理
匿名函数
装饰器
docker-compose
代码怎么在docker里面跑起来
怎么让编译器识别到你的代码,端口映射
mysql 优化查询 聚合函数 深浅拷贝 drf的理解 nginx 项目部署 union 英文 负载均衡: