深浅拷贝是什么
浅拷贝就是拷贝出一个新的对象 如果内部数据有可变类型数据 那么该可变类型数据还是引用原来的
深拷贝就时拷贝出一个新的对象 是一个全新的 内部所有数据都是全新的
魔法方法new和init有什么区别
__new__是实例化对象之前调用 可以返回一个新对象 重写他可以返回一个已有的对象 
__init__是实例化对象之后调用的 用于实例化对象的属性
python的可变和不可变数据类型是什么?
可变类型就是值改变 内存地址不变 如列表 集合 字典等
不可变类型就是值改变 内存地址变 如数字 字符串 元祖等
什么是生成器,有什么应用场景
和迭代器类似 不过是程序员自己定义的
在函数中有yield关键字 可以点出__iter__和__next__ 函数执行并不会直接返回值 需要通过__next__来获取执行结果 下次基于yield关键字后继续执行
用于迭代取值和惰性计算等
数据库三大范式是什么
第一范式
		指数据库表的每一列都是不可分割的 具有原子性
第二范式
		在第一范式基础上 如果表是单主键 那么主键以外的列必须完全依赖于主键 如果表是复合主键 那么主键以外的列必须完全依赖于主键 不能只依赖主键的一部分
第三范式
		在第二范式的基础上 表中的非主键列必须和主键直接关联而不能间接关联 也就是说非主键列之间不能相互依赖 没有了传递依赖
mysql有哪些索引类型,分别有什么作用
聚簇索引 主键索引
		设置一个主键 如果没有主键 那么会隐藏一个主键 用于构成聚簇索引
辅助索引
		也就是普通索引
唯一索引
		unique
联合索引
b-tree索引
		适用于等值查询 范围查询和排序操作 能够较快的定位到需要查找的数据
哈希索引
		是一种基于哈希表实现的索引类型 适用于等值查询 但不支持范围查询和排序操作 可以非常快速的找到需要的数据 不支持模糊匹配
全文索引
		是一种用于文本搜索的索引类型 适用于模糊查询和全文搜索等操作 可以快速的匹配到指定关键字的文本数据
空间索引
		用于地理位置数据搜索的索引类型 适用于查找地理位置相关的数据 能够快速找到满足特定位置关系的数据
其他类型的索引
		如 全文空间索引 前缀索引 组合索引 和唯一索引等
事务的特性和隔离级别
四大特性
		原子性
			事务中的操作要么全部成功 要么全部失败
		隔离性
			事务之间相互隔离 不会互相干扰
		一致性
			事务执行前后的状态一致
		持久性
			事务执行成功后 对数据库的修改是永久性的 
隔离级别
		是指多个事务之间相互隔离的级别
		未提交读
			最低隔离级别 事务中数据的修改对其他事务可见 可能导致脏读 不可重复读和幻读等
		提交读
			事务中的数据修改只有提交后其他事务才可见 避免脏读的问题 但还是有不可重复读和幻读问题
		可重复读
			在一个事务中多次读取同一记录的结果是一致的 即使其他事务修改了记录 可以避免在脏读和不可重复读的问题 但还是有幻读问题
		串行化
			最高隔离级别 强制事务串行执行 避免脏读等问题 但会降低并发性能
		# 脏读
			指的是一个事务读取了另一个事务为提交的数据 正好被另一个事务回滚 那么该事务读取的数据就是无效的
		# 不可重复读
			事务在多次读取同一记录的结果不一致 会导致数据不一致的问题
		# 幻读
			并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读
什么是接口幂等性问题及如何解决
接口幂等性问题是指一个接口被重复调用多次 可能会导致系统数据状态出现异常的问题
解决措施有多种如
	1.请求唯一标识:每次请求中添加一个唯一标识 历史使用uuid
	2.版本控制:在接口中怎家版本号控制 在每个版本中保证接口幂等性
	3.token机制:在每个请求中添加一个token 避免多次请求对系统产生影响
	4.乐观锁机制:通过给数据表添加一个版本号字段 并在更新时检查版本号 如果版本号不一致则不更新 这样可以避免多次更新同一条数据
	5.数据校验机制:在每次请求前 对参数进行校验 避免重复数据或参数不一致情况
