数据库三大范式是什么?

数据库三大范式是设计关系型数据库时的规范化原则,确保数据库结构的合理性和减少数据冗余。
这三大范式分别是:

  • 第一范式(1NF): 数据表中的所有列都是不可分割的原子数据项,即每一列都不可再分。
    这消除了重复的列。
  • 第二范式(2NF): 数据表中的非主键列完全依赖于主键。
    这意味着每一行数据都能通过唯一的主键来识别,并且非主键列不能部分依赖于主键。
  • 第三范式(3NF): 数据表中的非主键列之间不存在传递依赖关系。
    换句话说,非主键列不能依赖于其他非主键列。

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

	-(1).主键索引(聚簇索引)---》主键,表不建立主键,也会有个隐藏字段是主键,是主键索引,

mysql是基于主键索引构建的b+树,如果没有主键,如果按主键搜索,速度是最快的---》

一定会有主键

-(2). -辅助索引(普通索引,其他索引)-->咱们给某个自己加索引,django  index=True,

	通过该字段查询,会提高速度,如果字段 变化小(性别,年龄),不要建立普通索引

-(3).前缀索引: 在创建索引时,使用列名后加括号,并在括号中指定要索引的前缀长度。

2.1 索引的类型:

按照功能逻辑区分,MySQL目前主要有以下索引类型:

1.主键索引
	数据列不允许重复,不允许为NULL,一个表只能有一个主键。
	ALTER TABLE table_name ADD PRIMARY KEY (column);
2.普通索引
	MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和NULL值。
	一个表允许多个列创建普通索引。
	ALTER TABLE table_name ADD INDEX index_name (column);
3.唯一索引
	索引列中的值必须是唯一的,但是允许NULL值。建立唯一索引的目的大部分时候
	都是为了该属性列的数据的唯一性,而不是为了查询效率。一个表允许多个列
	创建唯一索引。
	ALTER TABLE table_name ADD UNIQUE (column);
4.全文索引
	主要是为了快速检索大文本数据中的关键字的信息。
	字段长度比较大时,如果创建普通索引,在进行like模糊查询时效率比较低,
	这时可以创建全文索引,基于倒排索引,类似于搜索引擎。
	MyISAM存储引擎支持全文索引,InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
	ALTER TABLE table_name ADD FULLTEXT (column);
5.前缀索引
	在文本类型如BLOB、TEXT或者很长的VARCHAR列上创建索引时,
	可以使用前缀索引,数据量相比普通索引更小,可以指定索引列的长度,
	但是数值类型不能指定。
	ALTER TABLE table_name ADD KEY(column_name(prefix_length));
6.组合索引
	指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,
	索引才会被使用。使用组合索引时遵循最左前缀原则。
	主键索引、普通索引、唯一索引等都可以使用多个字段形成组合索引。
	例如,ALTER TABLE table_name ADD INDEX index_name
	 ( column1, column2, column3 );
7.空间索引
MySQL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。
MySQL在空间索引这方面遵循OpenGIS几何数据模型规则。

事务的特性和隔离级别

1.事务的四个特性(ACID):

  • (1)原子性(Atomicity): 事务是一个不可分割的工作单位,要么全部执行,
    要么全部不执行。
  • (2)一致性(Consistency): 事务执行后,数据库从一个一致的状态变到另一个一致的状态,
    保持数据库的完整性。
  • (3)隔离性(Isolation):多个事务并发执行时,每个事务都应该感觉不到其他事务
    的存在,它们之间是隔离的。
  • (4)持久性(Durability): 事务一旦提交,对数据库的改变应该是永久性的。

2.事务的隔离级别:

  • (1.)READ UNCOMMITTED(读取未提交):允许读取其他事务未提交的数据,
    可能导致脏读、不可重复读和幻读。
  • (2.)READ COMMITTED(读取已提交):只能读取已经提交的数据,解决了脏读问题,
    但仍可能存在不可重复读和幻读。
  • (3)REPEATABLE READ(可重复读): 保证了在同一事务中多次读取同一数据时,
    结果保持一致。解决了脏读和不可重复读问题,但仍可能存在幻读。
  • (4)SERIALIZABLE(可串行化):最高的隔离级别,通过强制事务串行执行来
    解决所有并发问题,包括脏读、不可重复读和幻读。

