面试题

可变类型和不可变类型

  可变类型:列表 字典 集合
  不可变类型:整型 浮点型 字符串 数组  

常用的魔法方法

 魔法方法就是在某种情况下会自动触发
  __init__  类()---->对象进行实例化
  __new__   类()---->产生一个空对象---->触发__init__完成对象实例化 并且有返回值 返回实例化好的对象
*** 两者的区别是:当实例化一个类的时候最先被调用的方法是__new__方法,__init__是在类实例被创建出来之后调用的,它完成的是类实例的初始化操作,而__new__方法是创建这个类实例的方法


  __call__  对象加括号调用时则会触发
  __str__   对象执行打印操作会触发 这个方法返回什么打印结果就是什么 并且这个方法必须返回一个字符串类型的数据  
    
  __getattr__  对象.属性 属性不存在 则会触发
  __setattr__  对象.属性=值 则会触发
    
  __getitem__  对象['属性'],属性不存在 则会触发
  __setitem__  对象['属性']=值  则会触发
    
    
# 双下enter(enter) 和 双下exit(exit)必须一起使用 要不会报错
  __enter__  当对象被当作with上下文管理操作的开始自动触发,并且这个方法返回什么,as后面的变量就会收到什么
  __exit__   当对象参与的with上下文管理语法执行完之后自动触发(子代码结束)

-上下文管理器:只要重写了__enter__和__exit__方法,就具备这个能力
  with 对象 as xx:
        1 写了一行代码,触发__enter__的执行
  2 写了一行代码,触发__exit__,做一些资源清理工作

类中的装饰器用过哪些

 如果类中不加任何装饰器,是绑定给对象的方法,对象来调用,对象 点 直接调用,会自动把对象传进去
    classmethod  绑定给类的方法,类来调,会自动把类传进去,对象也可以调
    staticmethod 不绑定给任何东西,也叫静态方法,类可以调 对象也可以调,它们不会自动传值
   
# 如何把方法包装成数据属性
    property装饰器 
加property装饰器的功能就是将函数的返回值作为类的属性,方法伪装成属性
 意思就是我们调用这个被装饰的函数不需要加括号去运行,而是直接像变量或者属性一样获取这个值

类中如何隐藏属性

  __属性,方法

高匿代理和透明代理

# 高匿代理和透明代理
-高匿:服务端拿不到真实客户端的ip地址
-透明:服务端能拿到真实客户端的ip地址

后端如何拿到真实客户端ip地址

X-Forwarded-For(XFF):获取HTTP请求真实的IP(作用)
    
通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段

什么是http

超文本传输协议 信息是明文传输
是一个简单的请求——响应协议
设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。它可以使浏览器更加高效

http版本区别
0.9:底层基于tcp,每次http请求,都是建立一个tcp连接,三次握手,请求结束需要四次挥手
1.1:请求头中有个参数Keep-alive,可以保证多个http请求公用一个TCP连接
2.x:多路复用,多个请求使用同一个数据包

http和https的区别

hhtp是http+ssl/tls
http:
    超文本传输协议,是互联网上应用最为广泛的一种网络协议
    
https:
    是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全
    SSL 协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。SSL 协议可分为两层:
    SSL 记录协议,它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。
    SSL 握手协议,它建立在 SSL 记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等
    
区别:
  1 https协议需要CA申请证书,一般免费证书较少,因而需要一定费用
  2 http是超文本传输协议,信息是明文传输,https则是具有安全性的SSL加密传输协议
  3 连接方式不同,http连接是无状态 https是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议 比http安全
  4 http和https使用的是完全不同的连接方式,用的端口的也不一样,http是80,https是443
  4 http页面响应速度比https快,因为http使用TCP三次握手建立连接,客户端和服务器需要交换 3 个包,而https除了TCP的三个包,还要加上ssl握手需要的 9 个包,所以一共是 12 个包

http请求头

x-forword-for,user-agent(用户代理),cookie,referer(告诉服务器该网页是从哪个页面链接过来的),content-Type(编码)

http协议

HTTP协议:是W3C制定的一种超文本传输协议
         底层是TCP/IP

-请求协议:
    请求首行:请求头地址,请求方式,http的版本
    请求头:key-value
    请求体
   
-响应协议:
    响应首行:响应状态码,响应字符串描述
    响应头:key-value,响应状态码,cookie
    响应体

http协议特性

1 基于TCP/IP协议之上的应用层协议

2 基于请求/响应模式

3 无状态保存

4 无连接

http协议详情

特点
 1 基于请求响应--->服务端不能主动给客户端推送消息--->websocket协议
 2 无状态无连接--->不能做会话保持--->才出现了cookie,session,token
 3 基于tcp之上的应用层协议
  -详情:
    	-请求协议:
        	请求首行:请求方式(get,post,delete),请求地址,请求http协议版本号/r/n
            请求头:key:value   (cookie,useragent,referer,x-forword-for)
            请求体:编码方式
        -响应协议:
        	响应首行:响应状态码(1xx 表示正在处理一般看不到,2xx 表示请求处理成功,3xx 重定向,4xx 客户端错误,5xx服务端错误)
            响应头:key:value    跨域问题的响应头
            响应体: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协议来实现   

websocket协议

# 如何实现服务器给客户端发送消息,websocket是什么?用过吗
websocket:
    也是一种通信协议,区别于http协议的是可以单个TCP连接上进行双工通信。允许服务端主动向客户端推送数据。浏览器和服务器只需要完成一次tcp连接,两者就可以建立持久性的连接,并进行双向数据传输
    没有跨域问题
    应用场景:
       只要涉及到服务端主动给客户端发送消息的场景,都可以用
       实时聊天:之前做的项目没有这方面的业务需求
       服务器发生变化,主动向客户端推送一个通知
        
        
django中使用channles模块实现

http的get请求和post请求的区别

POST 和GET本质都是一样的

最直观的区别就是get把参数包含在url中
post通过request body传递参数
你要给get加上request body,给post带上url参数,技术上是完全行的通的

1 get产生一个TCP数据包,post产生两个TCP数据包
  对于get方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据)
  而对于post,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200(返回数据)

2 get请求在浏览器刷新或者退出的时候是无害的,post的话数据会被重新提交

3 get可以存在缓存中,而post不行

4 get请求无法发送大量数据,而post可以

5 get请求只能进行url编码,而post支持多种编码方式

6 对参数的数据类型,get只接受ASCII字符,而post没有限制

7 get比post更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息

8 get可以被书签收藏,post不行

面试官要看项目

看的是你的编码水平

公司的看不了,给他看的都是个人项目
公司项目看不了,签了保密协议

数据库如何处理的?

-云数据库:阿里云数据库,花钱买的服务--->给账号密码--->公司不需要自己搭建mysql
    -MySQL
    -Reids
    -MogoDB
    
-自己的数据库,部署在云服务器上的数据库,是你自己的

你用过什么云产品?

阿里云的ecs,服务器
阿里云的oss,对象存储
云短信
七牛云 文件存储

看看你的数据库

-配置文件 dev.py  连的是本地的127.0.0.1

因为是自己的项目写一些小项目想用来开源 连的都是本地 自己做测试用的

上线怎么弄

不知道,公司就是给我一个地址和端口号,账名和密码不知道
账号和密码是在环境变量里去取的 这是他们上线的人才知道的 我们是不知道的

上线的数据库服务和项目服务是在同一台机器上吗?

不是的
django+uwsgi服务 一台
MySQL服务 单独一台 我们使用地址和端口号去链接 账号和密码是在环境变量里去取的 这是他们上线的人才知道的 我们是不知道的
redis 一台
前端服务 一台


是的
我们用户量不大 所有服务都在一台机器上 虽然这样不好 但公司就是这么用的 因为公司可能没有吧

celery用过吗?

用过
异步任务
延迟任务
定时任务

是可以用来做来定时任务,但是我们一般不采纳,因为celery太过庞大,就像用django写小脚本一样  

celery异步框架的架构(执行流程)

提交任务放在消息队列中,worker从消息队列里去拿任务执行,执行完将结果存到结果存储器中,worker将执行结果返回给客户端,客户端可以通过查询结果存储器来获取任务执行状态和结果

celery跟用的框架没有必然联系,但它能够解决我这个框架如果需要异步、延迟、定时这种任务,可以帮其他框架解决这种问题

APScheduler定时任务框架

如果只是起个定时任务的话,不太需要celery框架,因为它有点大材小用了,用APScheduler就可以了

APScheduler是一个轻量级的python定时任务框架,可以用来执行定时任务或者周期性任务
定义调度器然后将任务添加到调度器中,调度器通过start()方法启动

深浅拷贝

浅拷贝:
    会在内存中新开辟一个空间,存放这个拷贝的列表,但是列表里面的内容还是沿用之前对象的内存地址
    
深拷贝:
    会在内存中开辟新空间,将原列表以及列表里面的可变数据类型重新创建一份,不可变数据类型则沿用之前的
    