什么是qps,tps,并发量,pv,uv
	QPS表示每秒查询率 即每秒中处理的请求次数 通常用于衡量服务器的处理能力 它越大表示服务器的性能越强
	TPS表示每秒事务数 是指系统每秒中可以处理的事务数量 通常用于衡量数据库的性能
	并发量指的是同时在线用户数量或同时发送请求的数量 通常用于衡量系统的并发处理能力 它越大表示系统的并发能力越强
	PV指的是页面浏览量 即页面被访问的次数 通常用于衡量网站或者应用的访问量
	UV值的是独立访客数量 及访问网站或应用的独立用户数量 通常用于衡量网站或应用的受众范围
都是衡量系统性能和用户活跃度的重要指标
什么是gil锁
	全局解释器锁 它的本质就是一个大的互斥锁 它是cpython的一个机制 gil只存在与cpython解释器 它限制了一个线程只有获取到了gil锁才能执行 如果没有拿到gil锁 线程是不会执行的
	解释器不止有cpython 还有pypy jpython等
	gil锁的作用
		限制线程的执行
	为什么要有gil锁
		有垃圾回收机制的存在 python的垃圾回收机制需要垃圾回收线程来做 如果同一时刻 有多个线程在同时执行 垃圾回收就可能吧正在其他线程使用的变量给回收掉了
python的垃圾回收机制是什么样的
高级一点的语言 为了保证内存的使用效率 都会有垃圾回收的机制 python使用了下列三种方式
	引用计数
		有多少个变量指向它 它的引用计数就为几 当引用计数为0 说明没有变量指向它了 那么这块内存空间就会被回收掉
		引用计数会存在问题 循环引用
	标记清除
		是为了解决引用计数存在的循环引用问题
		第一阶段是标记阶段 它会把所有的活动对象打上标记 第二阶段是把那些没有标记的对象进行回收
		它会把循环引用的内存空间打上标记 然后回收掉
	分代回收
		把对象分为三代 一开始 对象在创建的时候 放在第一代中 如果在第一次一代的垃圾回收检查中存活了下来 就会被放在二代中 同理 在一次二代的垃圾检查中 该对象存活了下来 就会被放到三代中 之后会优先检查第一代中的对象 优先回收 其次依次往后检查回收
为什么计算密集型用多进程 io密集型用多线程
由于gil锁的存在 即使是多核机器 同一时刻也只能有一个线程在执行
线程需要cpu取调度执行 计算密集型消耗cpu
	如果开了多线程 是计算密集型 电脑有4核 但是只用到了1核 并不能发挥全部 所以开多线程对于计算密集型没有用 而对于io密集型开启多线程是有并发效果的 并不会耗费cpu
	如果计算密集型开了多进程 gil是在cpython解释器的进程中的 在进程中开启线程执行计算 可以重复利用多核优势 开一个进程就相当于开了一个cpython解释器的进程 就会有一个gil锁