随着隔离级别的提高,事务的并发性能通常会降低,
因为更高的隔离级别通常需要更多的锁和资源。
选择隔离级别时需要权衡一致性和性能的需求。
幻读的定义:
事务A 按照一定条件进行数据读取, 期间事务B 插入了相同搜索条件的新数据,
事务A再次按照原先条件进行读取时,发现了事务B 新插入的数据 称为幻读

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

1、QPS (Queries Per Second) 是每秒查询率 ,是一台服务器 每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内 所处理流量多少的衡量标准, 即每秒的响应请求数,也即是最大吞吐能力;

2、TPS (Transactions Per Second) 也就是事务数/秒。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数;
TPS 的过程包括:客户端请求服务端、服务端内部处理、服务端返回客户端。
5、PV (Page View)页面访问量,即页面浏览量或点击量,用户每次刷新即被计算一次。可以统计服务一天的访问日志得到;

6、UV (Unique Visitor)独立访客,统计1天内访问某站点的用户数。可以统计服务一天的访问日志并根据用户的唯一标识去重得到。响应时间(RT):响应时间是指系统对请求作出响应的时间,一般取平均响应时间。可以通过Nginx、Apache之类的Web Server得到。

UV 访问数(Unique Visitor)指独立访客访问数,统计1天内访问某站点的用户数(以 cookie 为依据),一台电脑终端为一个访客。
系统吞吐量要素

一个系统的吞吐量(承压能力)与request(请求)对cpu的消耗,外部接口,IO等等紧密关联。单个request 对cpu消耗越高,外部系统接口,IO影响速度越慢,系统吞吐能力越低,反之越高。

重要参数

QPS(TPS),并发数,响应时间
QPS(TPS):每秒钟request/事务 数量
并发数:系统同时处理的request/事务数
响应时间:一般取平均响应时间
关系

QPS(TPS)=并发数/平均响应时间
一个系统吞吐量通常有QPS(TPS),并发数两个因素决定,每套系统这个两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统吞吐量就上不去了,如果压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换,内存等等其他消耗导致系统性能下降。

1 脏读,不可重复读,幻读 ,mysql5.7以后默认隔离级别是什么?

# mysql 版本使用情况
    5.6:不多了
    5.7:比较多
    8.0:比较多
# PostgreSQL

# 为了提高mysql,并发操作的性能
	-mysql 是个cs架构的软件,允许多客户端或者同一个客户端同时对数据进行操作
    -同时对数据进行操作,就会出现 并发安全的问题
    -mysql 为了保证效率,又要保证安全,才有了事务隔离级别
    -有不同事务隔离级别:四个
    	-读未提交:存在脏读问题,不可重复读和幻读问题
        -读已提交:解决了脏读问题,但是存在不可重复读和幻读问题
        -可重复读:解决了脏读和不可重复读--但是存在幻读问题
        -串行化:解决了脏读,不可重复读和幻读问题
   

# 脏读,不可重复读,幻读  出现的原因是什么?
    -事务隔离级别不同,而并发开启事务操作数据,就会出现不同的问题
    
#脏读:
脏读指的是读当前事务到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读
 
# 不可重复读:
    	-解释:不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况
        -导致的原因:事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致
        