总结:
   1 浅拷贝花费时间少,占用的内存少,只拷贝顶层数据,拷贝效率高
   2 对不可变对象拷贝时,浅拷贝和深拷贝的作用是一致的,不开辟新空间,相当于赋值操作
   3 可变对象浅拷贝时,只拷贝第一层中的引用,如果元素是可变对象,并且被修改,那么拷贝的对象也会发生变化
   4 可变对象深拷贝时,会逐层进行拷贝,遇到可变类型,就开辟一块内存复制下来

什么是生成器,有什么应用场景?

生成器是一个特殊的程序,可以被用作控制循环的迭代行为,是一边循环一边计算的机制,称为generator
生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器

生成器的特点:
    1 节约内存
    2 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,就是说,在整个所有函数调用参数都是第一次所调用时保留的,而不是新创建的
    3 send能穿值,能改变值
    4 yield会阻塞,下次调用会接着上面的未执行的接着执行
    
应用场景:
    由于可以使用生成器很方便地实现一个迭代器,因此迭代器适用的场景生成器几乎都适用
    进行大文件的读取时,多任务,python的协程

双写一致性

 1 修改数据库 更新缓存
 2 修改数据库 删除缓存
 3 定时更新

断点续传

# 概念
  客户端的断点续传指的是在下载或者上传文件的时候,将下载或者上传的文件人为的划成几个部分,每一个部分采用一个线程进行上传或者下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没不要从头再下载,可以节省时间提高速度
    
  -迅雷多线程下载


# 详细
大文件切片上传:
想要上传大文件,那么就需要将大文件进行切片,分为一块一块的小文件,将小文件分别发送给后台,后台接收到小文件后,将小文件存储起来,当所有小文件上传成功后,再将小文件合并到一个文件中,这样就完成了大文件切片上传
    
断点续传:
想要实现断点续传,那么首先实现大文件上传。当我们将文件切片,除了需要给文件对应的值(使用md5)外,还需要给每个小文件对应的编号,将md5和编号同时传送给后端,后端根据md5和编号进行小文件存储,当发生文件上传一半终止上传后,获取后端已经上传的小文件编号发送到前端,前端检测到后端发送的小文件编号,从对应编号的切片进行上传

内网穿透

内网穿透就是使用一台有公网 IP 的电脑(frp服务器,下文称为节点)作为 “中间人” 来与没有公网IP的电脑建立连接并转发数据


https://zhuanlan.zhihu.com/p/370483324

缓存穿透,击穿,雪崩

# 缓存穿透
  是指查询一个缓存中和数据库中都不存在的数据,导致每次查询这条数据都会透过缓存,直接查库,最后返回空。当用户使用这条不存在的数据疯狂发起查询请求的时候,对数据库就造成的压力就非常大,甚至可能直接挂掉

# 解决缓存穿透的方法一般有两种:
 第一种:缓存空对象
    就是当数据库中查不到数据的时候,缓存一个空对象,然后给这个空对象的缓存设置一个过期时间,这样下次再查询该数据的时候就可以直接从缓存中拿到,从而达到减少数据库压力的目的
  缺点:
    1 需要缓存层提供更多的内存空间来缓存这些空对象,当空对象很多的时候就会浪费很多内存
    2 会导致缓存层和存储层数据不一致

 第二种:使用布隆过滤器(推荐方法)
    就是一种数据结构,它是由一个长度为m bit的位数组与n个hash函数组成的数据结构,位数组中每个元素初始值都为0,在初始化布隆过滤器时,会先将所有key进行n次hash运算,这样就可以得到n个位置,然后将这n个位置上的元素改为1,这样,就相当于把所有的key保存到了布隆过滤器中了
    
布隆过滤器:
   bloomfilter:是一个通过多哈希函数映射到一张表的数据结构,能够快速的判断一个元素在一个集合内是否存在,具有很好的空间和时间效率。(典型例子,爬虫url去重)
    

    
# 缓存击穿
  是指当缓存中某个热点数据过期了,在该热点数据重新载入缓存之前,有大量的查询请求穿过缓存,直接查询数据库。这种情况会导致数据库压力瞬间骤增,造成大量请求阻塞,甚至直接挂掉
    
# 解决缓存击穿的方法也有两种:
 第一种:设置key值永不过期
    在设置热点key的时候,不给key设置过期时间即可,还有另外一种方式也可以达到key不过期的目的,就是正常给key设置过期时间,在后台同时启一个定时任务去定时更新这个缓存
    
 第二种:使用分布式锁
     使用了加锁的方式,锁的对象就是key,这样,当大量查询同一个key的请求并发进来时,只能有一个请求获取到锁,然后获取到锁的线程查询数据库,然后将结果放入到缓存中,然后释放锁,此时,其他处于锁等待的请求即可继续执行,由于此时缓存中已经有了数据,所以直接从缓存中获取到数据返回,并不会查询数据库

    

# 缓存雪崩
  是指当缓存中有大量的key值在同一时间过期,或者Redis直接宕机了,导致大量的查询请求全部到达数据库,造成的数据库查询压力骤增,甚至直接挂掉

# 缓存雪崩解决方案
 第一种:key值过期时间打散
    将每个key值的过期时间打散那,使它们失效点尽可能均匀分布
    
 第二种:redis发生故障的情况
    署redis时可以使用redis的几种高可用方案部署
     -redis主从架构
     -redis哨兵机制
     -redis集群

http/2多路复用

HTTP/1下的请求,并不能很好的利用宽带,一个TCP连接同时只能有一个HTTP请求和响应,如果正在发送一个HTTP请求,那其他的HTTP请求就得排队
这种排队会产生一个请求队列,当队头的请求发生意外(比如丢包、服务器响应缓慢),导致比平时要慢得多,就会导致后面的请求被延迟。这种情况我们称为 队头阻塞


为了解决这个问题HTTP/2采用了多路复用

在HTTP/2中,有两个非常重要的概念,分别是帧(frame)和流(stream)
帧代表着最小数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流
HTTP/2采用二进制数据帧传输,取代了HTTP1.x的文本格式,二进制格式解析更高效

# 多路复用
多路复用代替了HTTP1.x的序列和阻塞机制,所有相同的域名请求都通过同一个TCP连接并发完成
同一TCP中可以发送多个请求,对端通过帧中的标识知道属于哪个请求,通过 这个技术,可以避免HTTP旧版本中的队头阻塞问题,极大的提高传输性能

IO多路复用


数据库三大范式

1 . 每一个字段的值必须具有原子性,字段是最小的单元不可再分

第一范式(1NF):是指数据库表的每一列都是不可分割的

​ 表中不能嵌套表,要保证数据不可再分

​ 例如:姓名,可以分为 姓 和 名 这是不允许的

  1. 表中字段必须完全依赖于全部主键而非部分主键

第二范式(2NF):如果表是单主键,那么主键以外的列必须完全依赖于主键,如果表是复合主键,那么主键以外的列必须完全依赖于主键,不能仅依赖主键的一部分

​ 任意 一个字段都只能依赖表中的同一个字段

​ 例如:一个表中有学生ID和学生姓名,班级ID和班级名称 这个就不符合第二范式

  1. 非主键外所有字段都必须互不依赖

第三范式(3NF):表中的非主键列必须和主键之间相关而不能间接相关,也就是说:非主键列之间不能相关依赖

​ 一张表最多存在在2层同类型信息

​ 例如:一张表中有学生编号和学生名称,有班级ID和院系名称,那么学生可以确定班级,班级可以确定院系,那么院系和学生有传递依赖,所以不符合第三范式

MySQL有哪些索引类型,分别有什么作用

索引:索引是提高检索速度的

MySQL有哪些类型的索引

目前MySQL主要有FULLTEXT,HASH,BTREE,RTREE

FULLTEXT

FULLTEXT全文检索,目前只有MySAM引擎支持

这类针对于文本的模糊查询效率较低问题

HASH

由于HASH的唯一及类似键值对的形式

可以一次定位,不需要像树形索引那样逐层查找,因此具有较高的效率。但是这种高效是有条件的,即只在'='和'in'条件下高效,对于范围查找、排序及组合索引仍然效率不高

BTREE

BTREE索引就是一种将索引值按一定的算法,存入一个树形结构中,每次查询都是从树的入口开始遍历。这是mysql里默认和最常用的索引类型

RTREE

RTREE在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储只有MySAM、BDb、innoDb,NDb、Archive几种。相对于BTREE,RTREE的优势在于范围查找

索引种类

聚簇索引,聚集索引,主键索引,主键,如果不存在主键,隐藏一个主键,构建聚簇索引:

加速查询 + 列值唯一(不可以有null) + 表中只有一个组合索引,其效率大于索引合并

辅助索引 普通索引 index:

仅加速查询

唯一索引 unique:

加速查询+列值唯一

联合索引,组合索引,多列索引:unique_to

常见的索引数据结构

哈希表,有序数组,二叉树

哈希表
  对索引的key进行一次hash计算就可以定位出数据存储的位置
  很多时候hash索引要比B+Tree索引更高效

有序数组
  有序数组在范围查找中可以使用二分法,能很大程度的缩短查询时间
    
二叉树
  右侧元素大于父元素数据,左侧数据小于父元素数据
    
