面试旁白
面试旁白
- 面试旁白
- 内置函数
- GIL锁
- 闭包函数
- 深浅拷贝
- GC机制(垃圾回收机制)
- 匿名函数
- 递归函数
- 装饰器
- 反射机制
- oop编程
- 封装继承多态
- 魔法方法
- io多路复用
- 进程线程协程
- 多进程多线程
- 进程池
- 进程间通信
- 生产消费者模型
- 乐观锁和悲观锁
- Django的auth模块
- Django的中间件
- django的forms组件
- Django的过滤与排序
- orm的优化
- restful规范
- django的生命周期请求流程
- JWT的认证与签发
- xadmin替换
- action装饰器
- mysql严格模式
- redis的架构
- redis五大数据结构
- redis的持久化
- 主从复制:多版本
- 消息队列
- 互斥锁
- Websocket协议
- sql的注入
- xss脚本攻击
- csrf跨域请求伪造
- Nginx负载均衡
- dockerfile
内置函数
sum()、sorted()、open()、float()、range()、print()、set()无序不重复元素集、getattr\setattr\hasattr
GIL锁
- GIL锁是cpython的特点,在cpython解释器中,GIL是一把互斥锁,用来保证进程中同一时刻只有一个线程在执行。
- 在没有GIL锁的情况下,有可能多线程再执行一个代码的同时,垃圾回收机制对所执行代码的变量直接进行回收,其他的线程再使用该变量时会导致运行错误
闭包函数
在函数内部引用了其外部作用域的变量,并且可以在函数被调用后继续访问和操作这些变量
深浅拷贝
- 浅拷贝,指的是重新分配一块内存,创建一个新的对象,但里面的元素是原对象中各个子对象的引用。
- 深拷贝,是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联。
GC机制(垃圾回收机制)
引用计数:有多少个变量指向它,它的引用计数就为几,当引用技术为0的时候,说明没有变量指向它了,这块内存就会被回收掉,引用计数存在循环引用问题
标记清除:是为了解决引用计数存在的循环引用问题
第一阶段是标记阶段,它会把所有的 [活动对象] 打上标记,第二阶段是把那些没有标记的对象【非活动对象】进行回收
它会把循环引用的内存空间,打上标记,然后回收掉
分代回收
把对象分为三代,一开始,对象在创建的时候,放在第一代,如果在一次一次的垃圾回收检查中,该对象存活下来,就会被放到二代中,同理在一次二代垃圾检查中,该对象存活下来,就会被放到第三代中,后面优先检查第一代的对象,优先回收,其次依次往上检查做回收
匿名函数
lambda 没有名字的函数 需要运用在一个函数上,只要用一次,又不想费神去命名,有局限性。只能用一次,也无法在其他类中使用。但是在日常的使用过程中,lambda主要以参数的用法存在的。。可以用在比较大小、排序、过滤、映射配合着使用
递归函数
函数直接或者间接的调用函数本身,则称为递归函数。如果在一个函数内部,调用本身自身,那么这个函数就是递归函数
特性:
- 必须有一个明确的结束条件
- 每进入更深一层的递归时,问题规模要比上一次递归都应减少
优点:逻辑简单清晰,缺点是过深的递归容易导致栈溢出
尾递归:为了解决递归调用栈溢出的问题。尾递归是指,在函数返回的时候,调用自身,并且 return 语句中不能包含表达式
尾递归和循环的效果一样,可以把循环看成一种特殊的尾递归函数。但是python没有针对尾递归做优化,Python解释器也没有做优化,所以还是要注意递归深度
装饰器
装饰器通常情况下本质是闭包,在不改变被装饰的函数的源代码和调用方式的情况下给对象新增新的功能或者方法。
被装饰的函数会被当做参数传入装饰器,最后再返回
应用场景:
- 权限的校验:django中的@login_required和@permission_required装饰器
- 性能测试:计算函数运行时间,运行次数,
- 插入日志
有了装饰器,我们就可以抽离出大量与函数功能本身无关的代码,增加一个函数的重用性和整体代码的可读性
类装饰器
写一个类,也要有__init__ 重写__call__方法
反射机制
反射机制使得程序在运行的过程中动态修改结构和行为的能力,通过字符串的形式去对象中操作(增删查改)
python中主要有4个重要的内置函数:
getattr
判断类、对象或者模块是否有相应的功能或者方法, getattr(obj,str,default=None) # 判断obj中是否有str属性,有就返回,没有的时候如果有第三个参数就返回,没有就报错
setattr
设置属性。第三参数为新的属性值
hasattr
判断是否有某个属性,有就返回True,没有就返回False
delattr
删除某个属性
oop编程
面向对象的本质,其实就是一种如何将大量的代码和数据进行管理的归档方法。每一个类,其实就像一个文档盒子,把相似的东西,可以归类在同一个盒子里
封装继承多态
封装:是为了提高代码的复用性和可维护性,把很多数据封装到一个对象中,把固定功能的代码封装到一个代码块,比如洗衣机
继承:继承是类与类的一种关系,子类与分类的关系,子类继承父类的属性和方法,也可以提供自己的属性和方法
多态:多态就是一个行为具有多个不同表现形式或形态的能力。不同对象调用同一个方法功能的表现形式不一样
单例:确保一个类中只有一个实例存在,单例模式的实现方式
1.通过模块实现
2.装饰器实现
3.类实现 重写__new__方法
魔法方法
init__和__new
类加括号的创建对象的时候,第一执行__new__方法,new方法其实才是真正的实例化一个对象,将返回值cls传给init作为第一个参数,然后init在初始化对象,完成创建
new()的返回语句中,object.new(cls)意思是调用父类(object)的__new__(),
__init__是类加括号自动触发,执行初始化对象,
str:执行打印的时候自动触发
getattr(self,name):当对象获取一个不存在的属性时的行为,
call 类创建出一个对象,对象加括号的时候自动触发
类实现装饰器,类中写了__init__和重写__call__方法
io多路复用
i/o多路复用(I/O Multiplexing) 是一种I/O 模型,循序单个进程同时监听多个I/O事件(例如套接字链接,数据连接等),从而提高I/O处理的效率。
在io多路复用模型中,操作系统提供了一种机制,允许应用程序使用一个程序调用来同时监听多个I/O时间。无须使用多个线程或者进程来处理这些事件。
实现的三种方式: select、poll和epoll,它们都是在操作系统内核中实现的,应用程序可以使用这些 API 在用户空间中与内核交互。
了解知识点 ,再问的深入了 就不行了
进程线程协程
① 进程
进程是程序关于某个数据集合上的一次运行活动,是操作系统资源分配的最小单位,每个进程都有独立的内存空间和系统资源,同一台计算机上的多个进程数据上严格意义上的物理隔离(默认情况下),也就是说默认情况下进程之间无法互相传递信息,进程之间通过进程间通信(IPC)进行交互。
在Python中,可以使用multiprocessing
模块来创建新的进程
② 线程
线程是进程中的一条执行路径,是操作系统调度的最小单位,多个线程可以共享进程的资源,例如内存空间、文件句柄等。
③ 协程
协程是一种轻量级的线程,它是用户自己调度的,不需要操作系统进行切换。
协程可以在一个线程中实现多个子任务的并发执行,使用协程可以避免线程切换和锁竞争等开销,从而提高程序的执行效率,多个协程也相对独立,但是其切换由程序自己控制。
避免了传统的多线程或多进程编程中可能遇到的线程切换、上下文切换等问题。
进程:进程其实是资源单位,表示一块内存空间
线程:线程才是执行单位,表示真正的代码指令
多进程多线程
多进程:在多进程编程中,每个进程都有自己独立的内存空间和系统资源,因此多进程编程天然具有并发性和并行性。多进程编程需要使用操作烯体哦那个提供的进程管理机制,如fork 因此跨平台性差,但是安全性高
多线程:在多线程编程中,多个线程共享同一个进程的内存空间和系统资源, 因此多线程2.编程更加轻量级。多线程编程需要使用锁、信号量等机制来保证线程安全,同时也需要考虑线程的上下文切换等问题,因此在实现时需要注意线程安全问题。
协程: 协程是一种用户态的轻量级线程,它可以在单个线程中实现并发。协程之间可以通3.过yield和send等方式切换执行权,从而实现并发和异步编程。协程的优点是轻量级、高效,但也需要注意协程的切换开销和共享状态的问题。
进程池
进程池是资源进程、管理进程组成的技术的应用。
进程池技术的应用由以下两部分组成:
资源进程:
预先创建好的空闲进程,管理进程会把工作分发到空闲进程来处理
管理进程:
管理进程负责创建资源进程,把工作交给空闲资源进程处理,回收空闲资源进程
管理进程要有效的管理资源进程,那么管理进程跟资源进程间必然需要交互,通过IPC,信号,信号量,消息队列,管道等进行交互。(先别说)
进程间通信
进程间通信的目的
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源
- 通知事件:一个进程需要向另一个或者一组进程发送消息
- 进程控制:有些进程期望完全控制另外一个进程的执行
进程间通信的本质
进程间通信的本质是 让不同的进程看到同一份资源(内存,文件,内核缓冲等)
几种方式
管道 匿名管道 命名管道 消息队列 共享内存 信号量 信号 socket
生产消费者模型
生产者:
产生数据的模块,就形象地称为生产者;
消费者:
处理数据地模块,就成为消费者
缓冲区(阻塞队列)
生产者和消费者之间的中介叫做缓冲区
生产消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题,生产者和消费者彼此之间不能直接通讯,二通过阻塞队列来进行通信
生产者生产完数据不用等待消费者处理,而是直接丢给阻塞队列,消费者不找生产者要数据,而是直接去阻塞队列中直接取,阻塞队列相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是给生产者和消费者解耦合的
乐观锁和悲观锁
悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。
乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了(具体方法可以使用版本号机制或 CAS 算法)。
在INSTALLED_APPS中添加'django.contrib.auth'使用app,auth模块默认启动
Django的auth模块
auth模块是django提供的标准 权限管理系统,可以提供用户身份认证,用户组和权限管理这些功能,那么我就可以使用它来 auth 和admin模块配合使用,快速建立网站的管理系统
auth认证系统的几大常用功能:
- 创建用户
- 认证用户
- 登录
- 登出
- 只允许登录用户访问
- 判断用户是否登录
1.创建用户 User.objects.create_user(username,password) 2.认证用户 # 创建的user用户的password是加密之后入库的,无法直接判断用户是否存在,所以django自带了authenticate函数来进行用户认证 from django.contrib.auth import authenticate user = authenticate(username=username,password=password) # 认证用户的密码是否有效, 若有效则返回代表该用户的user对象, 若无效则返回None(注意:该方法不检查is_active标志位.) 3. 登录 from django.contrib.auth import login login向session中添加SESSION_KEY, 便于对用户进行跟踪): login(HttpRequest,user) login(request, user) 4. 登出 from django.contrib.auth import logout 使用(logout会移除request中的user信息, 并刷新session) 5.只允许登录的用户访问: @login_required修饰器修饰的view函数会先通过session key检查是否登录, 已登录用户可以正常的执行操作,未登录用户将会被重定向到login_url指定的位置 如果未指定login_url参数,则重定向到settings.LOGIN_URL # 导入使用函数 from django.contrib.auth.decorators import login_required # 使用 @login_required(login_url='/accounts/login/') def my_view(request): ... 6.判断用户是否登录 is_authenticated # 通过在视图函数中利用User对象的is_authenticated方法进行判断用户是否登录
Django的中间件
自定义中间件:
# 重点 process_request # 请求来的时候会经过每一个中间件的process_request方法 ,从上往下,方法里有一个request参数 如果没有,则跳过该中间件,执行下一个 如果该方法中返回了HttpResponse对象,则请求将不再往下执行,返回 process_response process_view # 掌握 process_render_templates process_exception
django的forms组件
Django的过滤与排序
orm的优化
尽可能的使用only与defer
:Defer方法的用途是查询数据库时跳过指定的字段,only方法是指定需要载入的字段。从而节省空间。一行数据可能没啥关系,但数据很多时,节省的内存空间不可忽视。
only会产生对象结果集,对象点括号内出现的字段不会再走数据库查询,但是如果点击了括号内没有的字段也可以获取到数据 但是每次都会走数据库查询
defer相反,对象点括号内出现的字段会走数据库查询,如果点击了括号内没有的字段也可以获取到数据 每次都不会走数据库查询
排序
排序功能接口只针对于:获取所有数据,也就是list方法接口,并且需要视图类继承GenericAPIView及其子类
视图类
from rest_framework.generics import ListAPIView from rest_framework.filters import OrderingFilter from .models import Book from .serializer import BookSerializer class BookView(ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer # 重写GenericAPIView提供的属性,把排序类加进去 filter_backends = [OrderingFilter, ] # 需要排序的字段,不写默认为序列化类所有字段 ordering_fields = ['price'] def get(self, request, *args, **kwargs): return super().list(request, *args, **kwargs)
根据访问地址中带参数来排序
按照price字段来排序
http://127.0.0.1:8000/books?ordering=price # 升序 http://127.0.0.1:8000/books?ordering=-price # 降序 http://127.0.0.1:8000/books?ordering=字段1,字段2
过滤
过滤用于筛选数据,与排序一样,只作用于list方法的接口,并且视图类要继承GenericAPIView类及其子类。
排序过滤的原理:
关键在于GenericAPIView类中的filter_queryset方法
内置过滤类
SearchFilter过滤类:用于模糊查询
# SearchFilter过滤类 模糊查询 from rest_framework.generics import ListAPIView from rest_framework.filters import SearchFilter from .models import Book from .serializer import BookSerializer class BookView(ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer # 重写GenericAPIView提供的属性,把过滤类加进去 filter_backends = [SearchFilter, ] # 需要过滤的字段,不写就无法过滤 search_fields = ['name', 'price'] def get(self, request, *args, **kwargs): return super().list(request, *args, **kwargs)
过滤:
http://127.0.0.1:8000/books?search=5 # 根据search_fields过滤数据,search=5相当于: name like 5 or price like 5
第三方过滤类
安装:
pip install django-filter
视图类使用:
from django_filters.rest_framework import DjangoFilterBackend from rest_framework.generics import ListAPIView from .models import Book from .serializer import BookSerializer class BookView(ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer # 重写GenericAPIView提供的属性,把过滤类加进去 filter_backends = [DjangoFilterBackend, ] # 需要过滤的字段 filter_fields = ['name','price'] def get(self, request, *args, **kwargs): return super().list(request, *args, **kwargs)
根据访问地址中带参数来过滤:
过滤:
http://127.0.0.1:8000/books?name=xx&price=5 相当于: name=xx and price=5
自定义过滤类
from rest_framework.filters import BaseFilterBackend # 继承BaseFilterBackend class BookFilter(BaseFilterBackend): # 重写filter_queryset def filter_queryset(self, request, queryset, view): """主要操作queryset参数""" # 返回的数据就是过滤后的数据 return queryset.filter(price__gt=50)
视图类:
from rest_framework.generics import ListAPIView from .models import Book from .serializer import BookSerializer # 导入过滤类 from .throttling import BookFilter class BookView(ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer # 重写GenericAPIView提供的属性,把过滤类加进去 filter_backends = [BookFilter, ] def get(self, request, *args, **kwargs): return super().list(request, *args, **kwargs)
restful规范
Restful是一种定义Web APl接口的设计风格,尤其适用于前后端分离的应用新模式
十个规范
- 数据的安全规范,通常使用https协议
- 接口中带有api标识
- 多版本共存,路由中带版本信息
- 数据就是资源,尽量使用名词,
- 资源操作由请求方式决定(method)
- 请求地址带过滤条件
- 响应状态码
- 返回数据中带错误信息
- 返回的结果应该符合规范
- 返回数据中带链接
1.数据的安全规范,通常使用Https(http+ssl)协议 url连接一般采用https协议进行传输 采用https协议,可以提高数据交互过程中的安全性 2. 接口中带api标识 可以写在域名中,也可以写在路由中(我一般都写在路由中) 3. 多版本共存,路径中带版本信息 4. 数据即是资源,均使用名词,尽量不出现动词(最核心的部分) 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 - patch请求:修改主键为1的书(局部修改一条) 'ps:patch用的不多,一般用put即可' https://api.baidu.com/books/1 - delete请求:删除主键为1的书(删除一条) 6. 在请求地址中带过滤条件 指定返回记录的数量 指定返回记录的开始位置 指定第几页,以及每页的记录数 指定返回结果按照哪个属性排序,以及排序顺序 指定筛选条件 7. 响应状态码: 1xx: 请求正在处理 2xx:成功响应 3xx:重定向 4xx:客户端错误 5xx: 服务端错误 '常见的需要记住' 响应状态码2XX 200:常规请求 201:创建成功 响应状态码3XX 301:永久重定向 302:暂时重定向 响应状态码4XX 403:请求无权限 404:请求路径不存在 405:请求方法不存在 响应状态码5XX 500:服务器异常 8. 返回数据中带错误信息 { code:0 msg:"ok/用户名错误" } 9. 返回的结果应该符合规范: GET 获取所有数据,返回资源对象的列表 GET 单个对象,返回单个资源对象 POST 新增对象, 返回新生成的资源对象 PUT 修改对象: 返回完整的资源对象 DElETE 删除: 放回一个空文档 10. 响应数据中带链接 响应数据要带有状态码,状态信息以及数据本身
django的生命周期请求流程
JWT的认证与签发
概念:
在用户注册或者登陆后,我们想要记住用户的登录状态,或者为用户创建身份认证的凭证,就是用Json Web Token(本质就是token)认证机制
构成和原理:
JWT就是一段字符串,由三段信息构成
# 第一部分: 头部 header -主要有两部分信息: 声明这是jwt 声明加密算法 通常直接使用 HMAC SHA256 然后加头部进行base64加密(该加密是可以对称解密的),构成了第一部分 # 第二部分: 荷载 payload -荷载就是存放有效信息的地方,像是承载的货品,主要包含三个部分: 1.标准中的注册声明 2.公共的声明 3.私有的声明 # 荷载: payload -存放有效信息的地方 - 过期时间 - 签发时间 -用户id -用户名字等 # 第三部分: 签证信息,签证信息由三部分组成: header(base64加密后的) payload(base64加密后的) secert 这个部分需要base64加密后的header和base64加密后的payload使用 . 连接组成的字符串,然后通过header中声明的加密方式进行 加盐secret组合加密,然后构成jwt的第三部分。
将这个三个部分用.
连接成完整的一个字符串,构成了最终的jwt
本质原理
jwt认证算法:签发与校验
签发: 根据登录提交来的 账号+密码+设备信息 签发token
校验: 根据用户端带token的请求 反解出 user 对象
drf项目的jwt认证开发流程(重点)
- 用账号密码访问登录接口,登录接口逻辑中调用 签发token算法,得到token,返回给客户端,客户端自己存到cookies中
- 校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户
注意:登录接口需要做 认证 +权限 两个局部禁用
不改变jwt登录认证的情况下,进行一个后台下线的操作。
维护用户会话信息。当用户登录时,生成一个随机的会话 ID,并将该会话 ID 存储在服务器端,同时将该会话 ID 与 JWT token 关联起来,可以使用 Redis 或 Memcached 等内存数据库来存储会话信息。
定期检查用户会话状态。在后端定期检查用户的会话状态,例如每隔 5 分钟检查一次。如果发现会话过期或会话 ID 无效,则将相应的 JWT token 设置为无效。
在需要下线用户时,将相应的会话 ID 标记为无效,或者将相应的 JWT token 标记为无效。这样,当用户再次发起请求时,就会发现自己的 token 无效,从而需要重新登录。
需要注意的是,如果用户在多个客户端登录,那么需要在服务器端记录所有的会话 ID,以便在需要下线用户时,同时下线用户在所有客户端上的会话。同时,为了提高安全性,可以在用户登录时生成一个 refresh token,用于更新 JWT token,从而避免 token 被盗用的风险。
base64 应用场景
''' 1 jwt 使用了base64 2 网络中传输数据,也会经常使用 base64编码 3 网络传输中,有的图片使用base64编码 ''' s='去网上找,比如12306的图片都是用base64加密过的,找到他的地址去掉前面两部分就能得到base64加密后的字符串' res=base64.b64decode(s) with open('a.png','wb') as f: f.write(res)
xadmin替换
内置功能丰富:Xadmin作为一款全面的后台管理系统框架,不仅提供了基本的CRUD功能,还内置了丰富的插件功能。包括数据导出、书签、图表、数据添加向导及图片相册等多种扩展功能。
action装饰器
1.视图类中的方法,会用到一些其他的名字,必须要使用action装饰器做映射
2.action中的一些参数:
- methods: 请求方式,列表
- detail: pk有关,默认是False, 控制生成的路由是:/user/login还是/user/pk/login
- url_path: 控制生成的/user/后面的路径是什么,如果不写,默认就是方法名
- url_name: 别名,用于反向解析
@action(methods=['POST',],detail=False, url_path='login') def login(self,request):
mysql严格模式
mysql的严格模式是一种设置,它使Mysql对插入、更新和创建表的操作更加严格。
如果出现数据插入错误或警告,mysql会拒绝执行操作并抛出错误,而不是默默的忽略错误或警告。严格模式有助于提高数据的完整性和一致性
可以在mysql配置文件中设置 sql_mode 参数来启用
strict Mode功能说明
不支持not full字段插入null值
不支持text字段有默认值
不支持对自增长字段插入''值
redis的架构
部署架构
四种模式 单机、主从、哨兵、集群
哨兵
集群
redis为什么这么快
运行在内存中,速度块,数据结构简单,操作节省时间
redis五大数据结构
redis是一种高级的key:value存储系统,其中value支持五大数据类型:
- 字符串(strings)
- 字符串列表(lists)
- 字符串集合(sets)
- 有序字符串集合(sorted sets)
- 哈希(hashes)
redis的持久化
持久化的方案
# 什么是持久化 redis的所有数据保存在内存中,把内存中的数据同步到硬盘上这个过程称为持久化 # 持久化的实现方式 快照:某时某刻数据的一个完成备份 -mysql的Dump -redis的RDB 写日志: 任何操作记录日志,要恢复数据,只要把日志重新走一遍即可 -mysql的 Binlog -Redis的 AOF
数据持久化:有备无患
第一步: redis每次执行写入操作,除了写内存之外,同时也写一份到磁盘上 问题: 客户端每次写操作,又要写内存,又要写磁盘,而写磁盘的好事相比于写内存来说,肯定要慢很多!这势必会影响到Redis的性能
最优化的:Redis写内润由主线程来做,写内存完成后就给客户端返回结果,然后Redis用[另一个线程] 去写磁盘,这样就可以避免主线程写磁盘对性能的影响
这种持久化方案,就是我们经常听到的 Redis AOF (Append Only File)
AOF方案
# aof是什么 客户端每写入一条命令,都记录一条日志,放到日志文件中,如果出现宕机,可以将数据完全恢复 aof的三种策略 日志不是直接写在硬盘上,而是先放在缓冲区,缓冲区根据一些策略,写到了硬盘上 always:redis–》写命令刷新的缓冲区—》每条命令fsync到硬盘—》AOF文件 everysec(默认值):redis——》写命令刷新的缓冲区—》每秒把缓冲区fsync到硬盘–》AOF文件 no:redis——》写命令刷新的缓冲区—》操作系统决定,缓冲区fsync到硬盘–》AOF文件 # 解决了数据实时持久化,我们还会面临另一个问题,数据实时写入 AOF,随着时间的推移,AOF 文件会越来越大,那使用 AOF 恢复时变得非常慢,这该怎么办? aof重写 -当体积越来越大的时候(超过设定阈值),redis就会重新定期写一份新的AOF,这个新的AOF只记录数据的最终版本
RDB方案
简单理解:
把redis想象着水杯,向redis里写入数据就相当于往被子里倒水,此时拿一个相机给这个水杯拍照片,拍照的这一瞬间,照片中记录到这个水杯的容量,就是水杯的数据快照
RDB(Redis DataBase缩写快照)是redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb 通过配置文件中的save参数来定义快照的周期;记录Redis数据库的所有键值对,在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件达到数据恢复;
优点:
- 只有一个文件dump.rdb,方便持久化;
- 容灾性好,一个文件可以保存到安全的磁盘中;
- 性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是io最大化,使用单独子进程来进行持久化操作,主进程不会进行任何IO操作,保证了redis的高性能效率更高。
缺点:
- 数据安全性低;
- RDB是间隔一段时间内进行持久化,如果在持久化时redis发生故障,会产生数据丢失。所以这种方式在数据不严谨的时候更适合
持久化方案选择:
- AOF文件对RDB更新频率高,优先使用AOF还原数据;
- AOF比RDB更安全也更强大;
- RDB性能比AOF好
- 业务对数据完整性要求较高,选AOF,如果两个都配了优先加载AOF
在优化,既可以保证数据完整性,还能让持久化文件体积更小,恢复更快呢
回顾下 RDB和AOF各自的特点:
- RDB 以二进制 +数据压缩方式存储,文件体积小
- AOF记录每一次写命令,数据最全
想要完整性更高: 就不能只使用RDB,重点还是要放在AOF有划伤。
具体来说,当AOF在做重写时,Redis先以RDB格式在AOF文件中写入一个数据快照,再把这期间产生的每一个写命令,追加到AOF文件中
一个实例宕机,只能用恢复数据来解决,那我们是否可以部署多个Redis实例,然后让这些实例数据保持实时同步,这样当一个实例宕机时,我们在剩下的实例中选择一个继续提供服务就好了
这个方案就是 主从复制:多副本
RDB和AOF的优缺点
主从复制:多版本
为什么需要主从
会出现以下问题: 1. 机器故障 2. 容器瓶颈 3. qps瓶颈
把实时读写的节点叫做master,实时同步数据的节点叫做 slave。
app -----读写------ Redis master | 实时同步 | | Redis slave
采用多副本的方案,它的优势是:
1.缩短不可用时间:master宕机时,我们可以手动把
消息队列
解耦、异步、削峰
解耦:将系统按照不同的业务功能拆分出来,消息生产者只管把消息发布到 MQ 中而不用管谁来取,消息消费者只管从 MQ 中取消息而不管是谁发布的。消息生产者和消费者都不知道对方的存在;
异步:主流程只需要完成业务的核心功能;对于业务非核心功能,将消息放入到消息队列之中进行异步处理,减少请求的等待,提高系统的总体性能;
削峰/限流:将所有请求都写到消息队列中,消费服务器按照自身能够处理的请求数从队列中拿到请求,防止请求并发过高将系统搞崩溃;
互斥锁
互斥锁:对共享数据进行锁定,保证同一时刻只能有一个线程去操作。
注意:互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的需要等待,等互斥锁使用完释放后,其他等待的线程再去抢这个锁
总结:
- 互斥锁的作用就是保证同一时刻只能有一个线程去操作共享数据,保证共享数据不会出现错误问题。
- 使用互斥锁的好吃确没关键代码只能由一个线程从头到尾完整的去执行
- 使用互斥锁会影响代码的执行效率,多任务改成了单任务执行
- 互斥锁没用好的情况下会出现死锁
Websocket协议
websocket是一种在单个Tcp连接上进行全双工通信的协议
特点:
建立在tcp协议之上,服务端的实现比较容易
默认端口也是80和443,
sql的注入
注入产生的原因是后台服务器在接受相关参数时为做好过滤直接带入到数据库中查询,导致可以拼接sql语句
解决:
-
过滤输入内容,校验字符串
-
参数化查询
参数化查询目前被视作是预防 SQL 注入攻击最有效的方法。参数化查询是指在设计与数据库连接并访问数据时,在需要填入数值或数据的地方,使用参数(Parameter)来给值。
-
安全测试、安全审计
-
避免使用动态sql
-
不要将敏感数据保留在纯文本中
-
限制数据库权限和特选
xss脚本攻击
csrf跨域请求伪造
什么是跨域?
简单来说,就是我在一个站点向另一个站点发送了请求(ajax或者链接),只要这两个站点HTTP协议、域名或是端口中有一个不一样,说明发送了跨域。
跨域解决:
Jsonp:Jsonp是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。
原因:同源策略会阻止ajax请求;不阻止具有src属性的标签
CORS:设置响应头
Nginx负载均衡
实现负载均衡的方式很多种,常见几种:
- 轮询:默认的负载均衡算法,将请求一次分发给不同的后端服务器
- 最少连接: 将请求分配给当前连接数最少的后端服务器。
- IP哈希(IP Hash):根据请求的IP地址进行哈希,然后将请求分发给对应的后端服务器,这种方式可以保证同一IP的请求都被分发到同一台后端服务器。
- 权重(Weight):可以为每个后端服务器设置一个权重值,根据权重值进行请求的分配,可以根据服务器的性能或者硬件配置等因素设置不同的权重。
- URL哈希(URL Hash):
dockerfile
FROM: 指定基础镜像 RUN: 构建镜像过程中需要执行的命令。可以有多条。docker build CMD:添加启动容器时需要执行的命令。多条只有最后一条生效。可以在启动容器时被覆盖和修改。 ENTRYPOINT:同CMD,但这个一定会被执行,不会被覆盖修改。 MLABELAINTAINER:表明镜像的作者。将被遗弃,被LABEL代替。 EXPOSE:设置对外暴露的端口。 ENV:设置执行命令时的环境变量,并且在构建完成后,仍然生效 ARG:设置只在构建过程中使用的环境变量,构建完成后,将消失 ADD:将本地文件或目录拷贝到镜像的文件系统中。能解压特定格式文件,能将URL作为要拷贝的文件 COPY:将本地文件或目录拷贝到镜像的文件系统中。 VOLUME:添加数据卷 USER:指定以哪个用户的名义执行RUN, CMD 和ENTRYPOINT等命令 WORKDIR:设置工作目录 ONBUILD:如果制作的镜像被另一个Dockerfile使用,将在那里被执行Docekrfile命令 STOPSIGNAL:设置容器退出时发出的关闭信号。 HEALTHCHECK:设置容器状态检查。 SHELL:更改执行shell命令的程序。Linux的默认shell是[“/bin/sh”, “-c”],Windows的是[“cmd”, “/S”, “/C”]。
基础镜像(FROM):定义 Dockerfile 中构建镜像的基础镜像,用于从该基础镜像开始构建镜像。 维护者信息(MAINTAINER):定义 Dockerfile 的维护者信息。 安装软件和依赖项(RUN):定义在容器中安装软件和依赖项的命令。 容器内部的工作目录(WORKDIR):定义在容器内部设置的工作目录。 拷贝文件(COPY、ADD):将本地文件或目录复制到容器中,可以使用 COPY 或 ADD 指令。 暴露端口(EXPOSE):定义容器的对外开放的端口。 执行命令(CMD、ENTRYPOINT):定义容器启动时要执行的命令或程序。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)