由于有gil锁的存在 计算密集型开启多线程并不能利用多核的优势 开启多进程可以利用多核的优势
io不耗费cpu 开启多线程在一个时间段内是有并发效果的
开启多进程是非常消耗资源的 如果是io密集型 就没必要开启多进程 并不会有显著的效率提升
进程 线程和协程
进程是资源分配的最小单位 一个应用程序运行 至少会开启一个进程
线程是cpu调度的最小单位 cpu执行的最小单位
协程是单线程下实现并发 代码层面遇到io自己切换
为什么有了gil锁还要互斥锁
虽然gil锁可以确保线程安全 但是它并不能防止多个线程同时访问同一份共享资源 这种情况下互斥锁是非常必要的 另外 gil锁只存在于cpython中的 还有一些其他的 如jpython pypy等
进程 线程和协程代码如何实现
进程可以使用模块multiprocessing来创建
线程可以使用threading模块来实现
协程可以使用async关键字来实现
什么是鸭子类型
是一种动态语言的编程风格 其核心思想是如果看起来像一只鸭子 那么就应该被视为一个鸭子 
这以为这在鸭子类型中 不关心对象的类型 只关心对象是否具有某些特定的方法或者属性 如果一个对象具有所需的方法和属性 那么它就可以被视为符合所需的类型
可以使代码更加的灵活
什么是反射
在Python中,反射是指通过字符串的形式操作对象的属性和方法。它允许在运行时动态地获取对象的信息并且调用对象的方法或访问对象的属性,而无需提前知道对象的类型或名称。
http和https的区别
http是基于tcp作用于应用层上的超文本传输协议 默认端口80
https是基于http经过ssl或tls加密后的传输协议 保证了http传输的安全 默认端口443
什么是猴子补丁
猴子补丁是指在程序运行过程中动态的替换对象中的属性或方法 以实现额外的功能或修复一些缺陷
在浏览器输入地址并按下回车后,通常会经历以下过程:
1.DNS查询:浏览器会首先查看自己的缓存 如果没有查询本地DNS缓存和host文件 如果没有接着递归查询 向本地DNS服务器发送查询请求 没有则向根DNS服务器查询 还没有则报错
2.建立TCP连接:向目标域名解析出来的ip加端口发送TCP连接请求
3.发送HTTP请求:发送get请求
4.服务器响应:后台服务器收到请求后向浏览器发送HTTP响应 包括响应状态码 响应头和响应体
5.浏览器渲染页面:将响应体中的资源如html css js等渲染出页面 展示给用户
6.四次挥手:关闭TCP连接
左连接 右连接 内连接 全连接
数据通常不在一张表中 这就涉及到连表操作 而表之间连接方式有很多
内连接:把两张表中共有的数据 连接到一起
左连接:以左表为基准 把左表所有数据都展示 有可能右表没有 那么用空补齐
右连接:以右表为基准 把右表所有数据都展示 有可能左表没有 那么用空补齐
全连接:以左右两表数据作为基准 左右两表数据都展示 有可能左或右表没有 用空补齐
union和union all的区别
union和union all都是用于合并两个或多个select语句的结果集
union会去除结果集中的重复行 并且排序 而union all不会
union all的性能要比union号 因为union需要精心重复行的去重操作 会增加查询的开销
union all会返回所有的行 包括null和重复行 而union不会返回null值的行
OSI七层协议
	应用层:
		为应用程序提供服务 如http ftp dns等
	表示层
		处理数据的表示方式 如加密 压缩 转换数据格式等
	会话层
		负责建立 管理和终止会话 例如管理会话标识符等
	传输层
		负责数据传输 如TCP UDP PORT协议等
	网络层
		负责数据的路由和转发 如ip协议
	数据链路层
		负责定义数据的格式和传输规则 并进行差错检测和纠正等 如以太网 无线局域网 mac地址等
	物理连接层
		负责实际传输数据的硬件设备和物理介质 如电缆 光纤等
tcp和udp的区别
	都是互联网传输协议  都作用在传输层
	tcp协议是一种面向连接的可靠协议 可靠的有序的传输 在发送的时候需要先建立连接 并且会等待接收方的确认消息 以保证数据的完整
	udp协议是一种无连接的不可靠协议 不提供可靠的数据传输和确认机制 不需要建立连接 优点是速度快 但不能保证数据的完整性