mysql使用的是b-Tree
  b-Tree:
        非叶子节点不存储data,只存储索引,目的是可以放更多的索引
        叶子节点包含所有索引字段
        叶子节点使用指针连接,提高期间访问的性能

事务 事务特性和隔离级别

什么是事务

在MySQL中事务是一种机制,一个操作序列,是访问和更新数据库的执行单元,事务里会包含一个或多个数据库的操作命令,当所有命令作为一个整体一起向系统提交或者撤销操作请求的时候,那么这一组数据库的命令要都执行,要么都不执行,这就牵扯到了事务的四大特性,这就是原子性

事务的特性

1. 原子性
   就是事务所包含的所有操作,要么全部成功,要么全部失败
2. 一致性
   事务执行前和执行后,必须保持一致
3. 隔离性
   一个事务执行的过程中,并不能被别的事务干扰
4. 持久性
   事务被提交后,对数据的修改是永恒的,即使是出现故障也能够正常保持

事务的隔离级别

1. Serialzable(可串行化):可避免脏读、不可重复读、幻读的发生
2. Repeatable(可重复读):可以避免脏读、不可重复读的发生
3. Read committed(读已提交):可避免脏读的发生
4. Read uncommited(读未提交):最低级别,任何情况都无法保证

MySQL默认的是 Repeatable,oracle默认的是Read commited

MySQL支持四种隔离级别,而oracle支持Serialzable和Read committed这两种级别

MVCC机制

  MVCC是一种并发控制技术,主要用于解决多用户、多事务同时访问数据库时可能出现的数据不一致问题。MVCC机制基于版本号的概念,每个事务执行时都可以看到一个特定版本的数据,而这个版本号是根据该数据的更新时间戳等时间计算得出的。当多个事务并发执行时,MVCC机制会根据事务的隔离级别和锁的类型来确定返回哪个版本号的数据

脏读

脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并不一定最终存在的数据,这就是脏读

不可重复读

不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况

例如:
  事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致

幻读

幻读并不是说两次读取获取的结果集是不同的,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作

更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读

查的时候明明没有这条记录,但插入的时候 却告诉我 主键冲突,这就好像幻觉一样。

这才是所有的幻读。 不可重复读侧重表达 读-读,幻读则是说 读-写,用写来证实读的是鬼影

MVCC机制

  MVCC是一种并发控制技术,主要用于解决多用户、多事务同时访问数据库时可能出现的数据不一致问题。MVCC机制基于版本号的概念,每个事务执行时都可以看到一个特定版本的数据,而这个版本号是根据该数据的更新时间戳等时间计算得出的。当多个事务并发执行时,MVCC机制会根据事务的隔离级别和锁的类型来确定返回哪个版本号的数据

MySQL可重复读如何解决了幻读问题?

MVCC机制通过多版本的管理方式,可以在同一事务中多次读取同一数据时,返回相同的结果集,从而解决了幻读问题

具体来说:
  当一个事务在读取数据时,NVCC机制会根据该事务开始时间计算出可见的数据版本号范围,只返回版本号在该范围内的数据,而对于正在进行的并发修改操作所创建的数据,在该事务的隔离级别下是不可见的。因此,即使其他事务在同一时刻修改了数据,也不会影响当前事务的读取结果,从而避免了幻读的发生

mysql5.7以后默认隔离级别是什么?

REPEATABLE-READ(可重复读)
在开始读取数据(事务开启)时,不再允许修改操作,这样就可以在同一个事务内两次读到的数据是一样的,因此称为是可重复读隔离级别,但是有时可能会出现幻读

互斥锁

多线程并发下保证数据安全,如果多线程情况下,不加锁,可能会出现数据错乱,所以需要互斥锁的存在,只要在线程执行时都要拿到互斥锁才能执行,以牺牲效率从而提升数据安全

悲观锁和乐观锁,如何实现

	-无论是悲观锁还是乐观锁,都是人们定义出来的概念,仅仅是一种思想,与语言无关
    -并发控制:
    	-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中使用乐观锁

什么是qps,tps,并发量,pv,uv

1 QPS

Queries Per Second,每秒查询率,一台服务器每秒能够响应的查询次数

2 TPS

Transactions Per Second,是每秒处理的事务数,包括一条消息入和一条消息出,加上一次用户数据库访问

3 并发量

系统同时处理的请求或事务数,可以直接理解为:系统同时处理的请求数量

4 PV

PV(Page View):页面访问量,即页面浏览量或点击量,用户每次刷新即被计算一次。可以统计服务一天的访问日志得到。

5 UV

UV(Unique Visitor):独立访客,统计1天内访问某站点的用户数。可以统计服务一天的访问日志并根据用户的唯一标识去重得到。

什么是接口幂等性问题,如何解决?

什么是接口幂等性问题

什么是幂等性?
幂等性是系统服务对外一种承诺,承诺只要调用接口成功,外部多次调用对系统的影响是一致的。声明为幂等的服务会认为外部调用失败是常态,并且失败之后必然会有重试

接口幂等性
接口幂等性就是用户对同一操作发起了一次或多次请求的对数据的影响是一致不变的,不会因为多次的请求而产生副作用

什么是接口幂等性问题?
接口幂等性问题是指在多次调用同一个接口时,可能会产生重复的操作,导致系统数据异常或出现重复操作的情况

解决接口幂等性问题

token机制

1、服务端提供了发送token的接口。我们在分析业务的时候,哪些业务是存在幂等问题的,就必须在执行业务前,先去获取token,服务器会把token保存到redis中。

2、然后调用业务接口请求时,把token携带过去,一般放在请求头部。

3、服务器判断token是否存在redis中,存在表示第一次请求,然后删除token,继续执行业务。

4、如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执行。

关键点 先删除token,还是后删除token

后删除token:如果进行业务处理成功后,删除redis中的token失败了,这样就导致了有可能会发生重复请求,因为token没有被删除。这个问题其实是数据库和缓存redis数据不一致问题,后续会写文章进行讲解。

先删除token:如果系统出现问题导致业务处理出现异常,业务处理没有成功,接口调用方也没有获取到明确的结果,然后进行重试,但token已经删除掉了,服务端判断token不存在,认为是重复请求,就直接返回了,无法进行业务处理了。

先删除token可以保证不会因为重复请求,业务数据出现问题。出现业务异常,可以让调用方配合处理一下,重新获取新的token,再次由业务调用方发起重试请求就ok了。
token机制缺点
业务请求每次请求,都会有额外的请求(一次获取token请求、判断token是否存在的业务)。其实真实的生产环境中,1万请求也许只会存在10个左右的请求会发生重试,为了这10个请求,我们让9990个请求都发生了额外的请求。

乐观锁机制

这种方法适合在更新的场景中
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场景时幂等问题。但主键的要求不是自增的主键,这样就需要业务生成全局唯一的主键。

如果是分库分表场景下,路由规则要保证相同请求下,落地在同一个数据库和同一表中,要不然数据库主键约束就不起效果了,因为是不同的数据库和表主键不相关。

防重表

使用订单号orderNo做为去重表的唯一索引,把唯一索引插入去重表,再进行业务操作,且他们在同一个事务中。这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。

唯一ID

调用接口时,生成一个唯一id,redis将数据保存到集合中(去重),存在即处理过。

**唯一ID机制 **

调用者生成一个唯一ID

前端

前端:按钮只能点击一次

什么是gil锁,有什么作用

首先明确gil并不是python的特性,它是在实现python解释器(Cpython)时引入的一个概念

GIL:全局解释器锁
    GIL本质也是一把互斥锁 
    作用:用来阻止同一个进程内多个线程同时执行
    原因:因为CPython解释器中的垃圾回收机制不是线程安全的
    
    GIL只能够确保同进程内多线程数据不会被垃圾回收机制弄乱 
	并不能确保程序里面的数据是否安全

python的垃圾回收机制是什么样的

Python的垃圾回收,其实高级的语言都有自己的垃圾回收机制简称GC

Python 的GC模块主要运用了
     引用计数来跟踪和回收垃圾,通过“标记-清除”解决容器对象可能产生的循环引用问题,通过分代回收以空间换时间进一步提高垃圾回收的效率

也就是采用“引用计数“为主(实时性,一旦没有引用,内存就直接释放了),“标记-清除”与“分代收集”两种机制为辅的策略 

引用计数

为每一个对象维护一个引用计数器,当一个对象的引用被创建或者复制时,(对象的引用)计数器+1,当一个对象的引用被销毁时,计数器的值-1,当计数器为0时,就意味着对象已经再没有被使用了,可以将其内存释放掉

标记清除

标记清除的出现打破了循环引用,也就是它只关注那些可能会产生循环引用的对象,当内存占用达到临界值的时候,程序会自动停止,然后扫描程序中所有的数据,并给只产生循环引用的数据打上标记,之后一次性清除

分代回收

活的越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率

解释为什么计算密集型用多进程,io密集型用多线程

计算密集型:又称cpu密集型
    系统运行CPU读写I/O(硬盘/内存)时可以在很短的时间内完成,几乎没有阻塞(等待I/O的实时间)时间,而CPU一直有大量运算要处理,因此CPU负载长期过高
    