# 幻读:解决了不可重复读的问题了
    #幻读错误的理解
    幻读是 事务A 执行两次 select 操作得到不同的数据集,即 select 1 得到 10 条记录,select 2 得到 15 条记录。 这其实并不是幻读,既然第一次和第二次读取的不一致,那不还是不可重复读吗,所以这是不可重复读的一种。

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

    更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读
    
    
   # 每个隔离级别解决了什么问题
	-Read uncommitted(读未提交)-ru:存在脏读,不可重复读,幻读
    -Read committed(读已提交)-rc:解决了脏读,但是存在不可重复读和幻读
    -Repeatable read(可重复读取)-rr:解决了脏读,不可重复读问题,存在幻读问题
    -Serializable(串行化):解决了脏读,不可重复读和幻读问题,牺牲了效率
    
    
  # mysql 5.7 默认隔离级别
	-REPEATABLE READ(可重复读)-RR-解决了脏读和不可重复读,存在幻读
  # Oracle仅支持两种隔离级别:
	Read Committed:读已提交
    Serializable:默认基本为RC  存在不可重复读问题

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

# qps:Queries Per Second,每秒查询率,一台服务器每秒能够响应的查询次数,每秒的响应请求数
    	-如何估算自己项目的QPS?--取决于:并发量和平均响应时间
            0.1s * 10 =1s
            -并发量:同一时刻,能并发几个,假设并发量是1 
            -平均响应时间是0.1 
            -qps是10
# TPS:Transactions Per Second,是每秒处理的事务数,包括一条消息入和一条消息出,加上一次用户数据库访问
    TPS 的过程包括:客户端请求服务端、服务端内部处理、服务端返回客户端。
	例如,访问一个 Index 页面会请求服务器 3 次,包括一次 html,一次 css,一次 js,那么访问这一个页面就会产生一个T,产生三个Q
    
# 并发量
    系统同时处理的请求或事务数,可以直接理解为:系统同时处理的请求数量
    QPS = 并发量 / 平均响应时间 
    并发量 = QPS * 平均响应时间 
    例如当前系统QPS为1w,每个请求的响应时间都是2s,那么并发量就是2w 
# PV
	PV(Page View):页面访问量,即页面浏览量或点击量,用户每次刷新即被计算一次。可以统计服务一天的访问日志得到--->
    	比如咱么路飞项目--》记录课程访问量
        查询课程详情接口中---》加入 redis自增(course_view)
#UV
	UV(Unique Visitor):独立访客,统计1天内访问某站点的用户数。可以统计服务一天的访问日志并根据用户的唯一标识去重得到

# DAU(日活)
	DAU(Daily Active User),日活跃用户数量。常用于反映网站、app、网游的运营情况。
	DAU通常统计一日(统计日)之内,登录或使用了某个产品的用户数(去除重复登录的用户),与UV概念相似

# MAU(月活)
    MAU(Month Active User):月活跃用户数量,指网站、app等去重后的月活跃用户数量

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

# 幂等:幂等(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
    -扣减id为2的商品库存---》进入到扣减页面---》数据库中查一下这个数据的version号[假设是10]
    -访问扣减库存接口---》带着之前的version来了
    -后端的sql如下:
    update t_goods set count = count -1 , version = version + 1 where good_id=2 and version = 10
    -第一次可以顺利扣减
    -第二次:带的版本号还是10---》sql执行失败了--->扣减不了了
    
    
 # 唯一主键
    这个机制是利用了数据库的主键唯一约束的特性,解决了在insert场景时幂等问题。但主键的要求不是自增的主键,这样就需要业务生成全局唯一的主键
    
 # 唯一ID(unique)
	调用接口时,生成一个唯一id,redis将数据保存到集合中(去重),存在即处理过
    前端来到下单页面,前端生成唯一id,只要下单,带着这个唯一id到后端---》后端取出来--》存到reids中--》继续后续操作---》带着同样的id过来,redis中有了,这个就不继续执行了
    
 # 防重表
    使用订单号orderNo做为去重表的唯一索引,把唯一索引插入去重表,再进行业务操作,且他们在同一个事务中。这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。
    这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。
    
 # 前端:
	按钮只能点击一次
    
# 开源项目  sql审核平台
-https://gitee.com/cookieYe/Yearning
-https://gitee.com/rtttte/Archery

什么是gil锁,有什么作用

# 1 GIL:Global Interpreter Lock又称全局解释器锁   【本质就是一个互斥锁】
# 2 保证了cpython进程中得每个线程必须获得这把锁才能执行,不获得不能执行
# 3 使得在同一进程内任何时刻仅有一个线程在执行