tcp的三次握手和四次挥手
	三次握手
		客户端向服务器发送syn(同步)报文 请求建立连接
		服务器收到syn报文后 回复客户端一个syn+ack报文 同意连接 并请求连接客户端
		客户端收到syn+ack报文后 再向服务器发送一个ack确认报文 表示同意 至此 建立连接成功
	四次挥手
		客户端向服务器发送一个fin报文 表示要关闭连接
		服务端收到fin报文后 发送一个ack报文 表示已经收到客户端的关闭请求
		(服务端不直接发送关闭连接请求是因为可能数据还没发送完毕 为保证数据完整需要发送完再关闭连接)
		服务器再向客户端发送一个fin报文 表示服务器也准备关闭连接
		客户端收到服务器的fin报文后 发送一个ack报文确认 表示收到服务器的关闭连接请求 至此连接关闭
wsgi,uWSGI,uwsgi,cgi,fastcgi分别是什么
	wsgi是python web服务器和web应用程序之间的接口规范 定义了双方之间的通信方式 允许开发者使用python编写web应用程序 并将其余各种web服务器兼容 通常用于大型程序和框架
	uWSGI是一个符合wsgi协议的web服务器 用于将python应用程序和web服务器之间的通信 同时还支持多个协议和语言 如http websocket等
	uwsgi是一种uWSGI独有的协议 用于将web服务器和应用程序之间进行通信 并传递WSGI请求 它可以用于将WSGI应用程序与各种web服务器兼容 包括uWSGI
	CGI通用网关接口 是一种web服务器和应用程序之间的接口协议 所有bs架构软件都是遵循CGI协议的 定义了客户端和服务器之间如何传输数据
	fastcgi是一种改进的cgi协议 它通过减少重复的进程创建和销毁来提高性能 并且允许外部应用程序在web服务器的进程中保持运行状态 从而加速请求处理速度
如何自定义上下文管理器
	上下文管理是为了自然的管理资源 避免资源泄露等问题 确保资源被正确释放
	通过重写__enter__和__exit__方法来实现
	__enter__在进入上下文管理前执行
	__exit__在结束上下文管理后执行
python是值传递还是引用传递
	python既可以是值传递也可以是引用传递 这取决于传的是可变类型还是不可变类型 如果是可变类型就是引用传递 如果是不可变类型就是值传递
什么是迭代器 生成器和装饰器
	迭代器是一个对象 是由可迭代对象调用__iter__方法产生的 判断一个对象是否是迭代器只需要是否内置__iter__和__next__即可 迭代器对象提供了一种不依赖于索引取值的方式
	生成器是一个特殊的函数 内部有个yield关键字 该函数直接加括号并不会直接执行 而是生成一个生成器对象 需要调用next方法才会真正执行函数体代码 并且遇到yield关键字会停留在原地 等待下一次调用 下一次基于yield关键字继续往后 在数据量较大的情况下可以使用它 避免内存爆满 和迭代器类似的区别是它是程序员定义的
	装饰器通常是一个闭包函数 可以在不修改源代码及调用方式的情况下 给代码增加新功能或做一些其他操作 还有一个特殊的装饰器 类装饰器 通过写__init__和__call__方法实现
django中的信号
	是一种事件触发机制 用于在特定的操作发生时自动触发其他操作
	1.定义信号
		使用内置的或者自定义信号
		自定义信号通过导入django的Signal类来实现
	2.编写信号处理函数
		 该函数必须接收一个sender参数 该参数表示是谁发送的信号
	3.将信号处理函数与信号连接
		使用django的receiver装饰器装饰信号处理函数 且该装饰器传入信号
	4.发送信号
		内置信号会在达到特殊条件的时候自动触发
		自定义信号通过信号对象.send方法发送信号
Dockerfile
	Dockerfile是由一系列命令和参数构成的脚本文件 这些命令应用于基础镜像并最终创建一个新的镜像
	常用的命令有
		FROM		基于哪个基础镜像来创建
		MAINTAINER	声明该镜像的创建者
		ENV		设置该镜像中的环境变量
		RUN		是dockerfile的核心部分 在容器中执行的命令
		COPY		将本地文件复制到容器中
		ADD		和copy类似 支持url 并且可以自动解压缩
		EXPOSE	        指定容器监听的端口
		CMD		指定容器启动时要执行的命令
                WORKDIR         指定工作目录