io密集型
    系统运行多是CPU在等I/O (硬盘/内存)的读写操作,此类情景下CPU负载并不高
    

# 为什么计算密集型用多进程?
    -由于GIL锁的存在,即便是多核机器,同一时刻,也只能有一个线程在执行
    -线程需要cpu去调度执行
    -如果开了多线程,是计算密集型,计算是消耗cpu,假设是四核电脑,不能充分利用这四个核,只能有一个核在执行,开多线程没有用
    -而如果计算密集型,开了多进程,gil是在cpython解释器进程中的,再进程中开启线程执行计算,可以充分利用多核优势
    -开了一个进程,就开了一个cpython解释器的进程,就会有一个gil锁
    -由于GIL锁的存在,如果是计算密集型,开启多线程,不能利用多核优势,
    -开启多进程,可以利用多核优势
    
    但也分为单个cpu和多个cpu的情况
    单个cpu的情况的话还是多线程有优势 消耗的资源较少 多进程还要申请额外的空间,会消耗更多的资源
    但多个cpu的话多进程是完胜的,总耗时是单个进程的耗时,因为多个进程同时进行,多线程的话就是多个进程的综合时间
 


# 为什么io密集型用多线程?
   假设我们有多个线程都在发网络请求(request,response),一个请求的从发出到接收的过程中cpu大多时间都是在等。
所以,当前线程发出请求后,由于不占用cpu资源,可以阻塞等待,然后cpu执行权可以被另外一个线程所享有去发网络请求
当线程等待时间所占比例越高,需要越多线程,启用其他线程继续使用CPU,以此提高CPU的利用率
   本身开启进程是非常消耗资源的,如果是io密集型,没有必要开多进程,并不会有显著的效率提升

进程

进程是资源分配的最小单位,一个应用程序运行,至少会开一个进程

代码实现

使用multiprocessing模块
Process类

方式一:
    用函数创建进程
    process加括号产生一个对象
    对象点start()

方式二:
    自己写一个类继承Process
    重写run方法
    用自己的类实例化得到一个对象
    然后start()

线程

线程是cpu调度的最小单位,cpu执行的最小单位

代码实现

使用threading模块
Thread类


方式一:
    用函数创建进程
    Thread加括号产生一个对象
    对象点start()

方式二:
    自己写一个类继承Thread
    重写run方法
    用自己的类实例化得到一个对象
    然后start()

协程

单线程下实现并发,代码层面遇到io,自己切换

代码实现

早期之前:借助于第三方gevent,基于greelet写的
    
async 和 await 关键字,不借助于第三方,开启协程asyncio 包
   -必须写在一个函数前, async def task()--->这个函数执行的结果是协程函数
   -await 只要是io操作的代码,前面必须加 await

什么是猴子补丁,有什么用途

猴子补丁(Monkey Patch)
  在程序运行的过程中,动态替换的一种技术
    
1 gevent--->猴子补丁--->monkey.patch_all()--->动态的替换内部 会阻塞的代码
    time
    socket
    同步代码:io操作,会阻塞,会释放gil锁,这条线程就会被释放掉cpu的执行
    异步代码:遇到io,不释放gil锁
    把所有内置的会释放gil锁的模块,动态替换称gevent自己写的,不会释放gil锁的模块
    动态的把所有同步的代码,都替换成异步代码,把所有会阻塞的代码,都替换
    
2 比如咱们的json模块,用内置的json效率低,有一个第三方的ujson模块
    -不改变原来程序代码,把程序中所有json都替换成ujson
    -在程序入口处:import ujson as json
    
3 django中 pymysql的替换
   import pymysql
   pymysql.install_as_mysqlDB()

进程、线程、协程 你在哪里用过

  -我一般遇到计算密集型操作,我会开多进程,io密集型的操作我一般开多线程
  -闲来无事,爬取别人的数据,喜欢开多线程,爬虫的io居多
  -程序中,异步做一件事情,也可以开多线程
     比如一个视图函数,异步的把数据写进文件中
     异步的发送钉钉通知
     异步的发送邮件
  -但实际上,在项目中,不需要我们开启进程线程,可以借助于第三方框架比如celery就可以做异步操作
   而celery的worker,就是进程线程架构
  -django框架,是支持并发的,我们没有开启多进程和多线程,只要是符合uwsgi的,web服务器在进入django框架之前,开启了进程和线程来执行视图函数

为什么有了gil锁还要互斥锁

GIL只能保证进程内多线程数据不会被垃圾回收机制弄乱并不能确保程序里面的数据是否是安全的

gil锁:
   全局解释器锁,线程要执行,必须先获得到gil锁,才能执行
互斥锁:
   为了保证多线程并发操作数据(变量)而设置的锁,保证在加锁和释放锁之间,其他线程不能操作
    gil本质上也是一个大的互斥锁 
# 出现了数据错乱,出现了多条线程操作变量,出现的并发安全问题
  a=0
  线程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锁
    
# 什么是临界区?处在出现并发安全问题的这段代码称之为临界区,临界区会出现并发安全问题,所以要在临界区加锁
     # 加锁
     6 读取a=0
     7 计算a+1
     8 把结果赋值给a,a=1
     # 释放锁
    
    
# 互斥锁保证数据安全
  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锁并锁不住临界区,临界区需要我们自己用互斥锁加锁

鸭子类型

鸭子类型:
     只要看上去像鸭子 走路像鸭子 说话像鸭子,我们就可以叫它鸭子

解释:鸭子类型是python面向对象中描述接口的一个概念,区分与其他编程语言
    比如java:实现接口,必须显示的继承一个接口
    而python:实现接口,遵循鸭子类型,不需要显示继承一个接口(类),只要类中有对应的属性和方法,我们就称这几个类的对象为同一个种类型

并发 并行

并发
  同一时间段内,执行多个任务的能力
并行
  同一时间,执行多个任务的能力
    
# 并行必须是多cpu支持

同步 异步

# 程序调用的角度
  同步:
    同步是一件事一件事的做;只有执行完前一个任务,才会执行下一个任务。同步意味着有序
  异步:
    当一个任务已经执行完,你无需等待任务执行完成,就可以换到另一个任务上,异步意味着无序

阻塞 非阻塞

# 程序执行的角度
  阻塞:
    程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的
  非阻塞:
    程序在等待某操作过程中,自身不被阻塞,可以继续运行干别的事情,则称该程序在该操作上是非阻塞的

django flask高并发部署

django flask 同步框架,部署的时候,使用uwsgi部署,uwsgi是进程线程架构,并发量不高
可以通过uwsgi+gevent,部署成异步程序

什么是反射,python中如何使用反射

# 反射的含义
  专业的解释:指程序可以访问、检测和修改本身状态或者行为的一种能力
  白话:可以理解为利用字符串的形式去对象中操作成员属性和方法

# python中使用反射
  hasattr()
  判断字符串在对象obj中是否存在(属性,方法),存在返回True,不存在返回Flase

  getattr()
  根据字符串在对象obj中查找 如找到同名属性,则返回属性,如找到同名方法,则返回方法的引用
   如果未能找到同名的属性或者方法,则抛出异常:AttributeError
    
  setattr()
  字符串给对象设置键值对(名称空间中的名字)
    
  delattr()
  字符串删除对象对应的键值对(名称空间中的名字)
  将你输入的字符串str在对象obj中查找,如找到同名属性或者方法就进行删除

从浏览器输入一个地址,到看到页面信息,经历的过程

1 从浏览器中输入的是:域名--->要做域名解析--->把域名解析成ip地址+端口的形式--->dns解析--->(浏览器缓存,本机缓存,host文件,上一级递归解析服务,13台根dns)--->如果解析不到--->页面就会报错

2 解析完之后,向解析出的域名和端口号建立TCP连接,进行3次握手

3 向某个地址发送http的get请求 (可拓展http协议 请求头等)

4 如果后端服务是nginx转发,nginx把http请求转发给web框架(django,flask) --->django请求生命周期

5 后端服务器以http响应的形式返回给客户端浏览器

6 客户端浏览器把http响应体的内容展示在浏览器上,但http响应还有:
        响应首行:响应状态码,响应字符串描述 
        响应头:key-value,响应状态码,cookie  
        响应体
        
7 四次挥手断开TCP连接--->http协议版本号
http版本区别
0.9:底层基于tcp,每次http请求,都是建立一个tcp连接,三次握手,请求结束需要四次挥手
1.1:请求头中有个参数Keep-alive,可以保证多个http请求公用一个TCP连接
2.x:多路复用,多个请求使用同一个数据包

多路复用解释:
HTTP/1下的请求,并不能很好的利用宽带,一个TCP连接同时只能有一个HTTP请求和响应,如果正在发送一个HTTP请求,那其他的HTTP请求就得排队
这种排队会产生一个请求队列,当队头的请求发生意外(比如丢包、服务器响应缓慢),导致比平时要慢得多,就会导致后面的请求被延迟。这种情况我们称为 队头阻塞


为了解决这个问题HTTP/2采用了多路复用

在HTTP/2中,有两个非常重要的概念,分别是帧(frame)和流(stream)
帧代表着最小数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流
HTTP/2采用二进制数据帧传输,取代了HTTP1.x的文本格式,二进制格式解析更高效