# 4 gil锁只针对于cpython解释器----》目前基本都是在用cpython解释器
    -JPython
    -PyPy
    -CPython
    
    
 --------不是一个好的东西--------------
# 为什么要有gil锁?
    -python是动态强类型语言,因为有垃圾回收机制,同一个进程下可以有多个线程在执行,垃圾回收是垃圾回收线程【同一个进程下变量是共享的】,当线程做垃圾回收时,如果其他线程在运行,就可能会出并发安全的问题【数据安全的问题】,由于当年,只有单核cup【即便开启多线程,同一时刻,也只有一个线程在运行】,作者就强行做了一个GIL锁,保证在一个进程内,同一时刻只有一个线程执行,目的是为了防止垃圾回收线程做垃圾回收时,出现数据紊乱问题,所以加了gil锁
    -垃圾回收是垃圾回收线程,它在执行的时候,其他线程是不能执行的,而限于当时的条件,只有单核cpu,所以作者直接做了个GIL锁,保证一个进程内同一时刻只有一个线程在执行,但是后来随着多核机器的出现,就导致cpython解释器开启了多线程不能利用多核优势
    

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

	-https://www.jianshu.com/p/52ab26890114
    -什么是垃圾回收?
    	-GC机制:垃圾回收机制,大部分高级语言都会有自动释放---》都自带gc机制
            
    	-编程语言在运行过程中会定义变量--->申请了内存空间---》后期变量不用了---》这个内存空间应该释放掉
        -有些编程语言,这个操作,需要程序员自己做(c语言)
        -像java,python,go这些语言,都自带垃圾回收机制,可以自动回收内存空间,gc机制
        
    -不同语言垃圾回收的方式是不一样的,python是使用如下三种方式做gc,以引用计数为主,标记-清除和分代回收两个算法为辅
    	-(1)引用计数算法(reference counting);
        	-每个对象(变量)都有一个引用次数的计数属性,如果对象被引用了,那这个数就会 加1,如果引用被删除,引用计数就会 减1,那么当该对象的引用计数为0时,就说明这个对象没有被使用,垃圾回收线程就会把它回收掉,释放内存
            -引用计数不为0,但是变量应该回收
            -有问题:循环引用问题---》被循环引用的变量回收不了
            
   
        -(2) 标记-清除算法(Mark and Sweep);
        	-解决引用计数无法回收循环引用的问题
            -对象之间通过引用连在一起,节点就是各个对象,从一个根对象向下找对象,可以到达的标记为活动对象,不能到达的是非活动对象,而非活动对象就是需要被清除的

        -(3) 分代回收算法(Generational garbage collector)
        	-分代回收是解决垃圾回收效率问题
            -算法原理是Python把对象的生命周期分为三代,分别是第0代、第1代、第2代。每一代使用双向链表来标记这些对象。每一代链表都有总数阈值,当达到阈值的时候就会出发GC回收,将需要清除的清除掉,不需要清除的移到下一代。以此类推,第2代中的对象存活周期最长的对象,我们以后再做垃圾回收时,不同代,触发gc时间不同,比如第一个每隔1s,第二代每隔3s,第三代每隔6s

            
         -python垃圾回收最核心是:引用计数----》标记清除解决引用计数的循环引用问题---》分代回收解决垃圾回收的效率问题

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

---》只针对于cpython解释器(其他语言,都开多线程即可,能够利用多核优势)
	-计算是消耗cpu的:代码执行,算术,for都是计算
    -io不消耗cpu:打开文件,写入文件,网络操作都是io
    	-如果遇到io,该线程会释放cpu的执行权限,cpu转而去执行别的线程

    -由于python有gil锁,开启多条线程,同一时刻,只能有一条线程在执行
    -如果是计算密集型,开了多线程,同一时刻,只有一个线程在执行
    -多核cpu,就会浪费多核优势
    -如果是计算密集型,我们希望,多个核(cpu),都干活,同一个进程下绕不过gil锁
    -所以我们开启多进程,gil锁只能锁住某个进程中得线程,开启多个进程,就能利用多核优势
   
    
    -io密集型---》只要遇到io,就会释放cpu执行权限
    -进程内开了多个io线程,线程多半都在等待(多半进行io操作,不消耗cpu的),开启多进程是不能提高效率的,反而开启进程很耗费资源,所以使用多线程即可
    
   
    
    
