python面试题
面试题
1 数据库三大范式是什么
# 第一范式(1NF)是指数据库表的每一列都是不可分割
# 第二范式:如果表是单主键,那么主键以外的列必须完全依赖于主键;如果表是复合主键,那么主键以外的列必须完全依赖于主键,不能仅依赖主键的一部分。
# 第三范式(3NF)要求:表中的非主键列必须和主键直接相关而不能间接相关;也就是说:非主键列之间不能相关依赖
2 mysql有哪些索引类型,分别有什么作用
# 聚簇索引,聚集索引,主键索引,主键,如果不存在主键,隐藏一个主键,构建聚簇索引
# 辅助索引,普通索引 index
# 唯一索引 unique
# 联合索引,组合索引,多列索引:unique_to
3 事务的特性和隔离级别
# 原子性(Atomicity):数据库把“要么全做,要么全部做”的这种规则称为原子性
# 隔离性(Isolation):事务之间相互隔离,不受影响,这与事务的隔离级别密切相关
# 一致性(Consistency):事务执行前后的状态要一致,可理解为数据一致性
# 持久性(Durable):事务完成之后,她对数据的修改是永恒的,即时出现故障也能够正常保持
# READ UNCOMMITTED:未提交读(读未提交)
# READ COMMITTED:已提交读(读已提交)
# REPEATABLE READ:可重复读
# SERIALIZABLE:可串行化
脏读,不可重复读,幻读 ,mysql5.7以后默认隔离级别是什么?
-脏读:指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读
-不可重复读:指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况
-幻读:并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读
-https://www.cnblogs.com/liuqingzheng/p/16480047.html
什么是qps,tps,并发量,pv,uv
-qps:Queries Per Second,每秒查询率,一台服务器每秒能够响应的查询次数
-tps:Transactions Per Second,是每秒处理的事务数,包括一条消息入和一条消息出,加上一次用户数据库访问
-并发量:系统同时处理的请求数量
-PV(Page View):页面访问量,即页面浏览量或点击量,用户每次刷新即被计算一次。可以统计服务一天的访问日志得到
-UV(Unique Visitor):独立访客,统计1天内访问某站点的用户数。可以统计服务一天的访问日志并根据用户的唯一标识去重得到
-DAU(日活)DAU(Daily Active User),日活跃用户数量。常用于反映网站、app、网游的运营情况。DAU通常统计一日(统计日)之内,登录或使用了某个产品的用户数(去除重复登录的用户),与UV概念相似
-MAU(Month Active User):月活跃用户数量,指网站、app等去重后的月活跃用户数量
什么是接口幂等性问题,如何解决?
-幂等:幂等(idempotent、idempotence)是一个数学与计算机学概念
-一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同
-接口幂等性:无论调用多少次,产生的效果是一样的
-get 获取数据天然幂等
-put 修改数据天然幂等
-delete 删除 天然幂等
-post 新增数据,会出现不幂等的情况,要把它做成幂等性的
-解决:
1 唯一主键 unique 新增用户用用户名唯一,但是订单问题解决不了
2 token机制,随机字符串
-在使用新增之前,先生成一个随机字符串token,保存起来,只要调用新增接口,就必须携带随机字符串token,到后端,校验这个token在不在我们保存的里面,如果在,说明正常,删除token,允许新增,如果不在就不允许新增
3 前端:按钮只能点击一次
1 什么是gil锁
-全局解释器锁,它的本质就是一个大的互斥锁,它是cpython的一个机制,gil只存在于cpython解释器,它限制了一个线程只有获取到了gil锁,才能执行,如果没有拿到gil锁,线程是不能执行的
-解释器不仅仅只有cpython,pypy,jpython
-gil锁的作用是什么?
-限制线程只有获得到它才能执行
-为什么要有gil锁?
-保证数据安全? 用互斥锁就可以了,gil锁不能保证数据安全
-python的垃圾回收机制,需要垃圾回收线程来做,如果同一时刻,有多个线程在同时执行,垃圾回收就可能会把正在其他线程使用的变量给回收掉,因为当时只有单核电脑,本来统一时刻就不能有多个线程同时执行,于是作者就干脆做了一个gil锁,让线程必须获得到gil锁,才能执行,但后来随着多核的出现,导致python的多线程并不能利用多核
2 python的垃圾回收机制是什么样的?
-高级一点的语言,为了保证内存的使用效率,都会有垃圾回收的机制,而咱们python使用以下三种方式来做垃圾回收
-引用计数
-有多少个变量指向它,它的引用计数就为几,当引用计数为0,说明没有变量指向它了,这块内存空间就会被回收掉
-引用计数存在问题:循环引用问题
-标记清除
-是为了解决引用计数存在的循环引用问题的
-第一阶段是标记阶段,它会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『非活动对象』进行回收
-它会把循环引用的内存空间,打上标记,然后回收掉
-分代回收
-把对象分为三代,一开始,对象在创建的时候,放在一代中,如果在一次一代的垃圾回收检查中,该对象存活下来,就会被放到二代中,同理在一次二代的垃圾检查中,该对象存活下来,就会被放到三代中,后面优先检查第一代中的对象,优先回收,其次依次往上检查做回收
3 解释为什么计算密集型用多进程,io密集型用多线程
-由于GIL锁的存在,即便是多核机器,同一时刻,也只能有一个线程在执行
-线程需要cpu去调度执行
-如果开了多线程,是计算密集型,计算是消耗cpu,假设是四核电脑,不能充分利用这四个核,只能有一个核在执行,开多线程没有用
-而如果计算密集型,开了多进程,gil是在cpython解释器进程中的,再进程中开启线程执行计算,可以充分利用多核优势
-开了一个进程,就开了一个cpython解释器的进程,就会有一个gil锁
-由于GIL锁的存在,如果是计算密集型,开启多线程,不能利用多核优势,
-开启多进程,可以利用多核优势
-io不耗费cpu,开启多线程,在一个时间段内是有并发效果的
-即便是io密集型,用多进程是不是会显著提高效率?
本身开启进程是非常消耗资源的,如果是io密集型,没有必要开多进程,并不会有显著的效率提升
1 为什么有了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锁并不锁住临界区,临界区需要我们自己用互斥锁加锁
2 进程,线程和协程
概念
# 进程:是资源分配的最小单位,一个应用程序运行起来,至少有一个进程,进程管理器中就可以看到一个个的进程
# 线程:是cpu调度,执行的最小单位,一个进程下至少有一个线程
# 协程:单线程下的并发,程序层面控制的任务切换
代码如何实现
# 开启多进程两种方式
-1 写一个类,继承Process,重写类的run方法---》实例化得到对象,对象.start 开启了进程
-2 通过Process类实例化得到一个对象,传入任务 ,调用对象.start 开启了进程
你在哪里用过
# 同理开启线程两种方式
-1 写一个类,继承Thread,重写类的run方法---》实例化得到对象,对象.start 开启了进程
-2 通过Thread类实例化得到一个对象,传入任务 ,调用对象.start 开启了进程
你在哪里用过
# 开启协程
- 早期之前:借助于第三方gevent,基于greelet写的
- async 和 await 关键字,不借助于第三方,开启协程 asyncio 包
-必须写在一个函数前, async def task()---->这个函数执行的结果是协程函数
-await 只要是io操作的代码,前面必须加 await
在哪用过
-我一般遇到计算密集型的操作,我会开多进程,io密集型的操作,我一般开多线程
-闲来无事,爬别人数据,喜欢开多线程,爬虫io居多
-程序中,异步做一件事情,也可以开多线程
比如一个视图函数,异步的吧数据写的文件中
异步的发送钉钉通知
异步的发送邮件
-但实际上,在项目中,不需要我们开启进程线程,可以借助于第三方的框架比如celery就可以做异步的操作
而celery的worker,就是进程线程架构
-django框架,是支持并发,我们没有开启多进程和多线程,但是符合uwsgi的web服务器在进入djagno框架之前,开启了进程和线程来执行视图函数
3 什么是鸭子类型
-走路像鸭子,说话像鸭子,我们就可以叫它叫鸭子
-解释:鸭子类型是python面向对象中描述接口的一个概念,区分与其他编程语言,
比如java:实现接口,必须显示的继承一个接口
而python:实现接口,遵循鸭子类型,不需要显示的继承一个接口(类),只要类中有对应的属性跟方法,我们就称这几个类的对象为同一种类型
1 什么是猴子补丁,有什么用途
-猴子补丁:在程序运行过程中,动态替换的一种技术
-1 比如咱们json模块,用内置的json,效率低,有一个第三方的ujson模块
-不改变原来程序代码,把程序中所有json都替换成ujosn
-在程序运行的入口处:import ujson as json
-2 django中 pymysql的替换
import pymsql
pymysql.install_as_mysqlDB()
-3 gevent---》猴子补丁---》monkey.pach_all()---》动态的替换内部的会阻塞的代码
-time
-socket
-把所有内置的会释放gil锁的模块,动态替换成gevent自己写的,不会释放gil锁的模块
-同步代码:io操作,会阻塞,会释放gil锁,这条线程就会被释放cpu的执行
-动态的把所有同步的代码,都替换成异步代码,把所有会阻塞的代码,都替换
-异步代码,遇到io,不释放gil锁
2 什么是反射,python中如何使用反射
-反射:是程序在运行过程中通过字符串来操作对象的属性和方法
-getattr
-setattr
-hasattr
-delattr
3 http和https的区别
-https=http+ssl/tls
-监听端口不一样,80,443、
-保证了http传输的安全
# 1 tcp 三次握手和四次挥手
-TCP是可靠连接,使用三次握手,四次挥手保证了可靠连接,数据不会丢失
-SYN: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)
洪水攻击:
同一时间有大量的客户端请求建立连接 会导致服务端一直处于SYN_RCVD状态,服务端接收到了大量的syn请求,处于rcvd状态
2 osi七层协议,哪七层,每层有哪些,TCP/IP五层结构
-osi七层: 应用层 表示层 会话层 传输层 网络层 数据链路层 物理连接层
-五层结构: 应用层(三个合成一个) 传输层 网络层 数据链路层 物理连接层
-应用层 表示层 会话层(应用层)
-应用层协议:http,ftp,https,dns
-表示层:https=http+ssl 的加密
-会话层:负责建立、管理和终止表示层实体之间的会话连接
-传输层:
-tcp协议
-udp协议
-端口:端口协议
-网络层
-ip地址协议
-数据链路层:
-mac地址:以太网协议
-数据帧:电信号的分组方式
-物理层:
-物理介质,
-网线
3 tcp和udp的区别?udp用在哪里了?
-tcp是面向连接的可靠协议
-udp无连接的不可靠协议
-都是处于传输层
-比如:
udp:一些聊天,dns协议用的udp协议
tcp:http mysql,redis客户端服务端通信
1 wsgi uwsgi uWSGI,cgi,fastcgi 分别是什么?
# CGI:通用网关接口(Common Gateway Interface/CGI),CGI描述了服务器(nginx,apache)和请求处理程序(django,flask,springboot web框架)之间传输数据的一种标准
# 所有bs架构软件都是遵循CGI协议的
# 一句话总结: 一个标准,定义了客户端服务器之间如何传数据
# FastCGI:快速通用网关接口(Fast Common Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本
# FastCGI致力于减少网页服务器与CGI程序之间互动的开销,从而使服务器可以同时处理更多的网页请求
#常见的fastcgi服务器:Apache,Nginx,Microsoft IIS
# CGI的升级版
# WSGI:Python Web Server Gateway Interface,缩写为WSGI,Python定义的Web服务器和Web应用程序或框架之间的一种通用的接口
#一句话总结: 为Python定义的web服务器和web框架之间的接口标准
#uWSGI:符合wsgi协议的web服务器,用c写的,性能比较高,咱们通常用来部署django,flask
#一句话总结:一个Web Server,即一个实现了WSGI协议的服务器,处理发来的请求及返回响应。
# uwsgi:uWSGI服务器实现的独有的协议,用于定义传输信息的类型,是用于前端服务器与 uwsgi 的通信规范
# 1、一句话总结: uWSGI自有的一个协议
uWSGI:web服务器,等同于wsgiref
uwsgi:uWSGI自有的协议
符合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语句
# 上下文管理协议适用于那些进入和退出之后自动执行一些代码的场景,比如文件、网络连接、数据库连接或使用锁,s使用事务的编码场景等
# 如何使用
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一切皆对象---》(你不要说)内部函数是一切皆引用(对象本质就是地址,就是引用)
# 什么是值,什么是引用
-值就是一个变量=具体的值(一块内存空间放着这个变量的值)
-引用是一个变量=内存地址(内存地址指向了值)
# 什么是值传递 什么是引用传递
-如果是值传递,函数中修改了传递的值,不会影响原来的
-如果是引用传递,函数中修改了传递的引用,就会影响原来的
1 并发 并行
# 并发:同一时间段内,执行多个任务的能力
# 并行:同一时刻,执行多个任务的能力
# 并行必须是多cpu支持
2 同步 异步
# 程序调用的角度
# 同步:同步是一件事一件事的做;只有执行完前一个任务,才会执行下一个任务。同步意味着有序
# 异步:当一个任务已经执行了,你无需等待该任务执行完成,就可以切换到另外一个任务上。异步意味着无序
-出现了锁的概念
3 阻塞 非阻塞
# 程序执行的角度
# 阻塞:程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的
# 非阻塞:程序在等待某操作过程中,自身不被阻塞,可以继续运行干别的事情,则称该程序在该操作上是非阻塞的
1 什么是IPC,如何进行进程间通信
-IPC:Inter-Process Communication,进程间通信
-两种情况:
-同一台机器上的两个进程通信
-不同机器上的两个进程进行通信
-如何通信:
-pyton queue可以做进程间通信
-消息队列: redis就可以做消息队列,rabbitmq,kafka
-socket套接字:(展现形式:1 服务和服务之间通过接口调用 2 RPC调用:远程过程调用)
2 正向代理,反向代理
-正向代理:代理的是客户端 VPN 爬虫代理池
-反向代理:代理的是服务端 nginx
3 什么是粘包
-因为TCP是流式协议,tcp客户端发送的多个数据包就会像水流一样流向TCP服务端,多个数据包就会'粘'在一起,区分不开是几个数据包,造成了粘包现象
-1 每个包设置结束标志 http协议采用这种 /r/n/r/n
-2 每个包设置固定大小的头,头中包含包的大小
1 http协议详情,http协议版本,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),响应单词描述
响应头: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协议来实现
2 GET请求和POST请求的区别
post更安全(不会作为url的一部分)
post发送的数据更大(get有url长度限制)
post能发送更多的数据类型(get只能发送ASCII字符)
post比get慢
post用于修改和写入数据,get一般用于搜索排序和筛选之类的操作
3 如何实现服务器给客户端发送消息,websocket是什么?用过吗
-websocket:一种通信协议,区别于http协议,可在单个TCP连接上进行全双工通信。允许服务端主动向客户端推送数据。浏览器和服务器只需要完成一次tcp连接,两者之间就可以建立持久性的连接,并进行双向数据传输
-没有跨域问题
-应用场景:
-只要涉及到服务端主动给客户端发送消息的场景,都可以用
-实时聊天:我项目没写
-服务端发生变化,主动向客户端推送一个通知
-监控服务端的内存使用情况
-djanog中使用channles模块实现
-https://zhuanlan.zhihu.com/p/371500343
4 悲观锁和乐观锁,如何实现
-无论是悲观锁还是乐观锁,都是人们定义出来的概念,仅仅是一种思想,与语言无关
-并发控制:
-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)
python 的collections包下有哪些类型
-collections 是 python 的内置模块,提供了很多方便且高性能的关于集合的操作,掌握这些知识有助于提高代码的性能和可读性
# namedtuple() : 创建一个命名元组子类的工厂函数
# deque : 高效增删改双向列表,类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop)
# defaultdict : 当字典查询时,为key不存在提供一个默认值。
# OrderedDict : 有序词典,就是记住了插入顺序
# Counter : 计数功能
import collections
# print(dir(collections))
#用来表示一个二维坐标
point=collections.namedtuple('point',['x','y'])
p=point(1,2)
print(p.x)
print(p.y)
#deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈
q=collections.deque(['a','b','c'])
q.append('x')
print(q)
q.appendleft('y')
print(q)
#当字典查询时,为key不存在提供一个默认值。
# values=[11,22,33,44,55,66,77,88,99,90]
# my_dict=collections.defaultdict(list)
# print(values)
# for value in values:
# if value>66:
# my_dict['k1'].append(value)
# else:
# my_dict['k2'].append(value)
# print(my_dict)
dd=collections.defaultdict(lambda :'no exit')
dd['key1']='abc'
print(dd['key1'])
print(dd['key2'])
# OrderedDict : 有序词典,就是记住了插入顺序
od=collections.OrderedDict()
od['z']=1
od['y']=2
od['x']=3
print(od)
od.keys()
print(od)
# Counter : 计数功能
c=collections.Counter('sadasfdsafsdfa')
print(c)
#Counter({'s': 4, 'a': 4, 'd': 3, 'f': 3})
sorted(iterable, key=None, reverse=False)可以对所有的可迭代对象进行排序,可迭代对象作为参数之一传入,返回排序后的列表,原有列表的元素顺序不变。
List.sort(key=None, reverse=False)仅可对列表进行排序,排序是针对原有列表进行的,调用该函数后返回值为None,原有列表变得有序。

浙公网安备 33010602011771号