# 多路复用
多路复用代替了HTTP1.x的序列和阻塞机制,所有相同的域名请求都通过同一个TCP连接并发完成
同一TCP中可以发送多个请求,对端通过帧中的标识知道属于哪个请求,通过 这个技术,可以避免HTTP旧版本中的队头阻塞问题,极大的提高传输性能

左连接,右连接,内连接,全连接:MySQL不能直接支持

数据通常不在同一张表中,这就涉及到连表操作,而表间连接方式有很多

左连接:
   以左表为基准 展示左表所有的数据 如果没有对应项则用NULL填充
    
右连接:
   以右表为基准 展示右表所有的数据 如果没有对应项则用NULL填充
    
内连接:
   把两张表中共有的数据,连接到一起
    
全连接:
   以左右表为基准 展示所有数据 各自没有的全部NULL填充

union和union all的区别?

union就是全连接
select 出来结果,union,union all都是对结果进行合并,求并集

主要区别在于,union会自动去除重复行,而union all不会。因此,如果你想要完全保留所有的行,包括重复的行,可以使用union all

tcp 三次握手和四次挥手

三次握手

三次握手:建链接

第一次握手:我(客户端)发送一个链接请求,我这个请求报文的序号是x,并且等待服务器回应状态

第二次握手:我(服务器)答应连你,确认到你的信息了,下次你(客户机)给我发上次发送的报文(x)的后一个(就是x+1 因为这个我有了 我要下一个),我发送报文序号是y,并且进入等待客户机回应的状态
# 这个时候可能会发生洪水攻击  因为有可靠协议有反馈机制,客户端可能建立完第一次握手就跑了,而服务端在一直等待客户端的回应,并且在一定时间内反复发送请求与客户端建立连接的包,这时候大量的客户端都建立完第一次握手就跑路了,这时候就发生了洪水攻击

第三次握手:我知道你接受我的连接请求了,这次我发送的报文序号是(x+1),你下次给我发送y+1这个序号的报文

那为什么要三次握手呢?为什么不是两次握手呢?
   为了阻止历史的重复连接初始化造成的混乱问题
   防止使用 TCP 协议通信的双方建立了错误的连接

   1.流式协议、可靠协议(数据不容易丢失)
     造成数据不容易丢失的原因不是因为有双向通道,而是因为有反馈机制
     给对方发消息之后会保留一个副本 直到对方回应消息收到了才会删除
     否则在一定时间内反复发送
        
   2.洪水攻击
     同一时间有大量的客户端请求建立连接 会导致服务端一直处于SYN_RCVD状态,服务端接收到了大量的syn请求,处于rcvd状态
    
   3.服务端如何区分客户端建立链接的请求
     可以对请求做唯一标识

四次挥手

四次挥手:断链接

第一次挥手:客户端发送了一个断开连接的标志,并说这个报文是序号是u,接着客户端就进入了等待服务器确认的状态

第二次挥手:服务器说我确认收到你的断开请求了,我发送的报文序号是v, 下次你(客户机)给我(服务器)发送u+1这个序号的报文,服务器开始处理剩余的一些数据

第三次挥手:服务器给客户机发送了一个断开连接的标志,确认收到你的断开请求了,我的发送的报文序号是w,你下次给我发送u+1这个序号的报文,因为在此之前客户端并没有再次的给服务器发送报文。所以服务器给客户机要的报文序号还是u+1

第四次挥手:客户机给服务器说我知道你可以断开了,我发送的报文的序号是u+1, 你(服务器)下次给我(客户端)发送的报文序号是w+1。服务器收到客户机的确认后 无需多言,就关闭连接了,客户机等了2MSL后服务器没有动静了就也关闭连接了

# 为什么客户端要等待2MSL的时间再关闭?
MSL: 最长报文段的寿命。那2MSL就是报文段一来一回的最长时间

    1.四次不能合并为三次
      因为中间需要确认消息是否发完
    -SYN=1 表示要建立连接
    -FIN:表示断开连接
    -ACK:ACK=1 表示我收到了,允许
    -seq:随机数,建立连接无论客户端还是服务端要建立连接就要要携带
    -ack:回应请求就要加1返回
    
  -三次握手:
    	-第一次:喂(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=随机数)
    第二次:服务端收到后,回复这个请求(ACK=1,ack=随机数+1)
   	第三次:服务端向客户端发起断开连接的请求(FIN=另一个随机数)
    第四次:客户端收到后,回复这个请求(ACK=1,ack=另一个随机数+1)

osi七层协议,哪七层,每层有哪些

应用层
表示层
会话层
传输层
网络层
数据链路层
物理连接层


# 物理连接层
主要用于确保计算机之间的物理连接介质 接收数据(bytes类型、二进制)


# 数据链路层
1.规定了电信号的分组方式
2.以太网协议
    规定了计算机出厂都必须有一块网卡 网卡上有一串数字
    该数字相当于计算机的身份证号都是独一无二的
    该数字的特征:12位16进制数据
      前6位产商编号 后6位流水线号
    我们给这个独一无二的编号称之为'MAC地址/以太网地址'
  

# 网络层
IP协议:规定了所有接入互联网的计算机都必须有一个IP地址,类似于进场号(取决于网线,是可变的)
MAC地址是物理地址是永远无法改变的
IP地址是动态分配的 不同场所IP是不同的

IP地址特征:
    IPV4:点分十进制
      最小:0.0.0.0
      最大:255.255.255.255
    IPV6:十六进制
      能给地球上每一粒沙分一个IP地址
        
   
# 传输层
PORT协议(端口协议)
  用来标识一台计算机上面某一个应用程序
    特征:动态分配
  端口号范围:0-65535(也就是一台计算机可以一起运行65535个应用程序)
           0-1024:系统常用端口号
           1024-8000:常用软件端口号
           
TCP协议,UDP协议都属于传输层都是用来规定通信方式的


# 会话层
负责建立、管理和终止表示层实体之间的会话连接

# 表示层
数据的解码和编码、加密和解密、压缩和解压缩

# 应用层
应用层相当于是程序员自己写的应用程序 里面的协议非常的多
http(80) https(443) dns(53)之前都讲过都可拓展开来说

TCP和UDP的区别?

TCP与UDP都是用来规定通信方式的
区别:
  TCP协议称之为流式协议、可靠协议(数据不容易丢失)
    面向连接
  UDP协议称之为数据报协议、不可靠协议
    面向无连接
    
# UDP用在哪里?
早期的QQ使用的就是纯生的UDP协议
现在QQ自己添加了很多技术和功能
  使用UDP的原因就是因为很简单 快捷 粗暴 只要指定对方的地址就可以发消息了 而TCP还要建立三握四挥
    
TCP我们可以打比方为打电话 你一句我一句
UDP我们可以看成是发短信 只要发了就行 不管对方看不看

什么是session和cookie 它俩的区别

cookie是存放再客户端浏览器里的键值对(只保留一个sessionID)
session是保存在服务端的的键值对

cookie执行原理:当客户端访问服务器时,服务器会生成一份cookie传给客户端,客户端会把这个cookie存起来,以后客户端每次访问服务器都会携带着这份cookie

session执行原理:当客户端第一次访问服务端,服务端会产生一个session,并且把这个session的id作为cookie给客户端,以后每次请求客户端浏览器每次都会携带者这份cookie来访问服务端(session的数据id)

区别:
  一个是保存在客户端的,一个是保存在服务端的,然后cookie有大小限制不能超过4k,而session没有限制,cookie没有session安全,cookie只能存字符串,而session可以存任意类型

什么是jwt?

json web token(JWT) 就是web方向token的使用

token字符串分三段:
     分为头(header),荷载(payload)和签名
     头有一般有公司信息等等
     荷载里放的是有效信息 过期时间 签发时间 用户id 用户信息等等
     签名是第一部分和第二部分通过密钥+加密方式得到的
        
token的签发:
   登陆成功后服务端生成,把token生成的字符串给前端,后面要前端发请求就要带着token字符串到后端
    
token的认证:
   拿到第一部分和第二部分使用同样的加密方式+密钥再加密---》得到字符串和第三段作比较,如果一样表示没有被篡改,如果不一样就表示被篡改了,token就不能用了

什么是中间件

中间件是处于操作系统和应用程序之间的软件,中间件处于操作系统软件与用户的应用软件的中间所以叫中间件,中间件都要遵从底层协议或者自定义协议:TCP/IP协议

如何自定义中间件?

1 创建存储自定义中间件代码的py文件或者目录
2 参考自带中间件的代码编写类并继承
3 在类中编写五个可以自定义的方法
   五个自定义方法:
    # 说出来
     process_request
     1.请求来的时候会从上往下依次执行配置文件中注册的中间件 里面的process_request方法 如果没有则直接跳过
     2.如果该方法自己返回了HttpResponse对象,那么请求不再继续往后直接返回响应数据
        
     process_response
     1.响应走的时候会从下往上依次经过每一个注册了的中间件 里面的该方法 如果没有则直接跳过
     2.如果该方法自己返回了HttpResponse对象,那么响应会替换成该HttpResponse对象数据 而不再是视图函数想要返回给客户端的数据
    
    # 忘记也可以
     process_view
     路由匹配成功之后执行视图函数/类之前自动触发
     process_exception
     视图函数/类执行报错自动触发(顺序同process_response)
     process_template_response
     视图函数/类返回的HttpResponse对象含有render并且对应一个方法的时候自动触发(顺序同process_response)
    