# web 开发,io多(数据库查数据就是io),开启多线程,明显提高执行效率
	-uwsgi:进程线程架构---》进程4---》进程下再开线程
    -同一时刻,能允许多少人同时访问---》取决于---》4*线程数---》并发量
    -同一时刻,一个线程只能处理一个请求

    -异步框架,提高并发量	
    	-一个线程可以处理多个请求

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

# 解释
	-Python猴子补丁(Monkey Patch)是一种在【运行时】动态修改代码的技术。通在不修改源代码的情况下,改变代码的执行方式或增加功能
	-Monkey Patching是在 运行时(run time) 动态替换属性(attributes)或方法
	-原因:Python的类是可变的(mutable),方法(methods)只是类的属性(attributes);这允许我们在 运行时(run time) 修改其行为。这被称为猴子补丁(Monkey Patching), 它指的是偷偷地更改代码。
    
# 哪里用过
	# 1 位置
        import pymysql
        pymysql.instal_as_mysqldb()  # 动态的替换 原来使用 mysqldb链接数据库的类 conn
    # 2 位置
    	gevent.monkey.patch_all() # 动态的替换 原来阻塞的io方法 ,全都替换成自己写的,不阻塞的 time,socket...
        
    # 3 这种情况  ---》json ---》内置模块---》没关注性能高低
    	-如果有性能高的json转换模块---》想替换---》如果都换,每个地方都要改,太麻烦了
        -借助于猴子补丁---》程序运行一开始,动态的把json替换成ujson
        json.loads()
    
    # 4 公司写了函数:
    	函数有 三部分
        第一部分 
        	def get_data()   
            获取数据(链接到正式库查出来,现在没有权限连,但是我要测流程)
        第二部分 
        	def validate_data()   
            验证数据
        第三部分
       		def show_data()
        	输出结果
        
        写了个函数,动态的替换掉 get_data
        
        
        get_data=my_get_data   # 把数据写死
        
        get_data()
       
        validate_data()
        show_data()
        
       

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

# 反射就是通过字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员
# 反射:是程序在运行过程中通过字符串来操作对象的属性和方法

# 可使用反射的地方:
1、反射类中的变量 : 静态属性,类方法,静态方法
2、反射对象中的变量、对象属性、普通方法
3、反射模块中的变量

# python 提供了一些反射方法
    -getattr
    -setattr
    -hasattr
    -delattr
	
# 例子
class Dog:
    pass

def run():
    print('走狗')
d=Dog()
setattr(d,'run',run)
print(hasattr(d,'run'))
d.run()


# 反射在哪见过,用过
	-django,flask源码中大量使用反射
    	-dispatch
    -drf--》序列化类中--》修改对象,可以使用反射的setattr向对象中设置属性
    -判断对象中有没有某个属性,有的话调用,没有不调用
    

http和https的区别

# https://zhuanlan.zhihu.com/p/561907474

# 名字
# HTTP协议,英文全称是Hyper Text Transfer Protocol,是超文本传输协议
# HTTPS协议,英文全称Hyper Text Transfer Protocol over SecureSocket Layer,超文本传输安全协议

# 默认端口
# HTTPS 和 HTTP 的默认端口不同(443和80)

# 核心区别
# HTTPS在 HTTP与 TCP 之间加入了一个加密/身份验证层,提供了身份验证与加密通讯
HTTP+ SSL / TLS,也就是在 http上又加了一层处理加密信息的模块,比 http安全,可防止数据在传输过程中被窃取、改变,确保数据的完整性
https=http+ssl


# HTTP 由于是明文传输,主要存在三大风险:窃听风险、篡改风险、冒充