zoukankan      html  css  js  c++  java
  • 面试总结复习知识点

    一、python数据类型说一下

    可变:字典、列表、集合    不可变:数字、字符串、元组
    有序:数字、字符串、元组、列表 无序:字典、集合

    二、列表和元组有什么区别

    元组与列表不同点:
    语法差异:第一个不同点是元组的声明使用小括号,而列表使用方括号,当声明只有一个元素的元组时,需要在这个元素的后面添加英文逗号;
    是否可变:第二个不同点是元组声明和赋值后,不能像列表一样添加、删除和修改元素。如果需要对元组做出改变只能重新开辟一块内存,重新生成新的tuple。
    存储方式差异:第三个不同点因为列表是可变的,为了减少每次增加/删减元素时空间分配的开销(增减元素时为了减少分配空间的操作次数),python在分配空间时会额外分配多一些,时间复杂度为O(1)。
    元组长度固定大小,元素不可变,所以存储空间固定。


    注意说明:声明一个空列表分配40字节的内存,增加一个元素后,给列表分配了72字节的内存,一个字符8个字节,那就是分配了4个字符的内存空间

    三、字典内部是怎么存储数据的            https://blog.csdn.net/qq_41556318/article/details/84241909

    python调用内部的散列函数(哈希函数),将键(key)作为参数进行转换,得到一个唯一的地址,然后将值(value)存放在改地址中,这也可以解释为什么字典的键(key)不能重复,
    如果重复就会覆盖之前的内存地址。

    四、多线程和多进程的区别

    进程:是操作系统进行资源分配和调度的基本单位,进程就是由一个或者多个线程构成的,进程之间的数据是分开的
    线程:是CPU进行资源分配和调度的基本单位,一个进程间的线程之间数据是共享的,线程是进程中最小的运行单位
    多进程:计算机上运行中的应用程序,通常称为进程。当你运行一个程序,你就启动了一个进程。每个进程都有子集独立的内存空间。
    多进程就是指计算机同时执行多个进程,一般是同时运行多个软件。
    多线程:多线程就是指一个进程中同时有多个线程在执行。
    总结:多线程是异步的,但这不代表多线程真的是几个线程同时在运行,实际上是系统不断的在各个线程之间来回切换

    学习点:

    windows系统下开进程开销很大,因此windows多线程学习重点是大量面对资源争抢与同步方面的问题。
    
    Linux系统下开进程开销很小,因此需要学习在Linux下学习重点是进程间通讯的方法

    五、进程间怎么通信        https://blog.csdn.net/wm12345645/article/details/82381407

    队列(管道+锁)

    六、数据库怎么提高查询效率?索引建多了有什么缺点     https://weibo.com/ttarticle/p/show?id=2309404290091374752084

    1.查询优化:建立索引
    2.SQL语句优化
    查询优化:建立索引需要注意的一些点
    1.给经常需要查询的字段设置索引
    2.尽量避免在where子句中对字段进行null值判断,会导致索引失效进行全表扫描,所以字段值最好不要是null值,可以写默认值0
    select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值
    3.对经常需要更新的字段,不建议设置索引,因为索引是按顺序排列的,改值将导致整个表记录顺序需要调整。
    4.尽量避免在where子句中使用or来连接条件,如果连接的两个条件其中一个没有建索引,另一个条件的索引也会失效
    如: select id from t where num=10 or num=20
    
    可以这样查询: select id from t where num=10 union all select id from t where num=20
    5.like查询注意开头不用通配符一类的,比如%,_
    select id from t where name like ‘%abc%’
    6.最左原则
    索引建多了有什么不好:1.创建索引的时候也会创建索引文件,占用过多磁盘空间
    2.索引固然可以提高select效率,但是也降低了insert效率和update效率,因为insert和update会使索引重建,所以怎么建索引需要慎重考虑

    索引为什么会快

    1.索引就是通过事先排好序,从而在查找时可以应用二分查找等高效率的算法。
    2.一般的顺序查找,复杂度为O(n),而二分查找复杂度为O(log2n)。当n很大时,二者的效率相差及其悬殊

    七、MySQL事务、锁(乐观锁、悲观锁)、四种隔离级别、脏读、幻读、不可重复读

    https://www.cnblogs.com/wangcuican/p/12730205.html

    八、MySQL主从配置原理,做Django读写分离

    MySQL主从同步原理:主库专门做写数据,从库读数据
    配置好主从信息,当主库数据发生改变
    1)master会将变动记录到二进制日志(Bin Log)里面;
    2)master有一个I/O线程将二进制日志发送到slave;
    3) slave有一个I/O线程把master发送的二进制写入到relay日志里面;
    4)slave有一个SQL线程,按照relay日志处理slave的数据;
    Django中配置读写分离
    在settings中DATABASES配置读写分离的数据库基本信息
    在view视图中可以手动读写分离,在使用数据库时,通过using(数据库名)来手动指定要使用的数据库

    九、Django请求生命周期、url是怎么找到views的、Django中间件、CSRF/CORS

    浏览器页面输入URL,根据URL匹配相应的视图函数,view视图去models中取数据,models去数据库取数据,将数据返回给view视图,
    view视图把需要展示的数据返回给模板,模板渲染数据就是html文件在浏览器展示。

    https://www.jianshu.com/p/9e4e3195d731

    url怎么找到view:基于类视图函数处理请求
    先看url,Django的CBV在url中的书写是view.类名后加as_view(),函数名加括号会优先执行,as_view()返回值是view。
    当用户访问url时,对应url会执行view函数,进入到dispatch方法,通过反射找到request.method对应的请求方式的类方法(get方法或者post方法),最后执行view视图中对应的方法。

    Django中间件/CSRF、CORS(跨域请求)  https://www.cnblogs.com/wangcuican/p/12685056.html

    十、多线程和多进程怎么开启、GIL锁

    Threading模块和线程池

    十一、map/reduce函数

    十二、__init__,__new__

    十三、深浅拷贝

    https://www.cnblogs.com/wangcuican/p/11215728.html

    十四、数据库的连表有哪些    https://www.cnblogs.com/wangcuican/p/11395635.html

    1.inner join     #内连接,只连接两张表相同的地方
    select * from emp inner join dep on emp.dep_id = dep.id;   #on后面跟的是条件
    
    2.left join      #左连接,在内连接的基础上,保留左边表没有对应的数据
    select * from emp left join dep on emp.dep_id = dep.id;
    
    3.right join    #右连接,在内连接的基础上,保留右边表没有对应的数据
    select * from emp right join dep on emp.dep_id=dep.id;
    
    4.union        #全连接,就是左连接和右连接用union连起来,但是不包括重复行
    select * from emp left join dep on emp.dep_id = dep.id
    union
    select * from emp right join dep on emp.dep_id = dep.id;
    
    5.union all    #左连接和右连接用union连起来,包括重复行,即所有结果
    select * from emp left join dep on emp.dep_id = dep.id
    union all
    select * from emp right join dep on emp.dep_id = dep.id;

    十五、装饰器、迭代器   https://www.cnblogs.com/wangcuican/p/12709397.html

    装饰器定义:在不改变被装饰对象的源代码和调用方式的前提下,为其加上新的功能。
    代码举例:
    def outter(func):
        def inner(*args,**kwargs):
            res=func(*args,**kwargs)   #res=index(*args,**kwargs)
            print(args,kwargs)
            print(res)
        return inner
    
    @outter         #index=outter(index)
    def index(a,b):
        return '原函数'
    
    index(2,b=2)    #inner(2,b=2)
    
    #结果
    (2,) {'b': 2}
    原函数

    简易版装饰器

    def wrapper(func):
        def inner(*args,**kwargs):
            return func(*args,**kwargs)     #给被装饰函数传参    
        return  inner
    
    @wrapper                 #当遇到@wrapper,立即执行wrapper函数,并且把index当做参数传递进来:index=wrapper(index)
    def index(request):
        pass

    十六、Docker怎么启动一个容器,怎么新建一个容器    https://www.cnblogs.com/wangcuican/p/12132881.html

    启动已有的容器: docker start 容器id
    新建容器两种方式:
    1.以交互方式创建容器
    docker run -it 镜像名称:标签   /bin/bash      #如果退出容器,需要重新开启
    2.守护方式创建(后台运行)
    docker run -id 镜像名称:标签
    
    进入容器内部:
    docker exec -it  容器名称(或者容器ID)  /bin/bash

    十七、JWT是干嘛的,怎么实现的    http://www.manongjc.com/detail/10-jfpsyicugnzdwvj.html

    JWT是做用户认证的
    JWT-token分为三段式:头部,载荷和签名
    1.头部的内容是基本信息和采用的加密方式
    2.载荷存放一些用户不敏感的信息:用户ID,过期时间戳等
    3.签名存放的是编码过后的头部和载荷以及一个密钥,然后使用头部中指定的签名算法加密
    
    签发:登录接口调用签发token
    1.头部信息采用base64编码得到头部字符串
    2.载荷信息采用base64编码得到载荷字符串
    3.用头部、载荷字符串和密钥,采用hash算法加密得到签名字符串
    在后台三段拼接起来生成token,返回给前台
    
    校验:根据客户端带token的请求,反解出user对象
    1.token拆分成三段,第一段头部加密字符串,获取加密方式
    2.第二段载荷字符串反解出用户ID,通过此ID可以查询出登录的用户,过期时间确保token是否过期
    3.再用第一段+第二段+密钥,采用头部信息保存的加密方式加密,与第三段签名字符串进行校验是否一致。
    校验通过才能代表第二段得到的用户是合法的登录用户
    
    jwt开发流程:
    1、用户请求登录服务器,登录接口调用签发token算法,得到token,返回给客户端,客户端自己保存在cookies中。
    2.每次请求带着这个token比较是否被更改过,可以反解出user对象,在视图中用request.user就能访问登录的用户,认证通过就可以返回对应的response。
    总结:
    1.可附带用户信息,后端直接通过JWT获取相关信息
    2.本地保存

    签发token的源码方法

    1.authenticate用户认证返回用户对象
    2.jwt_payload_handler方法传入user对象生成payload(载荷)
    3.jwt_encode_handler方法传入payload生成token

    传统用户认证一般流程是下面这样。 

    1.用户向服务器发送用户名和密码
    2.服务器验证通过后,在session中保存相关数据,比如用户角色、登陆时间等
    3.服务器向用户返回一个session_id,写入用户的cookie
    4.用户随后的每一次请求,都会通过cookie,将session_id传回服务器
    5.服务器接收到session_id,找到前期保存的用户数据,以此来验证用户

    十八、Django的Auth模块

    用户校验怎么实现的?浏览器怎么携带和设置Cookie,HTTP的请求数据格式?request.user怎么拿到的(原理)?

    cookie如何产生:
    在浏览器访问服务器时由服务器返回一个Set-Cookie响应头,当浏览器解析这个响应头时设置cookie
    Auth模块中几个常用的方法:
    1.authenticate():认证模块,用户校验
    提供用户认证,需要传入用户名和密码两个参数,用来验证用户名和密码是否正确。
    如果认证成功,则会返回一个User对象,并且会对该User对象设置一个属性标识后端已经认证了该用户
    2.login(HttpRequest,user):登录模块
    此方法接受一个HttpRequest对象,以及一个认证了的User对象。
    执行这步操作之后,用户浏览器保存cookies,并为该用户生成对应的session数据
    
    request.user原理(怎么获得到登录用户的)
    1.请求经过session的中间件,通过cookie中的session_id得到服务器的session
    2.在经过auth中间件,通过上一步取到session获取到user_pk,就能取到登录的用户

    十九、cookie和session

    Session是保存在服务端,用来跟踪用户的状态
    Cookie是保存在客户端的,用cookie来实现session跟踪的,创建session的时候,服务端会告诉客户端,在cookie里面记录一个session ID,
    以后每次请求把这个ID发送到服务端就知道是哪个用户了。

    二十、celery工作原理 

    celery是一个实现异步任务的工具,只要由三部分组成。
    消息中间件(broker)、任务执行单元(worker)、任务执行结果存储
    消息中间件
    Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成,比如:MQ,Redis
    任务执行单元
    worker是celery提供的任务执行单元
    任务结果存储
    用来存储worker执行的任务结果,使用Redis/MQ存储

    二十一、索引有哪些、索引是怎么加快查询速度的、索引底层是什么数据结构 

    索引有哪些
    1.从数据结构角度
    hash类型的索引:查询单条快,范围查询慢
    B+树类型的索引:B+树,层数越多,数据量指数级增长(InnoDB默认是B+树类型)
    2.按照功能逻辑来分
    普通索引:只是增加查询速度
    唯一索引:除了增加查询速度,而且限制数据的唯一性
    主键索引:在唯一索引的基础上增加了不为空的约束
    全文索引:用的不多,mysql自带的只支持英文,一般全文搜索使用ES
    3.按照物理存储来分
    聚集索引的叶子节点存放的是索引和真实数据
    非聚集索引的叶子节点存放的不是实际数据,而是指向实际数据的指针
    索引是怎么加快查询的
    1.索引有序
    InnoDB默认的索引的数据结构是B+树,数据都是在叶子节点有序的排列,在查找的时候能快速定位到。
    2.索引高度不会太高
    当数据量大的话,没建立索引,会全表扫描查询满足条件的数据。创建索引之后,虽然数据量大,一般B+树不会超过7层,所以查询7次就能查询出来了。层数越少,I/O查询次数越少,所以越快。

    图解B+树与查找过程

    如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)
    和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,
    P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、591013152829366075799099
    非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中

    B+树的查找过程

    如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,
    内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,
    发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。
    真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,
    那么总共需要百万次的IO,显然成本非常非常高。

    二十二、全局捕获异常

    当try中的代码块没有检测到异常时候,就会走else代码,finally无论有没有错误最后都会走。

    try:
       需要检验的代码
    except Exception:  # 万能异常  所有的异常类型都被捕获
        print('老子天下无敌')
    else:
        print('被检测的代码没有任何的异常发生 才会走else')
    finally:
        print('无论被检测的代码有没有异常发生 都会在代码运行完毕之后执行我')

    二十三、面向对象、with管理上下文源码怎么写、property是什么、__setattr__是什么 

    面向对象_继承:经典类和新式类

    property

    装饰器property,可以将类中的函数伪装成对象的属性,对象在访问该特殊属性会触发功能的执行,然后将该方法的返回值作为属性的值。
    class A:
    
        def __init__(self, x):
            self.x = x
    
        @property
        def t(self):
            return self.x
    
        @t.setter
        def t(self, value):
            return value
    
    a=A(2)
    print(a.t)
    
    b=a.t=3   #a.t=3修改类属性,触发t.setter
    print(b)

    二十四、DRF序列化、前后端框架分离实现原理

    原生Django请求生命周期

    原生Django请求生命周期:从urls.py中as_view()入手,函数名加括号优先执行as_view(),该方法返回值是view。
    当你在浏览器输入请求地址时,其实是执行view函数。然后走view函数中的dispatch方法,先会判断当前的请求方式是否在默认的八个方法内,
    如果存在,就使用反射getattr获取到自定义类中的对应的方法(get,post等),然后执行视图类中对应的请求方法。

    DRF请求生命周期

    DRF请求生命周期:根据urls.py,走as_view()进入,因为自定义视图类没有as_view()方法,只能走父类APIview的as_view方法。
    在APIView也是调用父类(Django原生View)的as_view方法,同时还禁用了CSRF认证。在as_view中执行dispatch方法,
    因为APIView中有dispatch方法,所以不再需要走View的dispatch,而是走自己的dispatch。dispatch包括请求模块,渲染模块,三大认证等

    DRF序列化组件三种

    Serializer/ModelSerializer/ListModelSerializer
    
    为什么使用序列化组件:因为视图中查询到的对象和queryset类型不能直接作为数据返回给前台,所以要使用序列化组件

    二十五、Redis五大数据类型、主从、主从缺点、持久化、哨兵

    string类型
    添加/修改数据   set key value     没有就增加新的,如果原来存在就覆盖之前的
    获取数据       get key
    删除数据       del key
    
    添加/修改多个数据    mset key1 value  key2 value  ...
    获取多个数据     mget  key1 key2 
    
    设置生命周期
    setex key seconds value             #设置多少秒之后数据过期   如果设置相同的key会把之前的清掉
    psetex  key milliseconds value      #设置多少毫秒之后过期
    Hash类型
    添加/修改数据   hset key field value     #hset  kk name  wang
    获取数据      
    hget key field          (******)获取字段field的值
    hgetall key        #获取key下面所有数据数据,显示field和value  
    删除数据  hdel key field1  [field2]
    
    添加/修改多个数据   hmset key field1 value1  field2  value2  ...
    获取多个数据   hmget key field1 field2 ...
    List类型
    添加/修改数据
    lpush  key value1 value2          #从左边一个个插入
    rpush  key  value2  value2        #从右边一个个插入
    
    获取数据
    lrange key start stop     #start和stop都是指索引位置(从0开始),如果不知道末尾索引是多少可以写-1
    lindex key index     #根据索引位置获取数据,index是索引
    
    获取并移除数据
    lpop key        #从左边开始移除数据,并且返回移除的数据
    rpop key
    set类型
    添加数据   sadd key member1  member2 ...
    获取全部数据    smembers key
    删除数据    srem key member1   member2 ...
    获取集合数据总量  scard key 
    判断集合中是否包含指定数据 sismember key member    #如果存在返回1,不存在返回0
    zset有序集合
    添加数据
    zadd key score1 member1 [score2 member2]
    
    获取全部数据
    zrange key start stop [withscores]         #如果不写withscores就只会显示member,默认是升序排名
    zrevrange key start stop [withscores]    #降序排名
    
    删除数据
    zrem key member [member]

     RDB和AOF持久化

    1.RDB将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据              
    2.AOF将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂,关注点在数据的操作过程    
    RDB两种保存方式:save和bgsave指令,bgsave在后台保存数据
    bgsave指令工作原理:
    1.客户端发送bgsave指令到redis服务端
    2.系统调用fork函数,生成子进程
    3.创建rdb文件
    4.完成之后会返回redis服务端消息,告诉已经保存完毕
    RDB的弊端:
    1.存储数据量较大时,效率较低
    2.基于fork创建子进程,内存产生消耗
    3.宕机带来数据风险
    AOF写数据的三种策略:
    1.always(每次)    2.everysec(每秒)  3.no(系统控制)
    AOF写数据过程:客户端发出指令给服务端,服务端并没有马上记录,而是放到AOF写命令刷新缓存区,到一定时间之后将命令同步到AOF文件中

    Redis过期删除策略

    1.定时删除:到了设置的过期时间就会删除
    2.惰性删除:数据到期了先不做处理,等下次访问该数据时再来删除
    3.定期删除:activeExpireCycle()函数对每个Redis数据库进行检测,随机挑选w个key检测。Key删除就删除数据,如果一轮中key删除的数量>w*25%,
    则循环这个过程,如果小于25%,继续检查下一个数据库

    数据逐出策略(内存不足时候)

    在执行每一个命令前,会调用freeMemorylfNeeded()检测内存是否充足。
    三种数据逐出策略:
    FIFO(First In First Out):先进先出,淘汰最先进来的页面,新进来的页面最迟被淘汰,符合队列
    LRU(Least recently used):最近最少使用,淘汰最近不使用的页面
    LFU(Least frequently used):最近使用次数最少,淘汰最少使用的页面

    主从复制

    主从复制的三个阶段:
    1.建立连接
    2.数据同步阶段
    3.命令传播阶段
    建立连接过程
    1.slave发送指令slaveof ip port
    2.master接收指令,响应slave,slave保存master的ip和端口
    3.建立socket连接,master保存slave的端口号
    数据同步过程:
    1.slave发送数据同步指令
    2.master执行bgsave生成RDB同步数据文件,通过socket传递给slave
    3.slave接收RBD文件,清空之前的所有数据,执行RDB文件恢复过程
    命令传播:时时保持数据同步
    当master数据库被修改时,此时需要让主从数据同步到一致状态。master将接收到的数据变更发送给slave,slave接收到命令后执行。

    哨兵工作原理

    一、监控阶段:先启动主从服务器再启动哨兵
    1.启动哨兵,连接上master之后发送info指令,获取master的信息
    2.哨兵和master之间建立cmd连接方便发送指令,同时在哨兵端保存了所有信息包括master/slaves/sentinels,在master端也保存了所有信息包括master/slaves/sentinels
    3.哨兵根据获得的slave信息去连接每一个slave,发送info指令获取slave详细信息
    4.启动第二个哨兵也会自动去连接master,发送info指令,会发现master已经和其他哨兵连接过同时建立cmd连接,此时哨兵端保存信息包括master/slave/sentinels(包括之前连接的哨兵),两个哨兵之间也会建立连接。
    5.当第三个哨兵启动的时候,也会进行之前的操作
    
    二、通知阶段
    哨兵之间自己形成内网
    
    三、故障转移阶段
    1.其中一个sentinel向master发送信息,一段时间内master没有回应,标记状态SRI_S_DOWN(主观下线)
    2.sentinel在自己内网中告诉其他哨兵,master挂了。其他哨兵也去连接master,半数以上的哨兵发现master挂了,那就是真的挂了,状态改成SRI_O_DOWN(客观下线)
    3.所有的哨兵推选出一个哨兵领导,哨兵根据自己的原则从所有的slave中挑选出新的master,其他slave切换新的master,故障恢复后原master作为slave

    二十六、linux查看端口号、查看内存占用、设置权限

  • 相关阅读:
    pthread 的 api 分类
    移动端网页实现拨打电话功能的几种方法
    阿里云ECS服务器活动99元一年,最高可买三年
    jQuery 文档操作
    [Err] 1062
    中国标准城市区域码
    json和jsonp的使用区别
    xshell评估过期解决办法
    xshell评估期已过怎么办
    git之本地仓库关联远程仓库
  • 原文地址:https://www.cnblogs.com/wangcuican/p/12986148.html
Copyright © 2011-2022 走看看