4 一定再配置文件中注册中间件才可以生效

django中间件

七个中间件
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',   'django.contrib.auth.middleware.AuthenticationMiddleware',   'django.contrib.messages.middleware.MessageMiddleware',    'django.middleware.clickjacking.XFrameOptionsMiddleware',

WSGI uwsgi uWSGI,CGi,fastCGI 分别是什么?

WSGI协议
  是为python语言定义的web服务器和web应用程序或者框架之间一种简单而通用的接口 其实就是定义了一种服务和应用程序解耦合的规范
    
uwsgi协议
  uWSGI服务器实现的独有的协议,用于定义传输信息的类型,用于前端服务器与 uwsgi 的通信规范
    uWSGI自有的一个协议
    uWSGI:web服务器,等同于wsgiref
    uwsgi:uWSGI自有的协议
    
uWSGI
  符合WSGI协议的web服务器,用c写的,性能比较高,通常用来部署django,flask
    
CGi	
 通用网关接口,用于在web服务器和应用程序或者之间传递信息
    
fastcgi
 是一种改进的CGi协议,用户提高web服务器和应用程序之间的通信效率和性能,与CGI不同的是,fastCGI可以通过保持一个长连接的方式重复利用已经创建的进程或线程,从而避免了多次启动和关闭的开销

符合WSGI协议的web服务器

# 符合WSGI协议的web服务器
  wsgiref,werkzeug(一个是符合wsgi协议的web服务器+工具包(封装了一些东西))
  uWSGI 是c语言写的,性能比较高
  gunicorn:python写的
  tornado:也可以部署django项目

web服务器到底是什么?服务器中间件

客户端(浏览器,app) 跟 服务端(web框架)之间的东西,服务器中间件
# nginx  apache 是一类东西,就是做请求转发
# uWSGI  gunicorn 只针对于python的web框架
# tomcat,jboss,weblogic 只针对java的web框架

如何自定制上下文管理器

一个对象如果实现了__enter__和__exit__方法,那么这个对象就支持上下文管理协议,即with语句
上下文管理协议适用于那些进入和退出之后自动执行一些代码的场景,比如文件、网络连接、数据库连接或使用锁、使用事务的编码场景等

# 如何使用
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.close()
        
with ComonSession() as session:
    session.filter()
    
print('上下文管理器结束了')

python是值传递还是引用传递

# 严格意义上来说,python既不是值传递,也不是引用传递,python是自己的传递方式,规则是:
如果传递的是不可变类型,在函数中修改,就不会影响原来的变量
如果传递的是可变数据类型,在函数中修改,就会影响原来的变量,修改,而不是重新赋值

# python一切皆对象--->内部函数是一切皆引用(对象本质就是地址,就是引用)

# 什么是值,什么是引用
  值就是一个变量=具体的值(一块内存空间放着这个变量的值)
  引用是一个变量=内存地址(内存地址指向了值)

# 什么是值传递 什么是引用传递
  如果是值传递,函数中修改了传递的值,不会影响原来的
  如果是引用传递,函数中修改了传递的引用,就会影响原来的

什么是迭代器,生成器,装饰器

# 迭代:
    一个不依赖于索引取值的方式,我们不需要关注它的位置,只要能够一个个取值,它就称之为迭代
    for循环  next()
    
#可迭代对象:
可以迭代的(for,next取值的)python中的对象称之为可迭代对象,内置中有__iter__方法的都成为可迭代对象
    字典,列表,字符串,元组,集合,文件对象
    
# 迭代器
  迭代器要满足两个条件__iter__和__next__
  调用可迭代对象,通过iter方法返回一个迭代对象,然后不断的用next方法返回元素(迭代对象的值),直到迭代完成后停止迭代

# 自定义迭代器:
写个类,类中重写__iter__和__next__方法,这个类的对象就是迭代器

# 生成器
  生成器本质就是迭代器
生成器有两种:生成器表达式,生成器函数
   生成器表达式:
    生成器表达式使用元组,不过直接调用生成器表达式是无法得到对应值,需要用循环或者next方法才能够输出生成式表达式的值
   生成器函数:
    通过yield关键字来定义一个生成器函数,这个生成器函数返回值就是一个生成器对象,当循环一次后yield会进入挂起状态,然后再次循环的时候会从挂起的位置输出下一个结果
    
# 装饰器
  在不改变被装饰对象原代码和调用方式的情况下给被装饰对象添加新的功能
  本质也是闭包函数

# 闭包函数:
    就是定义在函数内部的函数 并且用到外部函数名称空间中的名字
    
# 闭包和装饰器的区别: 
    闭包传递是变量,而装饰器传递的是函数除此之外没有任何区别,或者说装饰器是闭包的一种,它只是传递函数的闭包
  
# 在哪里用到过装饰器?
  flask的路由就是装饰器
  django的信号也可以用装饰器的方式注册
  为接口记录访问日志

django的信号用过吗?如何用,干过什么

django提供的一种信号机制,其实就是观察者模式,又叫发布-订阅。当发生某种变化的时候,通知某个函数执行

# 内置信号:
如果是内置信号,用起来简单,只需要写个函数,跟内置信号绑定,当信号被触发,函数就会执行
# 如何用?
  对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数
    
# 绑定信号 在django中有两种方式
  @receiver
   connect连接

# 自定义信号
  就比内置信号多了两步:1 定义信号 2 触发信号 信号.send

Dockerfile用过吗?常用命令有哪些

# 什么是Dockerfile
	Dockerfile是由一系列命令和参数构成的脚本文件,用来构建镜像


# FROM  
设置要制作的镜像基于哪个镜像,FROM指令必须是整个Dockerfile的第一个指令,如果指定的镜像不存在默认会自动从Docker Hub上下载

# MAINTAINER
镜像作者的信息,比如名字或邮箱地址

# RUN
构建镜像时运行的shell命令

# CMD
CMD指令中指定的命令会在镜像运行时执行,在Dockerfile中只能存在一个,如果使用了多个CMD指令,则只有最后一个CMD指令有效

# EXPOSE
声明容器的服务端口

# ADD
将宿主机的文件复制到容器内,如果是一个压缩文件,将会在复制后自动解压

# COPY
拷贝文件或目录到镜像容器内,跟ADD类似,但不具备自动下载或解压功能

# ENV
设置容器的环境变量

Redis怎么做持久化方案?

Redis支持两种持久化方式:RDB和AOF。

RDB:在指定时间间隔内将内存中的数据集快照写入磁盘,恢复时将快照文件重新读入内存。可以理解为Redis对当前数据进行备份,可以将其作为一种数据库的全量备份方式来使用。

AOF:记录服务器执行的所有写操作命令,并将这些命令按照顺序追加到文件末尾。在恢复时,Redis会顺序执行文件中保存的所有写操作命令来还原数据。可以理解为Redis对用户操作进行的追加式备份,只有增量备份,不进行全量备份。

RDB持久化方式相对AOF方式更加高效,但是可能会丢失某些最近修改的数据,AOF持久化方式虽然具备更好的安全性,但是相对而言效率比较低。因此,我们根据业务场景和需求选择合适的持久化方式来使用。

什么是IPC,进程间通信方式有哪些

IPC
  进程间通信,是指在不通的进程之间传递或者交换信息的方式
 
常见的进程间通信方式:
1.管道:
   管道是一种用于进程间通信的机制,其中一个进程的输出被另一个进程的输入所使用。在shell命令中,管道可以通过“|”符号来实现。

2.消息队列
   消息队列是一种在异步通信中传递消息的机制,它允许应用程序通过发送和接收消息来进行松散耦合的通信。在消息队列中,消息是被排列在队列中的,当一个应用程序发送一条消息到队列中时,另一个应用程序可以从队列中接收到这个消息并进行处理  
   redis就可以做消息队列,RabbitMQ、Kafka
    
3.信号量
   信号量是一种计数器,用于多个进程对共享资源的访问进行同步。其主要作用是实现进程间的互斥和同步
    
4.共享内存
   共享内存是一种高效的进程间通信方式,可以在不同进程之间共享同一块物理内存地址,从而避免复制和传输数据的过程
    
5.套接字(Socket)
   套接字是一种网络通信机制,可以用于不同主机之间的进程间通信
    
    两种情况:
        同一台机器上的两个进程通信
        不同机器上的两个进程进行通信
    如何通信:
       python queue可以进程做通信
       -消息队列:redis就可以做消息队列,rabbitmq,kafka
       -socket套接字:(展现形式:1 服务和服务之间通过接口调用 2 RPC调用:远程过程调用)

正反向代理

正向代理:
1.客户端向代理服务器发送请求,代理服务器将请求转发给目标服务器,并将目标服务器响应返回给客户端
2.客户端无法直接访问目标服务器,而是需要通过代理服务器进行访问
3.代理服务器可以缓存请求结果,提升用户体验,还能过滤请求并对其进行管理
    VPN  爬虫代理池