并发和并行
	并发指的是同时处理多个任务的能力 但是这些任务可能不是同时执行的,而是交替执行的 可以使用线程 协程等
	并行指的是同时执行多个任务的能力 需要有多个执行单元同时执行不同的任务 可以使用多进程
同步和异步
	指的是一个任务的提交方式
	同步指的是任务按照预定的顺序依次执行 也就是第一个任务执行完了才执行第二个任务 同步操作需要等待上一个操作完成才能继续下一步操作 因此在执行过程中出现了阻塞的情况
	异步指的是任务不按照固定的顺序执行 在异步操作中 一个任务的执行并不需要等待另一个任务的完成 因此在执行过程中不会出现阻塞的情况
阻塞非阻塞
	指的是一个操作的等待方式
	阻塞会导致程序停滞 直到该操作完成为止会造成效率低下和资源浪费等问题
	非阻塞不会阻塞程序的执行 可以提高程序的并发性和响应速度
什么IPC,进程间通信方式有哪些
	IPC指的是进程间通信的技术,用于不同进程之间进行数据交换和通信
	常见的进程间通信包括
	1.管道
		一种半双工的通行方式,数据只能单向流动,进程通过创建管道来实现通信,管道被称为匿名管道,因为它没有与文件系统中的任何文件相关联
	2.命名管道
		类似于管道,但它有一个在文件系统中的名字,不同进程可以通过访问同一个命名管道来进行通信
	3.信号量
		一种用于进程间同步和互斥的机制,信号量是一个计数器,可以通过操作系统提供的原子操作来修改它的值,用于同步多个进程对共享资源的访问
	4.信号
		用于异步通知进程发生的事件,如错误 异常或用户自定义事件等,进程可以通过注册信息处理函数来捕获和处理异常
	5.消息队列
		一种可存储在内核中的消息链表,进程可以通过它们发送和接收消息,消息队列可以实现多对多的通信方式
	6.共享内存
		多个进程可以通过共享内存来访问同一个物理地址的内存区域,实现数据共享,进程可以通过读写共享内存来实现通信
	7.套接字
		用于网络通信,它是一种通用的进程间通信方式,进程可以通过套接字来与其他进程进行通信,套接字提供了一种标准的接口,使得进程可以通过网络进行通信
什么是正向代理,什么是反向代理
	正向代理是一种代理服务,是给客户端做代理,在客户端和原始服务器之间起到一个中介作用,客户端向代理服务器发送请求,代理服务器将请求转发给原始服务器,并将原始服务器的响应返回给客户端
	反向代理也是一种代理服务,是给服务端做代理,当客户端发送请求时,请求会被转发到反向代理服务器,反向代理服务器会根据请求的内容将请求转发给真正的服务器,并将真正服务器的响应返回给客户端,通常用于负载均衡等
什么是黏包,如何解决
	黏包是在网络通信中经常遇到的一种问题,指的是数据包在传输过程中被合并在一起,导致接收端无法正确接收,这种现象通常发生在tcp协议中,因为tcp是面向连接的流式传输协议
	解决方法有:
		1.消息定长,将发送的每个消息固定长度发送,接收端按照固定长度进行接收和处理
		2.包尾加特殊符号,在每个数据包的末尾添加特殊的结束标识符,接收端在接收到这个标识符后就可以将数据包分开处理
		3.包头加长度,发送每个数据的时候在开头表示该数据包长度的字段,接收端根据包头的长度进行处理