反向代理:
1.当客户端向服务器发送请求时,反向代理服务器收到请求并将其转发到真实的服务器上
2.客户端不知道真实服务器的存在,只与反向代理服务器进行通信
3.反向代理服务器可以根据负载均衡算法将请求分配到多个真实的服务器上,以提高请求处理速度
    Nginx

总结:
正向代理隐藏了客户端,反向代理隐藏了服务端。正向代理服务器扮演的是客户端的校色,帮助客户端发送请求,而反向代理服务器则扮演的是服务端的角色,为客户端提供服务,同时将请求转发到后端的服务器上

黏包现象,如何解决?

黏包现象就是指发送方向接收方发送多个小数据包,在网络传输过程中,这些小数据包会被合并成一个大的数据包一起传输,从而形成黏包现象

粘包现象主要是由TCP协议本身特性引起的
     将数据量比较小并且时间间隔比较短的数据整合到一起发送
     流式协议:跟水流一样不间断

        
# 如何解决黏包现象?
  只要能够判断接收的数据具体大小,就可以避免黏包问题
    
  1.固定的长度分包:发送端将需要发送的数据固定长度分割为固定长度的数据块,到接收端时依次接收和处理这些数据块
  2.分隔符分包:发送端在每个数据包的末尾添加一个特殊的分隔符,在接收端根据分隔符来判断包的边界,从而将多个数据包正确的分离开,类似于http请求用的是  /r/n/r/n
    
    
  3.字典报头+字典数据:  
       # 能够精准确定数据的大小 
       使用struct模块
    
 客户端 
        1.制作真实数据的信息字典(数据长度、数据简介、数据名称)
        2.利用struct模块制作字典的报头
        3.发送固定长度的报头(解析出来是字典的长度)
        4.发送字典数据
        5.发送真实数据     
 服务端
        1.接收固定长度的字典报头
        2.解析出字典的长度并接收
        3.通过字典获取到真实数据的各项信息
        4.接收真实数据长度

负载均衡

一种常见的技术方案,用于将大量请求分摊到多台服务器上,从而提高系统性能和可用性。在互联网应用中,负载均衡通常被用于分发web、数据库等服务的流量

负载均衡有两种实现方式
      硬件负载均衡,软件负载均衡
    
硬件负载均衡:
   硬件负载均衡是通过专门的硬件设备来实现负载均衡功能,例如F5和A10等商业设备,这些设备性能强,功能齐全,价格昂贵,大型企业使用
    
软件负载均衡:
   软件负载均衡是通过在服务器上安装负载均衡软件实现的,常用开源软件有LVS、HAProxy、Nginx等。软件负载均衡具有成本低、灵活性高的优点、适合中小型企业使用
    
根据工作层次不同,负载均衡可以分为四层负载均衡和七层负载负载均衡。四层负载均衡工作在传输层,可以基于来源地IP地址、端口号等信息进行负载均衡;而七层负载均衡工作在应用层,可以基于协议,URL路径等信息进行负载均衡

经典类和新式类

1.继承object
  经典类不需要继承object,而新式类需要继承object,python3中所有类默认都是新式类
    
2.MRO算法:
  多继承时,经典类采用深度优先搜索的方式查找父类,而新式类采用广度优先搜索的方式,广度优先搜索能保证所有的父类都被正确的搜索到,避免了一些潜在问题
  菱形继承:
     父类中名字的查找顺序按照继承时从左往右依次查找
     如果多个父类还有分类,那么遵循广度优先
  非菱形继承:
     父类中名字的查找顺序就是按照继承时从左到右的顺序依次查找,当多个父类还有分类时,那么遵循深度优先
    
3.方法解析顺序:
  MRO算法从左到右,深度优先的顺序进行搜索,以确定在多继承中继承链上各个成员被调用的顺序。在新式类中,可以通过调用super()函数,实现子类调用父类的方法

类变量和实例变量的区别

类变量:
   类变量是定义在类中,方法之外的变量,它与类的任何一个对象都无关,只要类被加载,类变量就会被初始化。可以通过"类名,类变量名"的方式访问它们,并且类变量可以被所有实例共享
    
实例变量:
   实例变量是定义在方法中或方法外,以self为前缀的变量,它属于实例,被实例所独有。每个实例都拥有自己的一份实例变量副本,相互之间没有任何影响
    
总结:
  类变量是共享的,而实例变量是相互独立的,彼此之间没有影响

django orm如何优化

1.减少查询次数
   优化函数:
      将关联查询的数据一次性加载到内存中,减少数据库查询的次数

    select_related
       是连表操作,先把表拼起来 然后整体封装成对象

    prefetch_related
       是子查询,先查一张表然后再查另外一张表 然后把两条SQL查询的结果封装起来

    only
      是Django ORM中的一个查询优化函数,用于限定返回的字段,只返回需要的字段,而不是全部字段
      查询括号内填写的字段  不走SQL查询
      也可以点括号内没有的字段  会走SQL查询

    defer
      也是Django ORM中的一个查询优化函数,与only相反,defer用于延迟加载指定字段,以节约内存
      查询括号内填写的字段  走SQL查询
      查询括号内没有的字段获取字段  不走SQL查询
        
2.分页查询
     对于数据量比较大的查询结果,应该进行分页查询以减少内存消耗和提高查询效率
    
3.使用缓存
     ORM 查询结果可以使用缓存技术进行缓存,减少数据库查询次数

django事务

在django中,事务是用于确保数据库操作的一致性和完整性的机制。开启事务后,如果有任何操作失败,则整个事务会被回滚,以保证数据不被损坏

使用django自带的ORM有三种开启事务的方式
  方式一:
     配置文件数据库相关添加键值对  全局有效
      "ATOMIC_REQUESTS": True
        
  方式二:
     使用transaction.atomic()函数来自动开启并管理事务  局部有效
    
  方式三:
     with上下文管理 可以对多个数据库操作进行分组,并检查它们是否成功 局部有效

python生命周期(执行流程)

读取源代码并逐条执行指令,在执行代码的时候python会创建对象并将对象分配到内存中,还会对内存进行垃圾回收,将不再使用的对象从内存清除,最后当执行完字节码会将执行结果输出到控制台或者文件中

django生命周期(执行流程)

服务端浏览器发送请求到web网关服务,通过了web网关服务到中间件,经历了三大认证,然后中间件到路由层,路由层再到视图层,视图层从可以去模板层也能可以去模型层到模型层用ORM查询出数据库中的数据替换到模板层,再回视图层到中间件出去经过uwsgi返回给服务端浏览器

Vue生命周期

从vue实例创建开始到销毁,总共经历了八个生命周期钩子
beforeCreate
created
beforMount
mounted
beforUpdate
updated
beforDestory
destoryed

flask生命周期(执行流程)

1.当客户端发起一个请求时,flask会根据请求的路径和请求方式调用相应的视图函数处理请求,这些视图函数通常由@app.route()装饰器指定

2.当flask找到对应的视图函数时,会将请求对象作为参数传递给它,由视图函数进行处理,完成相关的业务

3.在视图函数中返回的响应对象会被封装成符合HTTP协议的数据格式,包括HTTP状态码,响应头和响应体等

4.在请求过程中,flask会为每个请求创建一个请求上下文对象,在请求处理完成后,请求上下文对象会被销毁

5.在flask初始化过程中,会创建一个应用上下文对象,用于保存flask应用的全局信息

6.在请求过程中,可以使用flask的中间件进行对请求或响应信息处理,常用中间件包括Debug中间件、静态文件中间件等

drf生命周期(执行流程)

1.前端向后端发起请求
2.根据url路由的规则匹配到相应的视图函数,走dispatch方法经历三大认证
3.将请求体中的数据反序列化为python对象,并校验是否合法
4.调用视图函数中实现的相应的方法处理请求
5.处理响应:将业务方法的返回值序列化为JSON数据
6.将序列化后的JSON数据通过HTTP响应返回给前端

跨域问题

跨域:
  跨域是指一个域名下的网页去请求另一个域名下的资源出现的限制。这是由于浏览器的同源策略所导致的,同源策略的目的是防止恶意的网站通过脚本等方式访问其他网站的信息

JSONP:通过动态插入script标签,利用script标签没有跨域限制的特性,实现跨域数据传输的方法。

CORS:CORS(Cross-Origin Resource Sharing)是一种标准化的跨域解决方案,在服务端设置响应头中的Access-Control-Allow-Origin字段,允许指定的域名进行跨域访问。

代理服务器:使用代理服务器来转发请求,在代理服务器与目标服务器之间不存在跨域问题,可以解决前端跨域请求的问题。

WebSocket:WebSocket协议是HTML5规范定义的一种通信协议,它可以进行双向通信并且不受同源策略的限制

sql注入问题

sql注入问题:
   利用特殊符号符合组合产生特殊的含义 从而避开正常的业务逻辑
    
# 如何解决
  就是想办法过滤掉特殊符号
  excute 方法自带校验SQL注入问题,自动处理特殊符号

读写分离

读写分离:将读操作和写操作分别分配到不同的服务器上,减轻主服务器的负担,提高系统的性能和可扩展性。读写分离可以通过 MySQL Proxy 或者应用程序实现,也可以通过使用 MySQL Cluster 和多个实例来实现

主从搭建

主从搭建:主从复制是一种基于二进制日志(binlog)的数据复制方式,将主服务器上的所有更新操作记录在二进制日志中,然后通过网络传输到从服务器上执行。从服务器只能读取数据,不能写入数据,保持与主服务器数据的一致性。主从复制可以提高数据库的可用性、容错性和性能,适用于读写比例不平衡的场景

分库分表

将大型数据库中的数据按照一定的规则划分到多个数据库或者多张表中,减少单个数据库或者表的数据量,提高查询效率和可扩展性,适用于数据量大、访问频繁的场景。分库分表可以通过垂直分区和水平分区来实现

docker分层概念

在docker中,每个容器都由一系列镜像组成,这些镜像按照分层的方式组成一个树形结构,这种分层的镜像结构可以减少磁盘空间的占用,并提高容器的启动速度

镜像分了层就是为了更快的上传和下载镜像,分了层之后基础镜像就不用传了,每一层都有一个唯一ID号,这样的话,存在远程仓库里的时候,就知道一个镜像对应着一个ID号,如果一个镜像有五层,下面的两层文件ID号已经有了,只需要传上面三层就可以了

知道redis的跳跃表吗?

是redis的有序集合类型的底层实现

# 单线程为什么这么快?
  1 纯内存
  2 非阻塞IO(epoll),自身实现了事件处理,不在网络io上浪费过多的时间
  3 避免线程间切换和竞态消耗

有哪些方法数据库优化的方案

1 索引优化:通过创建和规划索引,可以加快查询和连接速度

2 优化查询语句:优化查询可以加快数据库响应速度,例如尽量使用WHERE子句限定查询范围,避免使用复杂的子查询和模糊查询等

3 数据库设计优化:规划良好的数据库结构可以提高数据库性能。例如避免使用过多的关联表和冗余数据

4 缓存:使用缓存可以提高数据库性能,例如使用redis缓存热点数据,减轻数据库压力

mysql的binlog redo undo日志

binlog,redo日志和undo日志

1. binlog(二进制日志) binlog记录了所有修改数据库的操作,除了查的操作,以二进制的形式存储在磁盘上。binlog可以用于数据备份、数据恢复和数据同步等 

2. redo日志(重做日志) redo日志也是记录数据库操作的日志,但是与binlog 不同的是,redo日志是在事务执行过程中记录的。当事务提交时,redo日志会被写入磁盘。如果数据库在事务提交前崩溃,redo可以用来恢复数据库 

3. undo日志(撤销日志) undo日志记录了事务执行过程中修改操作,用于回滚事务时撤销这些操作,undo日志也是在事务执行过程中记录,事务提交时,undo日志被删除。都可以用来干什么 binlog可以用来备份和恢复。 redo日志可以用来恢复崩溃的事务。数据复制和同步使用binlog

RBAC权限控制

首先就是表的设计 三张表  用户表 角色表 权限表
用户表和角色表 表关系是多对多 
    一个用户可以有多个角色 一个角色也可以有多个用户
角色表和权限表 表关系的多对多
    一个角色可以有多个权限 一个权限也可以有多个角色
将权限分配给角色 再将不同的角色分配给不同的用户

具体实现
   用户输入用户名密码之后 后端校验用户名密码成功之后 根据用户去查他所有的角色 根据角色去查所有的权限 
    代码实现的话:用的不是for循环 用的是递归序列化 递归序列化就是跟递归差不多 递归就是直接或间接调用自身 然后用在实际项目中的话 递归序列化出菜单 我们的权限表其实就是菜单表 权限是菜单表里的一个字段 菜单表是自关联 我们分了三级 一级菜单下面包含着二级菜单 二级菜单是真正的一个一个页面 三级的话就是二级菜单里的按钮 这个递归序列化就是按照列表套字典的形式返回数据 按钮的权限是单独放在一个列表里面 因为前端需要这样的格式 
这样方便前端按钮力度化判断是否有权限而进行是否展示 前端用的是v-if判断

给用户分配不用的角色 因为角色绑定是不同的权限 所以用户也就有了不同的权限了


如何给用户单独加权限?
   这就是rbac+acl 让用户与权限也多对多 需要设置第三张表 如果使用orm第三张表会自动创建
    给用户直接分配权限
    
    
rbac是基于角色访问控制
acl是访问控制少了一层角色

open cv 开源计算机视觉库

vue的3D模型库

three.js 3D模型库

vue-3d-model
尝试使用three.js后发现上手不是特别快,后来发现了vue-3d-model组件,能够很简单的展示出3D模型

Python图像处理库Pillow

Image类 可以从本地读取图像也可以从URL读取图像

Python通用验证码识别OCR库ddddocr

之前自己玩爬虫的时候 网站有验证码 然后就查了一下有这个模块
这个模块的话就是可以识别文字字母数字之类的验证码
对于爬虫过那些文字数字类的验证码比较好用

前端使用摄像头

我们对接的人脸识别打卡机他们打卡机进行抓拍之后会把图片穿给我们的人脸识别接口

调用js函数h5的api
getUserMedia

python的padans库

是基于numpy的数据分析工具 可以灵活高效的处理各种数据集

提供了数据结构有一个是dataframe 还有一个series
简单粗暴理解的话dataframe理解为excel里面的一张表 series是表中的某一列

使用dataframe构建函数的话里面参数有
    数据  行标题  列标题  类型  是否拷贝
    
文件读取的话有read_excel 参数有
   路径  指定哪一行为标题  指定列标题
    
其他的话就有一系列的方法像纵向合并 横向合并 去重 排序 分组 设置空值 删除空值一系列操作

es数据库

分布式全文检索引擎

Kubernetes容器编排

简称k8s
  它是一个主从分布式架构
  构成的话有主节点和从节点

关键的特性
  自动化装箱
  水平扩容
  负载均衡
  自动发布和回滚
  存储编排
    
主节点里有
  api服务器
  集群状态存储
  控制管理服务器
  调度器

消息队列

Kafka、RabbitMQ

RabbitMQ
  也是基于python中的queue来实现的
    生产者消费者模型
    连接rabbitmq
    连接channal
    创建队列 向队列里面发送信息
    
    连接rabbitmq
    起一个回调函数
    程序会夯在这里,等待从消息队列中取消息
    
    
消息安全
  消费完,确认后再删除
   
持久化

闲置消费
  正常情况如果有多个消费者,是按照顺序第一个消息给第一个消费者,第二个消息给第二个消费者
  但是可能第一个消息的消费者处理消息很耗时,一直没结束,就可以让第二个消费者优先获得闲置的消息

发布订阅

负载均衡

nginx apache(阿帕奇)

redis主从 哨兵高可用 分布式集群搭建

首先为什么要做主从搭建 主要解决了qps问题,机器故障的问题
主从实现主要能做到的就是做读写分离 提高并发量
但主从做好之后 后续还会有问题 如果主节点发生故障,需要做故障转移让其中一个从节点变为主节点 这就是做了哨兵高可用
但做了哨兵就又会有新的问题 主从复制只能主库写数据,所以写的能力和存储的能力都是有限的
第一个是并发量的问题好比方说单机的redis 的qps为10w/s,但我们可能需要百万级别的并发量 或者就是数据量单机内存只有16g到256,如果存500g的数据呢
这就要使用到集群解决这个问题 去加机器做分布式 单机无法满足就进行分区,分到多个子集中去
主流的分区方式有两种
   一个是哈希分布 一个是顺序分布
    顺序分布的话就是比如有100数据要分到三个节点上去 1-33一个节点 34-66一个节点 67-100第三个节点很多关系型数据库都是使用的这种方式
    
    还有就是哈希分区:节点取余,假设三台机器 hash(key)除3落到不同的节点上去
然后redis中有一个虚拟槽的概念就是每一个槽映射一个数据子集,用了hash函数crc16   redis中虚拟槽的范围是0-16383
redis使用虚拟槽的话就是对key进行hash得到数字对16383取余就知道这个数据归属于哪个槽管理 节点管理哪些槽是知道的 数据存在哪个节点就知道了
  最后这个集群的搭建就是要先做节点通信,然后给他们指派槽 做主从 做高可用这是一套完整的redis集群的搭建

最后还能有优化的话就是集群的扩容和缩容 先把新的机器加到集群中 做主从复制 迁移槽把之前分好的槽抽出来分给新的节点比如之前是3台机器扩容到4台机器 就16383除于4 给新的节点分过去
缩容的话就是把你的槽腾出来分别再分给其他还在使用的节点上 要算好有多少槽还有多少机器平均分配 分配完就可以把节点下线了 可以先下从再下主因为做了高可用先下主的话会触发故障转移 就是那个从库又变成了主库
posted @ 2023-04-17 21:04  Super小赵  阅读(187)  评论(0编辑  收藏  举报
****************************************** 页脚Html代码 ******************************************