http协议详情 版本及常用的请求头
        特点
          1.基于请求响应
          2.无状态
          3.无连接
          4.基于tcp之上的应用层协议
        请求协议
          请求首行:包含请求方式 请求地址 请求http协议版本号
          请求头:key:value组成方式
            user-agent:标识客户端的类型及版本等
            accept:指定客户端能够接收的内容类型
            host:指定服务器的主机名和端口
            referer:请求来源的url
            cookie:传递保存在客户端的cookie
            content-type:指定请求体中数据的类型
            x-forwarded-for:识别客户端的真实ip地址
          请求体:发送的数据
        响应协议:
           响应首行:包含响应状态码,响应单词描述
           响应头:也是key:value的组成形式
           响应体:如html和json数据等
        协议版本:
          0.9:
            HTTP协议的最初版本 功能比较简陋 只支持get请求方式 并且只能请求访问html格式的资源
          1.0:
            有多种请求方式 每次tcp连接只能发送一个请求 默认是短连接的 服务器响应后就会关闭这次连接 下一次请求需要建立新连接 出现了keep-alive概念
          1.1:
            默认支持持久连接 即tcp连接后默认不会关闭 不用声明Connection: keep-alive
          2.0:
            出现了多路复用等特性 多路复用就是对于1.x版本来说 虽然有了长连接 但是请求的发送也是串行的 多路复用可以并行的发送多个请求 提高对带宽的利用率
          3.0:
            弃用了TCP协议 改为使用基于UDP协议的QUIC协议来实现    
GET请求和POST请求的区别
        post请求更安全 携带的数据不会作为url的一部分
        post请求可以发送的数据更大 get请求由于url有长度限制 所以不能发送大的数据
        post能发送更多的数据类型 如json字符串 html 文件等
        post请求比get请求慢
        post请求用于修改和写入数据 get请求通常用于获取数据
websocket是什么
        websocket是一种通信协议 区别于http协议 可以在单个TCP连接上进行全双工通信 允许服务端主动向客户端推送数据 浏览器和服务器之间只需要完成一次TCP连接 二者就可以建立持久性的连接 并进行双向数据传输
        应用场景:
          只要涉及到服务端主动给客户端发送消息的场景 都可以使用
          如:实时聊天,服务端发生变化 主动向客户端推送通知,监控服务端的内存使用情况等
        django中可以使用channles模块实现
悲观锁和乐观锁
    无论是悲观锁还是乐观锁都是人们定义出来的概念 仅仅只是一种思想 与语言无关
    悲观锁:在对数据进行操作时 假设当前操作会产生冲突 因此会对数据进行加锁 保证在此期间其他线程无法修改数据 直到当前线程释放锁
    乐观锁:在对数据进行操作时 假设当前操作不会产生冲突 因此不会对数据进行加锁 而是在更新数据的时候判断数据是否被其他线程修改过 如果没有修改过那么就执行对应操作 被修改过则不执行操作
    悲观锁的实现方式:
      1.传统的关系型数据库使用这种锁的机制,如行锁 表锁 读锁 写锁等
      2.编程语言中的线程锁 如python中的互斥锁
      3.分布式锁 redis实现的分布式锁
    乐观锁的实现方式:
      1.CAS即比较并交换 是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS 操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B),执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作 会出现ABA问题 也就是你看到的其实已经不是原来的了 已经被动过了 不过长的和原来一样
      2.版本控制:在数据表中增加一个版本号字段 每次更新数据时将版本号自增 同时将当前版本号作为更新条件 如果版本号一致 那么就执行操作 反之不执行
      3.时间戳方式:在数据表中增加一个时间戳字段 每次更新数据时将时间戳字段更新为当前时间戳 同时将当前时间戳作为更新条件 如果当前时间戳与更新时的时间戳一致 则只需操作 反之不执行
    悲观锁和乐观锁的使用场景
      如果并发量不大 可以使用悲观锁解决并发问题 但如果系统的并发量非常大的话 悲观锁会带来非常大的性能问题 建议使用乐观锁
      如果需要非常高的响应速度 建议采用乐观锁的方案 成功就执行 不成功就失败 不需要等待其他并发取释放锁 因为乐观锁并未真正的加锁 效率高
      如果冲突频率非常高 建议使用悲观锁 保证成功率 冲突频率大使用乐观锁需要多次重试才能够成功 代价比较大
      乐观锁适合读多写少的应用场景 这样可以提高并发粒度