python 面试真题

0、Python是什么?javascript

复制代码
  • Python是一种解释型语言。可是跟C和C的衍生语言不一样,Python代码在运行以前不须要编译。其余解释型语言还包括PHP和Ruby。
  • Python是动态类型语言,指的是在声明变量时,不须要说明变量的类型。能够直接编写相似x=111x="Hello World"这样的代码,程序不会报错。
  • Python是一门强类型语言,是指不容忍隐式的类型转换,好比字符串类型的数字和整型的数字进行比较不会成立。
  • Python很是适合面向对象的编程(OOP),由于它支持经过组合(composition)与继承(inheritance)的方式定义类(class)。
  • 在Python语言中,函数是第一类对象(first-class objects)。这指的是它们能够被指定给变量,函数既能返回函数类型,也能够接受函数做为输入。类(class)也是第一类对象。
  • Python代码编写快,而运行速度比编译语言一般要慢。可是Python容许加入基于C语言编写的扩展,也常被用做“胶水语言”。所以咱们可以优化代码,消除瓶颈。好比说numpy就是一个很好地例子,它的运行速度很是快。
  • Python用途很是普遍——爬虫、Web 程序开发、桌面程序开发、自动化、科学计算、科学建模、大数据应用、图像处理、人工智能等等。
  • Python包含八种数据类型:字符串、元组、字典、列表、集合、布尔类型、整型、浮点型。拥有三大特性:封装、继承、多态。最显著特色是采用缩进/4个空格(不能混用)表示语句块的开始和结束。
  • Python的标识符命名规则有:能够由字母下划线数字组成,不能以数字开头,不能与关键字重名,不能含有特殊字符和空格,使用大驼峰、小驼峰式命名。Python区分大小写

 

_单下划线开头:声明为私有变量,经过from M import * 方式将不导入全部如下划线开头的对象,包括包、模块、成员。
单下划线结尾_:为了不与python关键字的命名冲突。
__双下划线开头:模块内的成员,表示私有成员,外部没法直接调用
__双下划线开头双下划线结尾__:指那些包含在用户没法控制的名字空间中的“魔术”对象或属性,如类成员的name 、doc、init、import、file、等。
表达式:算术运算符  + - * / // %   ; 比较运算符 >  <  >=  <= !=  ; 逻辑运算符 and or not ;判断是否为同一对象:is、is not ; 判断是否属于另外一个对象: in  、not in。
函数支持递归、默认参数值、可变参数、闭包,实参与形参之间的结合是传递对象的引用。另外还支持字典、集合、列表的推导式。
Python3中的print函数代替Python2的print语句 Python3中的Str类型表明Unicode字符串,Python2中的Str类型表明bytes字节序列 Python3中的 / 返回浮点数,Python2中根据结果而定,能被整除返回整数,不然返回浮点数 Python3中的捕获语法 except exc as var 代替Python2中的 except exc, var
复制代码

 

 

一、Python新式类&旧式类的区别php

复制代码
Python2中默认都是旧式类,除非显式继承object才是新式类; Python3中默认都是新式类,无需显式继承object。

 新式类对象能够直接经过__class__属性获取自身类型:实例对象a1.__class__ 、type(实例对象a1)结果为:<class '__main__.A1'>; 旧式类为 __main__.A、<type 'instance'>

 多继承时,经典类搜索父类的顺序: 先深刻继承树左侧,再返回开始找右侧,菱形搜索,深度优先搜索(MRO算法); 新式类搜索父类的顺序: 先水平搜索,而后再向上移动,广度优先搜索(C3算法)。

 新式类增长了__slots__内置属性, 能够把实例属性的种类锁定到__slots__规定的范围之中。

 新式类增长了__getattribute__方法

复制代码

 

 

二、如何在一个函数内部修改全局变量  css

复制代码
a = 10    

def info():
  print(a)

def foo():
  global a
  a = 22
  print(a)

print(a)
info()
foo()

# 通过foo函数的修改,a的值已变为22
print(a)
info()
复制代码

 

 

三、列出5个python标准库html

标准库:sys、os、re、urllib、logging、datetime、random、threading、multiprocessing、base64
第三方库:requests、Scrapy、gevent、pygame、pymysql、pymongo、redis-py、Django、Flask、Werkzeug、celery、IPython、pillow

 

 

四、字典如何删除键和合并两个字典前端

复制代码
di = {"name": "power"}
ci = {"age": 18}

# 删除键
del di["name"]

# 合并字典
di.update(ci)
复制代码

 

 

五、python实现列表去重的方法java

复制代码
li = [2, 2, 1, 0, 0, 1, 2]

# 方法一:使用set方法
sl = list(set(li))


# 方法二:额外使用一个列表
li2 = list()
for i in li:
    if i not in li2:
        li2.append(i)
      

# 方法三:使用列表的sort方法
l1 = ['b','c','d','c','a','a'] l2 = list(set(l1)) l2.sort(key=l1.index) print(l2)

l1 = ['b','c','d','c','a','a']
l2 = sorted(set(l1),key=l1.index)
print(l2)

 
 
复制代码

 

 

六、一句话解释什么样的语言可以用装饰器?python

复制代码
装饰器本质上是一个Python函数,他可让其余函数在不须要作任何代码改动的前提下额外增长功能,装饰器接收一个函数做为参数,返回值也是一个函数。
谁能够用:
函数能够做为参数传递的语言,就能够使用装饰器。
做用:在不改变源代码的状况下添加新的功能。
使用场景:插入日志、性能测试(计算函数运行时间)、事务处理(让函数实现事务的一致性)、缓存、权限校验等场景

问题:函数A接收整数参数n,返回一个函数B,函数B是将函数A的参数与n相乘后的结果返回

def info(func):
  def foo(n):
    res = func(n)
    return res * n
  return foo

@info
def func(n):
  return n

func(2)
[Out]: 4
复制代码

 

 

七、python内建数据类型有哪些 mysql

复制代码
# 不可变类型
int      整形
str      字符串
float    浮点型
tuple  元组

bool 布尔型

# 可变类型 list 列表 dict 字典
复制代码

 

 

八、简述面向对象中__new__和__init__区别linux

复制代码
一、__new__必须接收一个名为 cls 的参数,表明的是当前类对象。
二、__new__必须有返回值,返回的是由当前类对象建立的实例对象,而__init__什么都不返回。
三、__init__必须接收一个名为 self 的参数,表明的是由__new__方法所建立的实例对象。
四、只有在__new__方法返回一个 cls 的实例时,后面的__init__才能被调用。
五、当建立一个新实例对象是,自动调用__new__方法, 当初始化一个实例时调用__init__方法。

ps: __metaclass__ 是建立类时起做用.因此咱们能够分别使用__metaclass__,__new____init__来分别在类建立,实例建立和实例初始化的时候作一些小手脚.__metaclass____metaclass____new____init__
复制代码

 

 

九、进程&线程&协程?git

复制代码

一、进程多与线程进行比较

  • 线程是CPU真正调度的单位,也是进程内的一个执行单元,进程是系统资源分配的基本单位;
  • 一个进程内至少有一个线程,同一个进程内的线程共享进程的资源,而进程有本身独立的地址空间,每一个进程各自有独立的资源互不干扰。
  • 每一个独立的线程必须有一个程序运行的入口、顺序执行序列和程序的出口,可是线程不可以独立执行,必须依存在进程当中。
  • 不只进程之间能够并发执行,同一个进程的多个线程之间也能够并发执行。
  • 多线程的意义在与一个应用程序中,有多个执行部分能够同时执行。但操做系统并无将多个线程看作多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

 

二、协程与线程进行比较

  •  一个线程内能够有多个协程,一个进程也能够单独拥有多个协程,这样python中则能使用多核CPU。
  • 线程进程都是同步机制,而协程能够选择同步或异步。
  • 协程能保留上一次调用时的状态,每次过程重入时,就至关于进入上一次调用的状态。
复制代码

 

 

十、列表[1,2,3,4,5],请使用map()函数输出[1,4,9,16,25],并使用列表推导式提取出大于10的数,最终输出[16,25]

li = [1, 2, 3, 4, 5]

def info(x):
    return x**2

li2 = [i for i in map(info, li) if i > 10]

 

 

十一、python中生成随机整数、随机小数、0--1之间小数方法

复制代码
import random
import numpy as np

# 随机整数
print(random.randint(0, 99999))

# 随机小数
print(np.random.randn())

# 随机 0-1 小数
print(random.random())
复制代码

 

 

十二、避免转义给字符串加哪一个字母表示原始字符串?

复制代码
import re

a = 'power.top*one'

res = re.match(r'.*', a)

print(res)

# 注:r 只针对于Python代码生效,与re正则并没有关系
复制代码

 

 

1三、<div class="nam">中国</div>,用正则匹配出标签里面的内容(“中国”),其中class的类名是不肯定的

复制代码
import re

a = '<div class="nam">中国</div>'

res = re.match(r'<div class=".*">(.*)</div>', a)
res.group(1)


res = re.findall(r'<div class=".*">(.*)</div>', a)
print(res[0])
复制代码

 

 

1四、python中断言方法举例

 

 

 

1五、数据表student有id,name,score,city字段,其中name中的名字可有重复,须要消除重复行,请写sql语句

select distinct  name from student

 

 

1六、10个Linux经常使用命令

cd、ls、cp、mv、mkdir、touch、cat、grep、echo、pwd、more、tar、tree

 

 

1七、python2和python3区别? 

复制代码
一、Python3 使用 print 必需要以小括号包裹打印内容,好比 print('hi')
   Python2 既能够使用带小括号的方式,也能够使用一个空格来分隔打印内容,好比 print 'hi'

二、python3的range 返回可迭代对象,python2的range返回列表,xrange返回迭代器
  for ... in 和 list() 都是迭代器

三、python2中使用ascii编码,python中使用utf-8编码

四、python2中unicode表示字符串序列,str表示字节序 python3中str表示字符串序列,byte表示字节序列

五、python2中为正常显示中文,引入coding声明,python3中不须要

六、python2中是raw_input()函数,python3中是input()函数

复制代码

 


1八、列出python中可变数据类型和不可变数据类型,并简述原理

可变类型: 列表、字典

不可变类型:字符串、整型、浮点型、元组

 


1九、s = "ajldjlajfdljfddd",去重并从小到大排序输出"adfjl"

复制代码
s = "ajldjlajfdljfddd"

# set方法
li = list(set(s))
li.sort()        # 注:sort 没有返回值


# 循环遍历
li2 = []
for i in s:
    if i not in li2:
        li2.append(i)
li2.sort()
复制代码

 

20、用lambda函数实现两个数相乘

复制代码
nu = lambda x, y: x*y

nu(2, 3)


lambda 函数是一个能够接收任意多个参数(包括可选参数)而且返回单个表达式值的函数。

一、lambda函数比较轻便,即用即扔,很适合的场景是,须要完成一项功能,但此功能只在此一处使用,连名字都很随意的状况下。
二、匿名函数,通常用来给 filter、map 这样的函数式编程服务。
三、做为回调函数,传递给某些应用,好比消息处理。
复制代码

 

2一、字典根据键从小到大排序

dict(sorted(di.items(), key=lambda x: x))

 


2二、利用collections库的Counter方法统计字符串每一个单词出现的次数"kjalfj;ldsjafl;hdsllfdhg;lahfbl;hl;ahlf;h"

复制代码
from collections import Counter

s = "kjalfj;ldsjafl;hdsllfdhg;lahfbl;hl;ahlf;h"
Counter(s)

# 输出结果
Counter({'l': 9, 'h': 6, ';': 6, 'f': 5, 'a': 4, 'd': 3, 'j': 3, 's': 2, 'b': 1, 'g': 1, 'k': 1})
复制代码

 

 

2三、字符串a = "not 404 found 张三 99 深圳",每一个词中间是空格,用正则过滤掉英文和数字,最终输出"张三  深圳

复制代码
import re

a = "not 404 found 张三 99 深圳"

# 第一种方式
res = re.sub('[^\u4e00-\u9fa5]', '', a).split()
print(res)


# 第二种方式
value = re.findall(r'[^a-zA-Z\d\s]+', a)
print(''.join(value))
复制代码

 

 

2四、filter方法求出列表全部奇数并构造新列表,a =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

复制代码
# filter(function, iterable)      function -- 判断函数。 iterable -- 可迭代对象。

a =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


def info(x):
    
    if x % 2 == 1:
    
        return x


print(list(filter(info, a)))
复制代码

 


2五、列表推导式求列表全部奇数并构造新列表,a =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

li = [i for i in a if i % 2 == 1]

 

 

2六、正则re.complie做用

re.compile是将正则表达式编译成一个对象,加快速度,并重复使用

 

 

2七、a=(1,)b=(1),c=("1") 分别是什么类型的数据?

元组 : a=(1,)

整型 :b=(1)

字符串 : c=("1")

 

 

2八、两个列表合并为一个列表

复制代码
#[1,2,5,6,7,8,9]

li1 = [1,5,7,9]
li2 = [2,2,6,8]

# 第一种方法
li3 = list(set(li1 + li2))
print(li3.sort())

# 第二种方法
li1.extent(li2)
li5 = list(set(li1))
li5.sort()
print(li5)

# 第三种方法
li6 = li1 + li2
print(li6)
复制代码

 

 

2九、用python删除文件和用linux命令删除文件方法

python:   os.remove(文件名)
linux:       rm  文件名

 

 

30、log日志中,咱们须要用时间戳记录error,warning等的发生时间,请用datetime模块打印当前时间戳 “2018-04-01 11:38:54”

复制代码
from datetime import datetime

print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))

print(datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S"))


# Out[12]: '2019-03-30 18:57:55'


logging模块的日志等级:

DEBUG    最详细的日志信息,典型应用场景是:问题诊断

INFO     信息细程度仅次于DEBUG,一般只记录关键节点信息,用于确认一切都是按照咱们预期的那样进行工做

WARNING   当某些不指望的事情发生时记录的信息(如,磁盘可用空间较低),可是此时应用程序仍是正常运行的

ERROR     因为一个更严重的问题致使某些功能不能正常运行时记录的信息

CRITICAL   当发生验证错误,致使应用程序不能继续运行时记录的信息

 logging.debug('debug message') logging.info('info message') logging.warn('warn message') logging.error('error message') logging.critical('critical message') 
复制代码

 

3一、写一段自定义异常代码

复制代码
li = [1, 2, 3, 0]

for i in li:
    try:
        if i == 0:
            raise Exception("遍历获得0")
    except Exception as e:
        print(e)
复制代码

 

 

3二、正则表达式匹配中,(.*)和(.*?)匹配区别?

(.*)是贪婪匹配,会把知足正则的尽量多的日后匹配
(.*?)是非贪婪匹配,会把知足正则的尽量少匹配

 

 

3三、[[1,2],[3,4],[5,6]]一行代码展开该列表,得出[1,2,3,4,5,6]

li = [[1, 2], [3, 4], [5, 6]]

lis = [j for i in li for j in i]

 

 

3四、x="abc",y="def",z=["d","e","f"],分别求出x.join(y)和x.join(z)返回的结果

复制代码
x="abc"

z=["d","e","f"]

y="def"


x.join(y)
# Out[202]:'dabceabcf'

x.join(z)
# Out[202]: 'dabceabcf'
复制代码

 

 

3五、举例说明异常模块中 try except else finally 的相关意义

复制代码
li = [1, 2, 3, 0]

for i in li:
    try:
         if i == 0:
             raise Exception("遍历获得0")
     except Exception as e:
         print(e)
     else:
         print(i)
     finally:
         print("顺利跑完")
 

# Out[28]:
1
顺利跑完
2
顺利跑完
3
顺利跑完
遍历获得0
顺利跑完


try: 放置可能出现异常的代码
except: 当出现异常时,执行此处代码
else: 当程序没有没有出现异常时,执行此处代码
finally:  无论程序是否出现异常,此处代码都会执行
复制代码

 

 

3六、举例说明zip()函数用法

 

 

3七、a="张明 98分",用re.sub,将98替换为100

res = re.sub(r'\d+', '100', a)
print(res)

 

 

3八、写5条经常使用sql语句

复制代码
# 增
insert into student (name, age) values ("power", 22);

# 删
delete from student where name="power";

# 改
update student set age=12 where name="power";

# 查
select name from student where id=2;
复制代码

 

 

3九、简述ORM

复制代码
O  模型类对象    R 关系    M 映射    :    模型类对象和数据库的表的映射关系


ORM拥有转换语法的能力,没有执行SQL 语句的能力,执行SQL语句须要安装数据库驱动(python3解释器需安装PyMySQL, python2解释器需安装mysql)django只能识别mysqldb驱动,须要给pymysql起别名骗过django

ORM做用:
1. 将面向对象的操做数据库的语法转换为相对应SQL语句
2. 解决数据库之间的语法差别性(根据数据库的配置不一样,生成不一样的SQL语句) 
复制代码

 

 

40、a="hello"和b="你好"编码成bytes类型

a="hello"
print(b'a')

b="你好"
print(b.encode())

 

  

4一、提升python运行效率的方法

复制代码
一、使用生成器,由于能够节约大量内存。 

二、循环代码的优化,避免重复执行循环代码。
 
三、核心模块用Cython  PyPy等,提升效率。
 
四、对于不一样场景使用多进程、多线程、协程,充分利用CPU资源。
 
五、多个if elif条件判断,将最有可能先发生的条件放到前面写,可减小程序判断的次数。
复制代码

 

 

4二、遇到bug如何处理 

复制代码
0、查看报错信息(错误日志信息),分析bug出现缘由。

一、在接口的开头打断点,单步执行往下走,逐渐缩小形成bug的代码范围。

二、【可选】在程序中经过 print() 打印,能执行到print() 说明通常上面的代码没有问题,分段检测程序是否有问题,若是是js的话能够alert或console.log

二、若本身没法解决,利用搜索引擎将报错信息进行搜索。

三、查看官方文档,或者一些技术博客。

四、查看框架的源码。 五、对于bug进行管理与归类总结,通常测试将测试出的bug用teambin等bug管理工具进行记录。
复制代码

 

 

4三、正则匹配,匹配日期2018-03-20

复制代码
a= 'aa20kk18-power03-30oo'

res = re.findall(r'[-\d]+', a)
data = ''.join(res)
print(data)

Out[82]: '2018-03-30'
复制代码

 

 

4四、list=[2,3,5,4,9,6],从小到大排序,不准用sort,输出[2,3,4,5,6,9]

复制代码
li = [2, 3, 5, 4, 9, 6]
lis = []
# 冒泡排序 for i in range(len(l)-1): for j in range(len(l)-1): if l[j] > l[j+1]: l[j],l[j+1] = l[j+1], l[j] Out[51]: [2, 3, 4, 5, 6, 9]


# 递归删除
def info():
  nu = min(li)
  li.remove(nu)
  lis.append(nu)
  
  # 若是原列表中仍有数据,再次调用自身进行删除&追加
  if len(li)> 0:
    info()
  print(lis)

info()

 Out[77]: [2, 3, 4, 5, 6, 9]

复制代码

 

 

4五、写一个单例模式

复制代码
# 第一种方法: 基类 __new__ 方法是真正建立实例对象的方法
class Info(object):
 def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):    
            cls._instance = super(Info, cls).__new__(cls, *args, **kwargs)
        return cls._instance


info = Info() 
info2 = Info()
print(id(foo), id(foo2))
#Out[106]: 140395100343696, 140395100343696


# 第二种方法 type元类是python建立类对象的类,类对象建立实例对象时必须调用 __call__方法
class Info(type):   
  def __call__(cls, *args, **kwargs):     
    if not hasattr(cls, '_instance'):    
      #cls._instance = super().__call__(*args, **kwargs)   
      cls._instance = super(Info, cls).__call__(*args, **kwargs)   
    return cls._instance


class Foo(object):   
  __metaclass__ = Info


f = Foo()
f1 = Foo()
print(id(f), id(f1))
#Out[122]: 139622152568912, 139622152568912



# 第三种方法:使用装饰器
def singleton(cls):
 instances = {} def wrapper(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return wrapper @singleton class Foo(object): pass foo1 = Foo() foo2 = Foo() print foo1 is foo2 # True


单例模式应用场景:
资源共享的状况下,避免因为资源操做时致使的性能或损耗等,如日志文件,应用配置。
控制资源的状况下,方便资源之间的互相通讯。如线程池等,1,网站的计数器 2,应用配置 3.多线程池 4数据库配置 数据库链接池 5.应用程序的日志应用...
复制代码

 

4六、保留两位小数

复制代码
a = 0.255
b = 22

# 保留2位小数
print("%0.2f" % a)

# 补足6位
print("%06d" % b)
复制代码

 

4七、分别从前端、后端、数据库阐述web项目的性能优化 

复制代码
前端内容优化:

(1)减小HTTP请求数:这条策略是最重要最有效的,由于一个完整的请求要通过DNS寻址,与服务器创建链接,发送数据,等待服务器响应,接收数据这样一个消耗时间成本和资源成本的复杂的过程。常见方法:合并多个CSS文件和js文件,利用CSS Sprites整合图像,Inline Images(使用 data:URL scheme在实际的页面嵌入图像数据 ),合理设置HTTP缓存等。
(2)减小DNS查找
(3)避免重定向
(4)使用Ajax缓存
(5)延迟加载组件,预加载组件
(6)减小DOM元素数量:页面中存在大量DOM元素,会致使javascript遍历DOM的效率变慢。
(7)最小化iframe的数量:iframes 提供了一个简单的方式把一个网站的内容嵌入到另外一个网站中。但其建立速度比其余包括JavaScript和CSS的DOM元素的建立慢了1-2个数量级。
(8)避免404:HTTP请求时间消耗是很大的,所以使用HTTP请求来得到一个没有用处的响应(例如404没有找到页面)是彻底没有必要的,它只会下降用户体验而不会有一点好处。



cookie优化:

(1)减少Cookie大小
(2)针对Web组件使用域名无关的Cookie



CSS优化:

(1)将CSS代码放在HTML页面的顶部
(2)避免使用CSS表达式
(3)使用<link>来代替@import
(4)避免使用Filters


JavaScript优化:

(1)将JavaScript脚本放在页面的底部。
(2)将JavaScript和CSS做为外部文件来引用:在实际应用中使用外部文件能够提升页面速度,由于JavaScript和CSS文件都能在浏览器中产生缓存。
(3)缩小JavaScript和CSS
(4)删除重复的脚本
(5)最小化DOM的访问:使用JavaScript访问DOM元素比较慢。
(6)开发智能的事件处理程序
(7)javascript代码注意:谨慎使用with,避免使用eval Function函数,减小做用域链查找。


HTML优化:

一、HTML标签有始终。 减小浏览器的判断时间

二、把script标签移到HTML文件末尾,由于JS会阻塞后面的页面的显示。

三、减小iframe的使用,由于iframe会增长一条http请求,阻止页面加载,即便内容为空,加载也须要时间

四、id和class,在能看明白的基础上,简化命名,在含有关键字的链接词中链接符号用'-',不要用'_'

五、保持统一大小写,统一大小写有利于浏览器缓存,虽然浏览器不区分大小写,可是w3c标准为小写

六、清除空格,虽然空格有助于咱们查看代码,可是每一个空格至关于一个字符,空格越多,页面体积越大,像google、baidu等搜索引擎的首页去掉了全部能够去掉的空格、回车等字符,这样能够加快web页面的传输。能够借助于DW软件进行批量删除 html内标签之间空格,sublime text中ctrl+a,而后长按shift+tab所有左对齐,清除行开头的空格

七、减小没必要要的嵌套,尽可能扁平化,由于当浏览器编译器遇到一个标签时就开始寻找它的结束标签,直到它匹配上才能显示它的内容,因此当嵌套不少时打开页面就会特别慢。

八、减小注释,由于过多注释不光占用空间,若是里面有大量关键词会影响搜索引擎的搜索

九、使用css+div代替table布局,去掉格式化控制标签如:strong,b,i等,使用css控制

十、代码要结构化、语义化

十一、css和javascript尽可能所有分离到单独的文件中

十二、除去无用的标签和空标签

1三、尽可能少使用废弃的标签,如b、i等,尽管高版本浏览器是向后兼容的



服务器优化:

(1)CDN:把网站内容分散到多个、处于不一样地域位置的服务器上能够加快下载速度。
(2)GZIP压缩
(3)设置ETag:ETags(Entity tags,实体标签)是web服务器和浏览器用于判断浏览器缓存中的内容和服务器中的原始内容是否匹配的一种机制。
(4)提早刷新缓冲区

 
后端优化:

1.SQL优化,最多见的方式是,优化联表查询,以及优化索引。这里面包括,尽可能使用left join 替代 where联表;当碰到,频繁查询字段A和字段B,以及AB联合查询的状况时,对AB作联合索引,可以有效的下降索引存储空间,提高查询效率。在复杂联表的状况下,能够考虑使用 Memory中间表。

2.主从数据库和读写分离,主从分库是用来应对访问量增长,带来频繁读写致使数据库的访问和操做性能降低的问题。对数据库的操做,为了保证数据的完整性,一般涉及到锁的机制的问题。MySQL的InnoDB引擎支持行级锁,而MyIsAM只支持表锁定。这样的话,若是读写集中在一个表中的状况下,当访问量增长,就会形成明显的性能降低。所以,经过主从数据库的方式能够实现读写分离。通常来讲,使用InnoDB来做为写库,使用MyISAM做为读库。这种方式是缺点固然是,数据库的维护难度增长,可是一般都会有专门的DBA这个职位来负责。

3.数据库分库和分表.有的时候会出现,某个表变得愈来愈庞大,好比存放message信息表,这样会形成读取性能的增长。这种状况下,你能够经过分表的方式来解决。将一个大表切分红若干个表。

4.使用存储过程当中,将一些操做,直接经过存储过程的方式,预先设置在MySQL,客户端只须要调用存储过程就能够操做数据。

5.对动态页面进行缓存,好比网站首页,内容页等这种查询次数高,数据改动不大的页面:应用程序读取数据时,先从缓存中读取,若是读取不到或数据已失效,再访问磁盘数据库,并将数据再次写入缓存。

  • 1)直接生成静态html页面,须要更新时经过后台成生成新页面进行覆盖。而后能够把静态页存放到本地硬盘或者同步到CDN上。
  • 2)使用vanish服务器做为方向代理,对php生成的动态页面进行缓存,因为vanish能够使用内存做为缓存,所以访问速度更快,且对生成页面的php代码不须要作任何的修改,就能够实现静态页面缓存。因此能够很好地解决由于一直遗留下来的问题致使代码修改为本高的状况。缺点也是,提高了运维成本。

6.浏览器缓存,经过Cache-Control,以及Last-Modified等控制缓存头的设置来告诉浏览器缓存页面。这样没必要每次,都从服务器端重复请求新文件,也能够防止用户频繁刷新页面。

 

  代码优化:

  • 不断经过for循环去进行查找
  • 错误的选择了容器,致使不断的遍历查找
  • 在循环中添加了不少能够移到循环以外只运行一次的函数调用
  • 能够实现成 事件触发的逻辑 变成了轮询
  • 函数中存在大量重复调用
  • 避免循环和判断次数太多,若是多个if else判断,将最有可能先发生的状况优先判断
  • 将耗时操做放入celery异步任务中 
复制代码

 

 

4八、简述同源策略

协议相同: http / https
域名(主机号)相同
端口相同

 

 

4九、简述cookie和session的区别 

复制代码
cookie数据存储在客户端的浏览器上,session数据存储在服务器端。

cookie
经过request对象获取cookie,经过response对象设置cookie,而且cookie的设置和读取不可以同步进行,好比:第一次设置cookie时不可以立马获取。Session的获取&设置都经过request对象进行。

cookie
不是很安全,别人能够分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。session存储在服务器上,相对于cookie更安全。
单个cookie保存的数据不能超过4K,不少浏览器都限制一个站点最多保存20个cookie。 session中多用于存储敏感、重要的信息,session依赖于cookie。

session会在必定时间内保存在服务器上。当访问增多,会比较占用服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE。

Session的存储过程: session经过request对象进行设置,假设将session数据(键值对)存储到数据库中,会生成一个session_id交由响应对象返回给浏览器,浏览器会将其保存在本地。以后每一次请求这个网站时会自动携带session_id,服务器会根据session_id取出整条session数据。注意:session是依赖于cookie的,可是在浏览器中是能够禁用掉cookie的。即:cookie被禁用,session也将失去意义,此条session记录在服务器中将不能取出。
 
 

五、建议:

 
 
  • 将登录信息等重要信息存放为SESSION

  • 其余信息若是须要保留,能够放在COOKIE中

复制代码

 

 

50、简述any()和all()方法 

any():只要迭代器中有一个元素为真就为真

all():迭代器中全部的判断项返回都是真,结果才为真

Python中为假的条件:False、None、[]、()、{}、""、0

 

 

5一、IOError、AttributeError、ImportError、IndentationError、IndexError、KeyError、SyntaxError、NameError分别表明什么异常

复制代码
IOError:输入输出异常
AttributeError:试图访问一个对象没有的属性
ImportError:没法引入模块或包,基本是路径问题
IndentationError:语法错误,代码没有正确的对齐
IndexError:下标索引超出序列边界
KeyError:试图访问你字典里不存在的键
SyntaxError:Python代码逻辑语法出错,不能执行
NameError:使用一个还未赋予对象的变量
复制代码

 

 

 

5二、单位转换

复制代码
    
    1 bytes = 8 bit
    1024 bytes = 1KB
    1024 KB = 1 MB
    1024 MB = 1 GB
    1024 GB = 1 TB
    1024 TB = 1 PB
    1024 PB = 1 EB
    1024 EB = 1 ZB

10进制,人类使用。
2进制, 供计算机使用,1,0表明开和关,有和无,机器只认识2进制。
16进制,内存地址空间是用16进制的数据表示, 如0x8049324。

十进制转二进制: 十进制数除2取余法,即十进制数除2,余数为权位上的数,获得的商值继续除2,依此步骤继续向下运算直到商为0为止。

二进制转十进制: 把二进制数按权展开、相加即得十进制数。

二进制转八进制: 3位二进制数按权展开相加获得1位八进制数。(注意事项,3位二进制转成八进制是从右到左开始转换,不足时补0)。

八进制转成二进制: 八进制数经过除2取余法,获得二进制数,对每一个八进制为3个二进制,不足时在最左边补零。

二进制转十六进制:与二进制转八进制方法近似,八进制是取三合一,十六进制是取四合一。(注意事项,4位二进制转成十六进制是从右到左开始转换,不足时补0)。

十六进制转二进制: 十六进制数经过除2取余法,获得二进制数,对每一个十六进制为4个二进制,不足时在最左边补零。

十进制转八进制&十进制转十六进制:1.间接法—把十进制转成二进制,而后再由二进制转成八进制或者十六进制。  2.直接法—把十进制转八进制或者十六进制按照除8或者16取余,直到商为0为止。

八进制转十进制&十六进制转十进制:把八进制、十六进制数按权展开、相加即得十进制数。

八进制与十六进制之间的转换:1.先转成二进制而后再相互转换。  2.他们之间的转换能够先转成十进制而后再相互转换。

复制代码

 

 

5三、列出几种魔法方法并简要介绍用途

复制代码
__new__(cls[, ...])       1.实例化对象时第一个被调用的方法     2.其参数直接传递给__init__方法处理

__init__(self[, ...])       构造方法,初始化类的时候被调用
__del__(self)            析构方法,当实例化对象被完全销毁时被调用(实例化对象的全部指针都被销毁时被调用)
__call__(self[, args...])     容许一个类的实例像函数同样被调用:x(a, b) 调用 x.__call__(a, b)
__len__(self)            定义当被 len() 调用时的行为
__repr__(self)            定义当被 repr() 调用时的行为
__str__(self)            定义当被 str() 调用时的行为
__bytes__(self)           定义当被 bytes() 调用时的行为
__hash__(self)           定义当被 hash() 调用时的行为
__bool__(self)           定义当被 bool() 调用时的行为,应该返回 True 或 False
__format__(self, format_spec)        定义当被 format() 调用时的行为
复制代码

 


5四、a = "  hehheh  ",去除首尾空格

a.strip()
Out[95]: 'hehheh'

 

 

 55、用两种方法去除空格

复制代码
# 第一种方法:使用split方法分割
a = ' hello world '
b = a.split()
c = ''.join(b)
print(c)
#Out[22]:  ''helloworld''


# 第二种方法:使用re正则
a = ' hello world '
b = re.sub(' ', '', a)
print(b)
#Out[23]:  ''helloworld''


# 第三种方法:使用strip方法去除首尾空格
a = ' helloworld  '
b = a.strip()
print(b)
#Out[24]:  'helloworld'
# rstrip():去除尾部空格; lstrip():去除首部空格
复制代码

 

 

 

 

 

5六、举例sort和sorted对列表排序,list=[0,-1,3,-10,5,9]

复制代码
li=[0,-1,3,-10,5,9]


li.sort()
print(li)
Out[90]: [-10, -1, 0, 3, 5, 9]


lis = sorted(li)
print(lis)
Out[90]: [-10, -1, 0, 3, 5, 9]
复制代码

 

 

 57、对list排序foo = [-5,8,0,4,9,-4,-20,-2,8,2,-4],使用lambda函数从小到大排序

复制代码
foo = [-5,8,0,4,9,-4,-20,-2,8,2,-4]

# 第一种方式
g = lambda x: x.sort()
g(foo)
print(foo)
# Out[102]: [-20, -5, -4, -4, -2, 0, 2, 4, 8, 8, 9]

# 第二种方式
a = sorted(foo, key=lambda x: x)
print(a)
复制代码

 

 

5八、使用lambda函数对list排序foo = [-5,8,0,4,9,-4,-20,-2,8,2,-4],输出结果为[0,2,4,8,8,9,-2,-4,-4,-5,-20],正数从小到大,负数从大到小

复制代码
foo = [-5, 8, 0, 4, 9, -4, -20, -2, 8, 2, -4]

g = sorted(foo, key=lambda x:(x<0, abs(x)))

print(g)

Out[113]: [0, 2, 4, 8, 8, 9, -2, -4, -4, -5, -20]
复制代码

 

 

5八、字典分别按照key、value排序 

复制代码
a = {'y': 2, 'x':1, 'z': 3}

# 按照key排序 - 升序
g = sorted(a.items(), key=lambda x: x[0])
dict(g)

[Out]:{'x': 1, 'y': 2, 'z': 3}


# 按照key排序 - 降序
g = sorted(a.items(), key=lambda x: x[0], reverse=True)
dict(g)

[Out]: {'z': 3, 'y': 2, 'x': 1}


# 按照value排序 - 升序
g = sorted(a.otems(), key=lambda x: x[1])
dict(g)

[Out]: {'x': 1, 'y': 2, 'z': 3}


# 按照value排序 - 降序
g = sorted(a.items(), key=lambda x: x[1], reverse=True)
dict(g)

[Out]: {'z': 3, 'y': 2, 'x': 1}
复制代码

 

 

 

5九、列表嵌套字典,根据年龄排序

alist = [{'name':'a','age':20},{'name':'b','age':30},{'name':'c','age':25}]

def sort_by_age(list1):
  
  return sorted(alist, key=lambda x:x['age'], reverse=True)

 

 

60、如下代码的输出结果,为何?

复制代码
list = ['a','b','c','d','e']
print(list[10:])


代码将输出[],不会产生IndexError错误,就像所指望的那样,尝试用超出成员的个数的index来获取某个列表的成员。例如,尝试获取list[10]和以后的成员,会致使IndexError。
然而,尝试获取列表的切片,开始的index超过了成员个数不会产生IndexError。
复制代码

 

 

6一、给定两个列表,怎么找出他们相同的元素和不一样的元素?

A = [1,2,3]
B = [3,4,5]

A,B 中相同元素: print(set(A) & set(B))

A,B 中不一样元素:  print(set(A) ^ set(B))

 

 

6二、列表推导式、字典推导式、生成器

复制代码
# 列表推导式
li = [i for i in range(1,101) if i % 2 ==0]

# 字典推导式
a = [0, 1, 2]
b = ['a', 'b', 'c']
c = {a[i]:b[i] for i in range(len(a))}

# 生成器
def info(num):
  
  offset = 0
  a = 0
  b = 1

  while offset < num:
    result = a
    a, b = b, a+b
    offset += 1
    yield result

print(list(info(10)))   
复制代码

 

 

6三、正则匹配IPV4地址

IPV4由四组数字组成,中间由.隔开:

a = "169.254.0.112"
 re.search(r'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$', a).group()

 

 

6四、举例说明SQL注入和解决办法

复制代码
产生缘由:程序开发过程当中不注意规范书写 sql 语句和对特殊字符进行过滤,致使客户端能够经过全局变量POST 和 GET 提交一些 sql 语句正常执行。产生 Sql 注入。

防止办法:
    a. 过滤掉一些常见的数据库操做关键字,或者经过系统函数来进行过滤。
    b. 对传入的参数进行编码转义。
    c. SQL 语句书写的时候尽可能不要省略小引号(tab 键上面那个)和单引号
    d. 提升数据库命名技巧,对于一些重要的字段根据程序的特色命名,取不易被猜到的
    e. 使用Python的pymysql模块自带的方法,给execute传递两个参数,第一个是SQL语句,第二个是列表格式的SQL语句所需参数
复制代码

 

 

 

6五、s="info:xiaoZhang 33 shandong",用正则切分字符串输出['info', 'xiaoZhang', '33', 'shandong']

复制代码
s = 'info:xiaoZhang 33 shandong'

# 正则方式
g = re.findall(r'[^:\s]+', s)

 Out[162]: ['info', 'xiaoZhang', '33', 'shandong']




# spilt分隔方式
li = [] li.extend(s.split(":")[0].split() + s.split())

Out[156]: ['info', 'xiaoZhang', '33', 'shandong']
复制代码

 

 

6六、正则匹配以163.com结尾的邮箱 

re.match(r'[0-9a-zA-Z_]{0,19}@163.com', text).group()

 

 

6七、递归求和 

复制代码
def info(num):

    if num > 0:
        res = num + info(num - 1)
    else:
        res = 0
    return res


res = info(10)
print(res)
复制代码

 

 

6八、python字典和json字符串相互转化方法

json.dumps()   字典转json字符串
json.loads()     json转字典

 

 

6九、快排

复制代码
def info(li):
    
    midpivot = lil[0]
    lesspivot = [i for i in li[1:] if i <= midpivot]
    biggpivot = [i for i in li[1:] if i > midpivot]
    lesspivot.sort()
    biggpivot.sort()

    result = lesspivot + [midpivot] + biggpivot
    print(result)
复制代码

 

 

 

70、RESTFul风格 

复制代码
# 一、后端API定义规范
请求方法    请求地址    后端操做
GET        /goods     获取全部商品
POST     /goods      增长商品
GET       /goods/1    获取编号为1的商品
PUT       /goods/1    修改编号为1的商品
DELETE    /goods/1    删除编号为1的商品

# 二、尽可能将API部署在专用域名之下。
https://api.example.com

# 将API的版本号放入URL。
http://www.example.com/app/2.0/foo

# 三、路径
资源做为网址,只能有名词,不能有动词,并且所用的名词每每与数据库的表名对应。
API中的名词应该使用复数。不管子资源或者全部资源。

# 四、错误处理
服务器向用户返回出错信息时,返回的信息以error做为键名,出错信息做为value值便可。

# 五、返回结果规范
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档

# 六、超媒体
访问api.github.com会获得一个全部可用API的网址列表。

# 七、服务器返回的数据格式应尽可能为json格式,避免使用xml
复制代码

 

 

7一、列举3条以上PEP8编码规范

复制代码
分号:不要在行尾加分号, 也不要用分号将两条命令放在同一行.

行长度:每行不超过80个字符

缩进:用4个空格来缩进代码,绝对不要用tab, 也不要tab和空格混用

空行:顶级定义之间空两行, 方法定义之间空一行

空格:按照标准的排版规范来使用标点两边的空格,括号内不要有空格,不要在逗号、分号、 冒号前面加空格, 但应该在它们后面加(除了在行尾),参数列表, 索引或切片的左括号前不该加空格.
导入格式:每一个导入应该独占一行 
类:类应该在其定义下有一个用于描述该类的文档字符串. 若是你的类有公共属性(Attributes), 那么文档中应该有一个属性(Attributes)段. 而且应该遵照和函数参数相同的格式

继承:若是一个类不继承自其它类, 就显式的从object继承. 嵌套类也同样.
复制代码

 

7二、正则匹配中文

复制代码
import re

title = "Hello World, 你好 世界"

pattern = re.compile(r'[\u4e00-\u9fa5]+')
res = pattern.findall(title)

print(res)
复制代码

 

 

7三、Linux命令重定向 > 和 >>

复制代码
Linux 容许将命令执行结果 重定向到一个 文件

将本应显示在终端上的内容 输出/追加 到指定文件中

> 表示输出,会覆盖文件原有的内容

>> 表示追加,会将内容追加到已有文件的末尾
复制代码

 

 

7四、正则表达式匹配出<html><h1>www.baidu.com</h1></html>

g = re.match(r'<(\w+)><(\w+)>(.*)</\2></\1>', a)
g.group()

 

 

7五、遍历列表的同时删除元素

复制代码
# 1.新表用于遍历,旧表用于删除元素
a = [1, 2, 3, 4, 5, 6, 7, 8]
print(id(a))
print(id(a[:]))
for i in a[:]:
    if i>5:
        pass
    else:
        a.remove(i)
    print(a)
print('-----------')
print(id(a))


# 2.filter
a = [1, 2, 3, 4, 5, 6, 7, 8]
b = filter(lambda x: x>5, a)
print(list(b))


# 3.列表推导式
a = [1, 2, 3, 4, 5, 6, 7, 8]
b = [i  for i in a if i > 5]
print(b)


# 4.倒序删除
a = [1, 2, 3, 4, 5, 6, 7, 8]
print(id(a))
for i in range(len(a)-1,-1,-1):
    if a[i]>5:
        pass
    else:
        a.remove(a[i])

print(id(a))
print('-----------')
print(a)
复制代码

 

 

 

7六、常见的网络传输协议

复制代码
HTTP(80)      浏览器的网络请求 协议
HTTPS(443)    浏览器的网络请求 协议
TCP        数据传输协议
UDP        数据传输协议
SMTP(25)    发邮件协议    
TEL      电话协议  
SMS     短信协议     
POP3    收邮件协议 (需通过邮件服务器,从服务器拉去最新邮件时,服务器不作缓存) 
IMAP    收邮件协议(即便本地邮件删除,服务器上还有备份的邮件) 
HHT     文件传输协议 
复制代码

 

 

7七、8个老师随机分配3个办公室, 每一个办公室人数在2~3人

复制代码
import random

room_nums = 0  # 办公室数量默认是0
teacher_nums = 0  # 教师数量默认是0
while True:
    r_nums = input("请输入办公室的个数:")
    t_nums = input("请输入老师的个数:")

    # 若是教师数目大于等于办公室数目,才退出循环,并赋值给room_nums、teacher_nums
    # 不然从新输入
    if int(r_nums) <= int(t_nums):
        room_nums = int(r_nums)
        teacher_nums = int(t_nums)
        break
    else:
        print("老师数低于办公室数,请从新输入")


# 建立办公室列表,即建立一个嵌套列表 大列表:办公室  小列表:老师
rooms = []
while room_nums >= 1:
    rooms.append([])
    room_nums -= 1

# 建立老师列表,并添加老师
teachers = []
while teacher_nums>= 1:
    teacher_nums -= 1
    teachers.append("teacher%d"%(teacher_nums+1))

# 开始安排办公室
# 1.先随机选出三位老师,依次放到办公室中
for room in rooms:
    # 随机选出一名老师,注意teachers长度会变
    index = random.randint(0, len(teachers)-1)
    # pop方法弹出一个元素,并列表中删除
    teac = teachers.pop(index)
    room.append(teac)

# 2.将剩下的老师,再随机分配
for teacher in teachers:
    room_index = random.randint(0, len(rooms)-1)
    rooms[room_index].append(teacher)
print("分配结束后:", rooms)
复制代码

 

 

 

7八、静态函数、类函数、成员函数的区别

复制代码
类方法: 是类对象的方法,在定义时须要在上方使用 @classmethod 进行装饰,形参为cls,表示类对象,类对象和实例对象均可调用

类实例方法: 是类实例化对象的方法,只有实例对象能够调用,形参为self,指代对象自己;

静态方法: 是一个任意函数,在其上方使用 @staticmethod 进行装饰,能够用对象直接调用,静态方法实际上跟该类没有太大关系

\ 实例方法 类方法 静态方法
a = A() a.foo(x) a.class_foo(x) a.static_foo(x)
A 不可用 A.class_foo(x) A.static_foo(x)
 
 
复制代码

 

 

7九、变量查找顺序

复制代码
L: local 函数内部做用域(本地做用域)

E: enclosing 函数内部与内嵌函数之间

G: global 全局/模块做用域

B: build-in 内置做用域
复制代码

 

 

79.字符串 "123" 转换成 123,不使用内置api,例如 int()

复制代码
# 方法一:利用 str方法
def atoi(s):
    num = 0
    for v in s:
        for j in range(10):
            if v == str(j):
                num = num * 10 + j
    return num


# 方法二:利用 ord方法
def atoi(s):
    num = 0
    for v in s:
        num = num * 10 + ord(v) - ord('0')
    return num


# 方法三:利用 eval方法
def atoi(s):
    num = 0
    for v in s:
        t = "%s * 1" % v
        n = eval(t)
        num = num * 10 + n
    return num


# 方法四:使用 reduce 并结合方法二
from functools import reduce
def atoi(s):
    return reduce(lambda num, v: num * 10 + ord(v) - ord('0'), s, 0)
复制代码

 

 

80、MySQL的事务隔离级别

复制代码
未提交读(Read Uncommitted):容许脏读,其余事务只要修改了数据,即便未提交,本事务也能看到修改后的数据值。也就是可能读取到其余会话中未提交事务修改的数据
提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)。
可重复读(Repeated Read):可重复读。不管其余事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其余事务影响。
串行读(Serializable):彻底串行化的读,每次读都须要得到表级共享锁,读写相互都会阻塞

MySQL数据库(InnoDB引擎)默认使用可重复读( Repeatable read)


命令行修改数据库事务隔离级别:transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE}
配置文件中修改数据库事务隔离级别(最后一行进行添加):transaction-isolation={READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE}

===============================================================================================================
       隔离级别               脏读(Dirty Read)          不可重复读(NonRepeatable Read)     幻读(Phantom Read) 
===============================================================================================================

未提交读(Read uncommitted)        可能                            可能                      可能

已提交读(Read committed)          不可能                          可能                       可能

可重复读(Repeatable read)         不可能                          不可能                     可能

可串行化(Serializable )           不可能                          不可能                     不可能

===============================================================================================================

 

脏读: 是指事务T1将某一值修改,而后事务T2读取该值,此后T1由于某种缘由撤销对该值的修改,这就致使了T2所读取到的数据是无效的。

当一个事务正在访问数据,而且对数据进行了修改,而这种修改尚未提交到数据库中,这时,另一个事务也访问这个数据,而后使用了这个修改了还未提交的数据。  

栗子: 1.Mary的原工资为1000, 财务人员将Mary的工资改成了8000(但未提交事务)
      2.Mary读取本身的工资 ,发现本身的工资变为了8000,欢天喜地!
      3.而财务发现操做有误,回滚了事务,Mary的工资又变为了1000
          像这样,Mary记取的工资数8000是一个脏数据。

 

不可重复读 :是指在数据库访问时,一个事务范围内的两次相同查询却返回了不一样数据。在一个事务内屡次读同一数据。在这个事务尚未结束时,另一个事务也访问该同一数据。那么在第一个事务中的两次读数据之间,因为第二个事务的修改,第一个事务两次读到的的数据多是不同的。这样在一个事务内两次读到的数据是不同的,所以称为是不可重复读。

栗子1.在事务1中,Mary 读取了本身的工资为1000,操做并无完成。

     2.在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务。
     3.在事务1中,Mary 再次读取本身的工资时,工资变为了2000。

解决办法:若是只有在修改事务彻底提交以后才能够读取数据,则能够避免该问题。

 

幻读: 是指当事务不是独立执行时发生的一种现象,好比第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的所有数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么就会发生,操做第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉同样。 

栗子:目前工资为1000的员工有10人。
     1.事务1,读取全部工资为1000的员工。
     2.这时事务2向employee表插入了一条员工记录,工资也为1000
    3.事务1再次读取全部工资为1000的员工 共读取到了11条记录, 
解决办法:通常解决幻读的方法是增长范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。若是在操做事务完成数据处理以前,任何其余事务都不能够添加新数据,则可避免该问题

 

丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而致使第一个事务更新的数据丢失,这是因为没有加锁形成的;

复制代码

 

 

8一、TCP协议的 TIME WAITE 状态

复制代码
# 1.TIME WAITE状态:
是指主动关闭方在发送四次挥手的最后一个ACK后会进入TIME_WAIT状态,也就是这个发起关闭的一方会保持2MSL时间以后才会回到初始状态。(linux里一个MSL为30s,是不可配置的)

# 2.MSL值是数据包在网络中的最大生存时间。当主动关闭方进入TIME WAITE状态后,会使得这个TCP链接在2MSL链接等待期间,定义这个链接的四元数组(local_ip, local_port, remote_ip,remote_port)不能被使用。

# 3.TIME_WAIT状态产生的缘由:
虽然双方都赞成关闭链接了,并且握手的4个报文也都协调和发送完毕,按理能够直接回到CLOSED状态(就比如从SYN_SEND状态到ESTABLISH状态那样);但由于咱们必需要假想网络是不可靠的,你没法保证你最后发送的ACK报文会必定被对方收到
假设发起主动关闭的一方(client)四次挥手时最后发送的ACK在网络中丢失,但因为TCP协议的重传机制,执行被动关闭的一方(server)将会重发上一个数据包FIN,因此这个TIME_WAIT状态的做用就是用来重发可能丢失的ACK报文。(TCP属于全双工链接) # 4.TIME_WAIT两个MSL的做用: 可靠安全的关闭TCP链接。好比网络拥塞,主动方最后一个ACK被动方没收到,这时被动方会对FIN开启TCP重传,发送多个FIN包,在这时还没有关闭的TIME_WAIT就会把这些尾巴问题处理掉,不至于对新链接及其它服务产生影响。 # 5.TIME_WAIT状态如何避免(端口复用): 若是服务器程序中止后想当即重启,而新的套接字依旧但愿使用同一端口,能够经过(SO_REUSEADDR, True)选项避免TIME_WAIT状态。
复制代码

 

 

8二、网络的七层协议

复制代码
OSI的7层从上到下分别是:
  7 应用层 6 表示层 5 会话层 4 传输层 3 网络层 2 数据链路层 1 物理层 
  其中高层(即七、六、五、4层)定义了应用程序的功能,下面3层(即三、二、1层)主要面向经过网络的端到端的数据流。

应用层:与其它计算机进行通信的一个应用,它是对应应用程序的通讯服务的。TELNET,HTTP,FTP,NFS,SMTP

表示层:定义数据格式及加密。加密,ASCII等。

会话层:定义了如何开始、控制和结束一个会话,包括对多个双向消息的控制和管理,以便在只完成连续消息的一部分时可通知应用,表示层看到的数据是连续的,本质用于区分不一样进程的。RPC,SQL等。

传输层:负责将数据进行可靠或者不可靠传递,负责终端之间的传送。即:选择差错恢复协议仍是无差错恢复协议,以及在同一主机上对不一样应用的数据流输入进行复用,还包括对收到顺序不对的数据包的从新排序功能。TCP,UDP,SPX。

网络层:负责选择最佳路径,并保证数据始终沿着最佳路径传输,以数据包为单位。IP,IPX等。

数据链路层:在单个链路上如何传输数据,好比对数据进行处理封装成数据帧,进行传递和错误检测的就是数据链路层,数据以帧为单位。ATM,FDDI等。

物理层:为网络设备之间的数据通讯提供传输媒体及互连设备,为数据传输提供可靠的环境,数据是以比特的形式传递的。Rj45,802.3等。


栗子:从西班牙去罗马的贸易商人
一、 要想贸易得到成功,首先要有至少一条路,可以从西班牙通向罗马。此层为【物理层】
二、有了路是否是就能去贸易了?还要保证路上不会把商人的货物给磕坏了,要有一层保护的包装。引出第二层,【数据链路层】
三、所谓条条道路通罗马。并不仅有一条路可以到达罗马,那么在那么多的选择中选一条最短的,或者路费的成本最少的,这才符合商人的利益。引出第三层,【网络层】以上三层为网络中的下三层,叫媒体层,让咱们来看看另外4层。
四、贸易出门前要先检查一下本身的货,有没有拿错了,事先要检查过,若是错了要从新取货,引出第四层,【传输层】。
五、是否是能够上路了?还不行。咱们要和罗马联系好, 若是咱们这边的货物到了那边卖不出去怎么办?咱们首先要交流、协商一下,看看罗马的市场状况,能和那边的另一个商人合做的话就更好了,这就须要一些外交的关系。叫作【会话层】。
六、好象全部的事情都准备好了,可是商人到了罗马之后忽然发现,他的商队里没有人能听懂罗马人的话,罗马人也没有人能听懂西班牙语,这个时候,还须要一个翻译,要么把两种语言都转换成一种国际通用语言,好比说英语,要么至少能让双方能交流。这里就是【表示层】。
七、到了罗马了,最终须要在交易所中把商品卖掉,这个交易所就是一个交易平台,至关于各个软件平台,引出最后一层,【应用层】。
复制代码

 

 

8三、HTTP和TCP的keep-alive区别

复制代码
一、keep-alive(持久链接/长链接):Http是一个协议,它的keep-alive主要是为了使用同一个TCP链接来发送和接收多个HTTP请求/应答,而不是为每个新的请求/应答打开新的链接的方法。能够减小tcp链接创建次数,也意味着能够减小TIME_WAIT状态链接,以此提升性能(更少的tcp链接意味着更少的系统内核调用,socket的accept()和close()调用)。

二、HTTP的keep-alive:   HTTP 1.0版本中默认没有keep-alive操做,是根据浏览器是否支持keep-alive而决定的,若是浏览器支持的话,它会在请求报头中添加 'Connection: Keep-Alive' 字段,而服务器作出响应时也会在响应报头中添加 'Connection: Keep-Alive',这样就会保持当前的链接而不会中断。HTTP 1.1版本中默认全部都是持久链接。 须要特别注意的是:长时间的tcp链接容易致使系统资源无效占用。配置不当的keep-alive,有时比重复利用链接带来的损失还更大。因此,正确地设置keep-alive timeout时间很是重要。keep alive_timout时间值意味着:一个http产生的tcp链接在传送完最后一个响应后,还须要hold住keep alive_timeout秒后,才开始关闭这个链接。 三、TCP的keep-alive:
Tcp的keep-alive是Tcp协议的一种保鲜装置,当连接创建以后,若是应用程序或者上层协议一直不发送数据,或者隔很长时间才发送一次数据的状况下,TCP协议须要考虑当连接好久没有数据报文传输时肯定对方是否还在线,究竟是掉线了仍是确实没有数据传输,连接还需不须要保持。在此状况下,当超过一段时间以后,服务器自动发送一个监测包(数据为空的报文)
若是对方回应了这个报文,说明对方还在线,连接能够继续保持,若是对方没有报文返回,而且重试了屡次以后则认为连接丢失,没有必要保持连接。


四、http keep-alive与tcp keep-alive: http keep-alive是为了让tcp活得更久一点,以便在同一个链接上传送多个http,提升socket的效率。而tcp keep-alive是TCP的一种检测TCP链接情况的机制。
tcp keepalive原理,当网络两端创建了TCP链接,但双方没有任何数据流发送往来,服务器内核就会尝试向客户端发送侦测包,来判断TCP链接情况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。若是没有收到对方的回答(ack包),则会在指定x时间后再次尝试发送侦测包,直到收到对方的ack包,若是一直没有收到对方的ack,一共会尝试y次数。若是重试y次数后,依然没有收到对方的ack包,则会丢弃该TCP链接。

”请求-响应”

长链接概念:所谓长链接,指在一个TCP链接上能够连续发送多个数据包,在TCP链接保持期间,若是没有数据包发送,须要双方发检测包以维持此链接。链接->传输数据->保持链接 -> 传输数据 -> 直到一方关闭链接

短连接概念:短链接是指通讯双方有数据交互时,就创建一个TCP链接,数据发送完成后,则断开此TCP链接,即每次TCP链接只完成一对 CMPP消息的发送。链接->传输数据->关闭链接 

      浏览器client发起并创建TCP链接 -> client发送HttpRequest报文 -> server接收到报文->server handle并发送HttpResponse报文给前端->
      发送完毕以后当即调用socket.close方法->client接收response报文->client最终会收到server端断开TCP链接的信号->client 端断开TCP链接,调用close方法。

  • HTTP 协议的 KeepAlive 意图在于链接复用,同一个链接上串行方式传递请求-响应数据
  • TCP 的 KeepAlive 机制意图在于保活、心跳,检测链接错误。

Redis设置长链接:修改redis.conf配置文件的tcp-keepalive=1(启用长链接), 默认为0(表示禁止长链接)

复制代码

 

 

8四、当保持长链接时,如何判断一次请求已经完成? 

复制代码
Content-Length 
Content-Length表示实体内容的长度。浏览器经过这个字段来判断当前请求的数据是否已经所有接收。 因此,当浏览器请求的是一个静态资源时,即服务器能明确知道返回内容的长度时,能够设置Content-Length来控制请求的结束。
但当服务器并不知道请求结果的长度时,如一个动态的页面或者数据,Content-Length就没法解决上面的问题,这个时候就须要用到Transfer-Encoding字段。 Transfer-Encoding Transfer-Encoding是指传输编码,在上面的问题中,当服务端没法知道实体内容的长度时,就能够经过指定Transfer-Encoding: chunked来告知浏览器当前的编码是将数据分红一块一块传递的。
固然, 还能够指定Transfer-Encoding: gzip, chunked代表实体内容不只是gzip压缩的,仍是分块传递的。最后,当浏览器接收到一个长度为0的chunked时, 知道当前请求内容已所有接收。
复制代码

 

 

8五、TCP&UDP的区别

复制代码
TCP :
是一种面向链接的、可靠的字节流服务,一个客户端和一个服务器在发送数据以前必须先三次握手创建链接。 在一个 TCP 链接中,仅有两方进行彼此通讯。广播和多播不能用于 TCP。
TCP 采用发送应答机制,所发送的每一个报文段都必须获得接收方的应答才认为这个TCP报文段传输成功。
TCP 协议的超时重传机制,发送一个报文段后就启动定时器,若在必定时间内未收到对方应答就会从新发送这个报文段。
TCP 为了保证不发生丢包,会给每个包标上序号,也保证了消息的有序性。该消息从服务器端发出顺序会以一样的顺序发送到客户端。
由于TCP必须建立链接,以保证消息的可靠交付和有序性,因此相对于UDP而言TCP速度比较慢。
TCP 数据包报头的大小是20字节,UDP数据报报头是8个字节。TCP报头中包含序列号,ACK号,数据偏移量,保留,控制位,窗口,紧急指针,可选项,填充项,校验位,源端口和目的端口。而UDP报头只包含长度,源端口号,目的端口,和校验和。
TCP 使用滑动窗口机制来实现流量控制,经过动态改变窗口的大小进行拥塞控制,以此避免主机发送得过快而使接收方来不及彻底收下。
TCP 用⼀个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
TCP 使用发送应答机制、超时重传、错误校验、流控和阻塞管理来保证可靠传输。
注意:TCP 并不能保证数据必定会被对方接收到,由于这是不可能的。TCP 可以作到的是,若是有可能,就把数据递送到接收方;不然就(经过放弃重传而且中断链接这一手段)通知用户。所以准确说 TCP 也不是 100% 可靠的协议,它所能提供的是数据的可靠递送或故障的可靠通知。
UDP :
是无链接的,不可靠的,没有序列保证,可是一个快速传输的数据报协议。
UDP 缺少可靠性。UDP 自己不提供确认,序列号,超时重传等机制。UDP 数据报可能在网络中被复制,被从新排序。即 UDP 不保证数据报会到达其最终目的地,也不保证各个数据报的前后顺序,也不保证每一个数据报只到达一次
UDP 数据报是有长度的。每一个UDP数据报都有长度,若是一个数据报正确地到达目的地,那么该数据报的长度将随数据一块儿传递给接收方。而 TCP 是一个字节流协议,没有任何(协议上的)记录边界。
UDP 是无链接的。UDP 客户和服务器以前没必要存在长期的关系。UDP 发送数据报以前也不须要通过握手建立链接的过程。
UDP 支持多播和广播。
复制代码

 

 8六、闭包是什么?

闭包必须知足的三个条件:
一、一个外函数中定义了一个内函数。
二、内嵌函数中引用了外部函数的变量。
三、外部函数的返回值是内嵌函数的引用。

 

 

8七、中间件是什么?

复制代码
中间件是一种独立的系统软件或服务程序,处在操做系统软件与用户的应用软件的中间,分布式应用软件借助这种软件在不一样的技术之间共享资源(网络通讯功能),管理计算机资源和网络通信,相链接的系统,即便它们具备不一样的接口,但经过中间件相互之间仍然能交换信息,以便于运行在一台或多台机器上的多个软件经过网络进行交互。
中间件的六大分类:终端仿真/屏幕转换中间件、数据访问中间件、远程过程调用中间件、消息中间件、交易中间件、对象中间件。
 提供 分布环境下通信服务的中间件:远程过程调用中间件(Remote Procedure Call)、面向消息的中间件(MesSAge-Oriented Middleware)、对象请求代理中间件(object RequeST Brokers)
  RPC远程过程调用中间件:主要用于client/server分布式计算
  
  MOM面向消息
中间件:指的是利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通讯来进行分布式系统的集成,提供消息传递和消息排队模型,可在分布环境下扩展进程间的通讯。
      好比:IBM的MQSeries、BEA的MessageQ等;消息传递和排队技术特色:通信程序可在不一样的时间运行、对应用程序的结构没有约束、程序与网络复杂性相隔离
  ORB对象请求代理中间件:提供一个通讯框架,透明地在异构的分布计算环境中传递对象请求

django中,中间件其实就是一个类,监听请求进入视图以前 响应对象真正到达浏览器以前的中间执行过程,django会根据本身的规则在合适的时机执行中间件中相应的方法。在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每个元素就是一个中间件
中间件中一共有四个方法: __init__     # 初始化:无需任何参数,服务器响应第一个请求的时候调用一次,用于肯定是否启用当前中间件 process_request(request)    # 处理请求前:在每一个请求上调用,返回None或HttpResponse对象。 process_view(request,view_func,view_args,view_kwargs)      # 处理视图前:在每一个请求上调用,返回None或HttpResponse对象。
process_template_response(request,response)  # 处理模板响应前:在每一个请求上调用,返回实现了render方法的响应对象。
process_exception(request,exception)  # 当视图抛出异常时调用,在每一个请求上调用,返回一个HttpResponse对象。 process_response(request,response)  # 处理响应后:全部响应返回浏览器以前被调用,在每一个请求上调用,返回HttpResponse对象。 Django自定义中间件类时,必须继承自MiddlewareMixin父类:from django.utils.deprecation import MiddlewareMixin
Flask中的请求钩子也相似于中间件,经过装饰器实现的,支持如下四种: before_first_request:第一次请求以前,能够在此方法内部作一些初始化操做。 before_request:每次请求以前,能够对不一样请求的url处理不一样的业务逻辑。 after_request:每次请求以后,能够使用response对象统一设置cookie。 teardown_request:每次请求以后是否有错误,会接受一个参数,参数是服务器出现的错误信息。
复制代码

 

 

 

8八、深浅拷贝

复制代码

浅拷贝:copy.copy(变量);若是浅拷贝的是不可变对象,并无产生开辟新的内存空间;若是浅拷贝的是简单的可变对象,会产生一块新的内存空间,修改原来值不影响拷贝值;若是浅拷贝的是复杂的可变对象,浅拷贝只拷贝父对象,不会拷贝对象内部的子对象,若是原来值的内部子对象发生改变,会影响到拷贝值,内部子对象仅仅是引用拷贝。浅拷贝有三种形式:切片操做、工厂函数(list(a))、copy模块中的copy函数
深拷贝:copy.deepcopy(变量);若是深拷贝的是不可变对象,仍然没有开辟新的内存空间;若是深拷贝的是可变对象,则会产生一块新的内存空间,修改原来值也不会影响拷贝值;若是深拷贝的是复杂的可变对象,深拷贝会拷贝父对象及其对象内部的全部子对象,若是原来值的内部子对象发生改变,不会影响到拷贝值,两者是独立存在,互不影响。

只要是拷贝不可变对象,不管浅拷贝仍是深拷贝都是引用拷贝,都指向同一个内存空间;
复制代码

 

 

8九、Redis缓存击穿、缓存穿透、缓存雪崩

复制代码
缓存穿透
概念:缓存穿透是指查询一个必定不存在的数据,因为缓存是请求数据不命中时被动写入的,而且出于容错考虑,若是从存储层查不到数据则不写入缓存,这将致使这个不存在的数据每次请求都要到存储层去查询,在流量大时数据库可能就挂掉了,通俗说就是恶意用户模拟请求不少缓存中不存在的数据,因为缓存中都没有,致使这些请求短期内直接落在了数据库上,致使数据库异常。从系统层面来看像是穿透了缓存层直接达到db。
解决:
布隆过滤器(bloom filter):相似于哈希表的一种算法,将全部可能存在的数据哈希到一个足够大的bitmap中,在进行数据库查询以前会使用这个bitmap进行过滤,若是一个必定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
空值缓存:一种比较简单的解决办法,在第一次查询完不存在的数据后,将该key与对应的空值也放入缓存中,只不过设定为较短的失效时间,最长不超过五分钟。,这样则能够应对短期的大量的该key攻击,设置为较短的失效时间是由于该值可能业务无关,存在乎义不大,且该次的查询也未必是攻击者发起,无太久存储的必要,故能够早点失效。



缓存雪崩
概念:缓存雪崩是指在咱们设置缓存时采用了相同的过时时间,致使缓存在某一时刻同时失效,请求所有转发到DB,DB瞬时压力太重雪崩。
解决:
线程互斥:只让一个线程构建缓存,其余线程等待构建缓存的线程执行完,从新从缓存获取数据才能够,每一个时刻只有一个线程在执行请求,减轻了db的压力,但缺点也很明显,下降了系统的qps。
交错失效时间:能够在原有的失效时间基础上增长一个随机值,好比1-5分钟随机,这样每个缓存的过时时间的重复率就会下降,就很难引起集体失效的事件。



缓存击穿
概念:对于一些设置了过时时间的key,若是这些key在某些时间点被超高并发地访问,是一种很是“热点”的数据。这个时候可能会发生缓存被“击穿”的问题,和缓存雪崩的区别在于:缓存击穿是针对某一/几个key缓存,缓存雪崩则是不少key。当缓存在某个时间点过时的时候,刚好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过时通常都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
好比:微博有一个热门话题的功能,用户对于热门话题的搜索量每每在一些时刻会大大的高于其余话题,这种咱们成为系统的“热点“,因为系统中对这些热点的数据缓存也存在失效时间,在热点的缓存到达失效时间时,此时可能依然会有大量的请求到达系统,没有了缓存层的保护,这些请求一样的会到达db从而可能引发故障。击穿与雪崩的区别即在于击穿是对于特定的热点数据来讲,而雪崩是所有数据。
解决:
二级缓存:对于热点数据进行二级缓存,并对于不一样级别的缓存设定不一样的失效时间,则请求不会直接击穿缓存层到达数据库。
互斥锁(mutex key): 只让一个线程构建缓存,其余线程等待构建缓存的线程执行完,从新从缓存获取数据便可。
LRU算法:根据数据的历史访问记录来进行淘汰数据,其核心思想是“若是数据最近被访问过,那么未来被访问的概率也更高”。最多见的实现是使用一个链表保存缓存数据,缓存步骤:
    首先将新数据放入链表的头部
    在进行数据插入的过程当中,若是检测到链表中有数据被再次访问也就是有请求再次访问这些数据,那么就其插入的链表的头部,由于它们相对其余数据来讲多是热点数据,具备保留时间更久的意义
    最后当链表数据放满时将底部的数据淘汰,也就是不常访问的数据
复制代码

 

 

90、实现WSGI协议的框架

复制代码
WSGI协议:必须同时实现 web server web application;WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server如何与web application通讯的规范。
WSGI协议实际上是定义了一种server与application解耦的规范,便可以有多个实现WSGI server的服务器,也能够有多个实现WSGI application的框架,能够选择任意的server和application组合实现本身的web应用。例如uWSGI和Gunicorn都是实现了WSGI server协议的服务器,Django,Flask是实现了WSGI application协议的web框架,能够根据项目实际状况搭配使用。

WSGI server:
负责从客户端接收http请求,将request转发给WSGI application,将application返回的response返回给客户端。
WSGI application:
接收由server转发的request,并处理请求,最后将response处理结果返回给server。application中能够包括多个栈式的中间件(middlewares),这些中间件须要同时实现server与application,能够在WSGI服务器与WSGI应用之间起调节做用:对服务器来讲,中间件扮演应用程序,对应用程序来讲,中间件扮演服务器。
WSGI application应该实现为一个可调用对象,例如函数、方法、类(包含`call`方法)。须要接收两个参数:一、一个字典,该字典能够包含了客户端请求的信息以及其余信息,能够认为是请求上下文,通常叫作environment(编码中多简写为environ、env) 二、一个用于发送HTTP响应状态(HTTP status )、响应头(HTTP headers)的回调函数 经过回调函数将响应状态和响应头返回给server,同时返回响应正文(response body),响应正文是可迭代的、并包含了多个字符串。

uwsgi:与WSGI同样是一种通讯协议,是uWSGI服务器的独占协议,用于定义传输信息的类型(type of information),每个uwsgi packet前4byte为传输信息类型的描述。
uWSGI是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等。主要特色:超快的性能,低内存占用,多app管理,详尽的日志功能(能够用来分析app的性能和瓶颈),高度可定制(内存大小限制,服务必定次数后重启等)。uWSGI服务器本身实现了基于uwsgi协议的server部分,咱们只须要在uwsgi的配置文件中指定application的地址,uWSGI就能直接和应用框架中的WSGI application通讯


生产环境使用的WSGI服务器: gunicorn:接受从Nginx转发的动态请求,处理完以后返回给Nginx,由Nginx返回给用户。 uwsgi:把HTTP协议转化成语言支持的网络协议。好比把HTTP协议转化成WSGI协议,让Python能够直接使用。 注:响应时间较短的应用中,使用uWSGI+django;若是有部分阻塞请求 Gunicorn+gevent+django有很是好的效率; 若是阻塞请求比较多的话,仍是用tornado重写吧。
复制代码

 

 

9一、简述Django的MVT模式

复制代码
### MVC: 浏览器 --> Controll

Model : 用于 `Controll(服务器)和数据库的交互`,对数据库中的数据进行增、删、改、查操做。

View : 用于`封装html,css,js`,生成页面展现的html内容(数据的呈现方式)。

Controll : 用于 `接收请求,业务处理,返回结果`  & `C和V交互、C和M交互、C和客户端交互`

C必不可缺



### MVT:浏览器 --> 路由器 --> View(MVC中的C) 

M : model  `View (服务器)和数据库的交互`

V : View 等同于 `MVC的Controll`,接收请求,业务处理,返回结果 & C和V交互 、C和M交互 、C和客户端交互

T : template  `封装html,css,js`,负责封装构造要返回的html(如何显示数据,产生html界面)。

V必不可缺
复制代码

 

 

9二、Redis高并发的解决方法

复制代码
  • Redis 主从 + 哨兵(sentinel)

  • Redis Sentinel 集群 + 内网 DNS + 自定义脚本

  • Redis Sentinel 集群 + VIP + 自定义脚本

  • 封装客户端直连 Redis Sentinel 端口

    • JedisSentinelPool,适合 Java

    • PHP 基于 phpredis 自行封装

  • Redis Sentinel 集群 + Keepalived/Haproxy

  • Redis M/S + Keepalived

  • Redis Cluster

  • Twemproxy

  • Codis

 

Django解决高并发方案:HTTP重定向实现负载均衡、DNS负载均衡、反向代理负载均衡

复制代码

 

9三、实现斐波那契数列

复制代码
# 一、生成器方式
def fibonacci(num):

    a = 0
    b = 1
    offset = 0

    while offset < num:
        result = a
        a, b = b, a+b
        offset += 1

        yield result


result = fibonacci(10)
for i in result:
    print(i)



# 二、迭代器方式

class Fibonacci(object):
    """
    斐波那契数列 : 第一个数为 0 , 第二个数为 1 ,其后的每个数都由前两数相加之和
    """

    def __init__(self, num):

        self.offset = 0  # 记录当前取值的位置
        self.a = 0
        self.b = 1
        self.num = num  # 指定最终输出数列的长度

    def __iter__(self):

        return self  # 返回自身,自身就是一个迭代器

    def __next__(self):

        if self.offset < self.num:
            result = self.a
            self.a, self.b = self.b, self.a + self.b
            self.offset += 1
            return result
        else:
            raise StopIteration  # 抛出异常,中止迭代


list1 = list(Fibonacci(10))  # 也可以使用 元组,列表..接收结果
print(list1)
复制代码

 

 

9四、输入某年某月某日,判断今日是当年的第几天?

复制代码
import datetime

y = int(input("请输入4位数字的年份:"))
m = int(input("请输入月份:"))
d = int(input("请输入是哪一天"))

targetDay = datetime.date(y,m,d)
dayCount = targetDay - datetime.date(targetDay.year -1,12,31)
print("%s是 %s年的第%s天。"%(targetDay,y,dayCount.days))
复制代码

 

 

9五、Django一次请求的生命周期?

复制代码

1.wsgi ,请求封装后交给web框架(Flask,Django)

2.中间件,对请求进行校验或在请求对象中添加其余相关数据,例如:csrf,request.session

3.路由匹配 根据浏览器发送的不一样url去匹配不一样的视图函数

4.视图函数,在视图函数中进行业务逻辑的处理,可能涉及到:orm,templates

5.中间件,对响应的数据进行处理

6.wsgi,将响应的内容发送给浏览器



猴子补丁 : 在运行期间动态修改一个类或模块(在函数或对象已经定义以后,再去改变它们的行为)
1. 在运行时替换方法、属性等。 2. 在不修改第三方代码的状况下增长原来不支持的功能。 3. 在行⾏时为内存中的对象增长patc而不是在磁盘的源代码中
复制代码

 

 

9六、并行并发、同步异步、阻塞非阻塞

复制代码

并行: 同一时刻多个任务同时在运行(实现并行:multiprocessing)  CPU运算量大的程序,使用并行会更好

并发:不会在同一时刻同时运行,存在交替执行的状况。(实现并发的: threading)  须要执行较多的读写、请求和回复任务的须要大量的IO操做,IO密集型操做使用并发更好。

 
 
同步: 多个任务之间有前后顺序执行,一个执行完下个才能执行。

异步: 多个任务之间没有前后顺序,能够同时执行,有时候一个任务可能要在必要的时候获取另外一个同时执行的任务的结果,这个就叫回调!

阻塞: 若是卡住了调用者,调用者不能继续往下执行,就是说调用者阻塞了。

非阻塞: 若是不会卡住,能够继续执行,就是说非阻塞的。

同步异步相对于多任务而言,阻塞非阻塞相对于代码执行而言。
复制代码

 

 

9七、简述Python的垃圾回收机制

复制代码
主要是引用计数、垃圾回收机制、内存池机制。
使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,经过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,还有经过“分代回收”(generation collection)以空间换时间的方法提升垃圾回收效率。最后使用内存池机制将不使用的内存放到内存当中,而不是直接返回给操做系统。 引用计数:Python在内存中存储了每一个对象的引用计数(reference count)。当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了,分配给该对象的内存就会释放出来。
      好比某个新建对象,它被分配给某个引用,对象的引用计数变为1,若是引用被删除,对象的引用计数为0,那么该对象就能够被垃圾回收。 标记-清除:"标记-清除算法"是为了解决循环引用(reference cycle)问题而提出。它使用了根集的概念,基于tracing算法的垃圾收集器从根集开始扫描,识别出哪些对象可达,哪些对象不可达,并用某种方式标记可达对象,最后垃圾回收器回收那些不可达对象。
      举个例子,假设有两个对象o1和o2,并且符合o1.x == o2和o2.x == o1这两个条件。若是o1和o2没有其余代码引用,那么它们就不该该继续存在。但它们的引用计数都是1。 分代回收:基本思想是,将内存区域分两块(或更多),其中一块表明年轻代,另外一块表明老的一代。针对不一样的特色,对年轻一代的垃圾收集更为频繁,对老代的收集则较少,每次通过年轻一代的垃圾回收总会有未被收集的活对象,这些活对象通过收集以后会增长成熟度,当成熟度到达必定程度,则将其放进老代内存块中。
      例如,越晚建立的对象更有可能被回收。对象被建立以后,垃圾回收器会分配它们所属的代(generation)。每一个对象都会被分配一个代,而被分配更年轻代的对象是优先被处理的。
内存池机制:Pymalloc机制,为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。 
      Python中全部小于256个字节的对象都使用pymalloc实现的分配器,而大于256字节的对象则使用系统的malloc分配内存。
      对于Python对象,如整数、浮点数、list,都有独立的私有内存池,对象间不共享他们的内存池。也就是说若是你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。

内存泄漏:指因为疏忽或错误形成程序未能释放已经再也不使用的内存。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,致使在释放该段内存以前就失去了对该段内存的控制,从而形成了内存的浪费。 不使用一个对象时使用: del object 来删除一个对象的引用计数就能够有效防止内存泄露问题。可经过Python扩展模块gc 来查看不能回收的对象的详细信息;或者经过 sys.getrefcount(obj) 来获取对象的引用计数,并根据返回值是否为0来判断是否内存泄露
复制代码

 

 

9八、三次握手

复制代码
1) Client首先发送一个链接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个链接请求或链接接受报文,同时表示这个数据报不能携带数据,seq = x 表示Client本身的初始序号(seq = 0 就表明这是第0号帧),这时候Client进入syn_sent状态,表示客户端等待服务器的回复
2) Server监听到链接请求报文后,如赞成创建链接,则向Client发送确认。TCP报文首部中的SYN 和 ACK都置1 ,ack = x + 1表示指望收到对方下一个报文段的第一个数据字节序号是x+1,同时代表x为止的全部数据都已正确收到(ack=1实际上是ack=0+1,也就是指望客户端的第1个帧),seq = y 表示Server 本身的初始序号(seq=0就表明这是服务器这边发出的第0号帧)。这时服务器进入syn_rcvd,表示服务器已经收到Client的链接请求,等待client的确认。
3) Client收到确认后还需再次发送确认,同时携带要发送给Server的数据。ACK 置1 表示确认号ack= y + 1 有效(表明指望收到服务器的第1个帧),Client本身的序号seq= x + 1(表示这就是个人第1个帧,相对于第0个帧来讲的),一旦收到Client的确认以后,这个TCP链接就进入Established状态,就能够发起http请求了。
复制代码

 

 

9九、Redis为何比MySQL快?

复制代码
一、Redis存储的是k-v格式的数据。查找和操做的时间复杂度都是O(1)常数阶,而mysql引擎的底层实现是B+TREE,时间复杂度是O(logn)是对数阶的。

二、数据结构简单,对数据操做也简单,Redis中的数据结构是专门进行设计的;

三、Redis是单线程的多路复用IO(非阻塞IO), 单线程避免了线程切换的开销,也不存在多进程或者多线程致使的切换而消耗 CPU,不用去考虑各类锁的问题,不存在加锁释放锁操做,没有由于可能出现死锁而致使的性能消耗;而多路而复用IO避免了IO等待的开销,在多核处理器下提升处理器的使用效率能够对数据进行分区,而后每一个处理器处理不一样的数据。

四、Mysql数据存储是存储在表中,查找数据时要先对表进行全局扫描或根据索引查找,这涉及到磁盘的查找,可是顺序查找就比较慢。而Redis彻底基于内存,会根据数据在内存的位置直接取出,很是快速。

五、使用底层模型不一样,它们之间底层实现方式以及与客户端之间通讯的应用协议不同,Redis直接本身构建了VM 机制 ,由于通常的系统调用系统函数的话,会浪费必定的时间去移动和请求;

补充其余数据库的模型:

  一、单进程多线程模型:MySQL、Memcached、Oracle(Windows版本);

  二、多进程模型:Oracle(Linux版本);

  三、Nginx有两类进程,一类称为Master进程(至关于管理进程),另外一类称为Worker进程(实际工做进程)。启动方式有两种:

    (1)单进程启动:此时系统中仅有一个进程,该进程既充当Master进程的角色,也充当Worker进程的角色。

    (2)多进程启动:此时系统有且仅有一个Master进程,至少有一个Worker进程工做。

    (3)Master进程主要进行一些全局性的初始化工做和管理Worker的工做;事件处理是在Worker中进行的。

复制代码

 

 

100、sql语句中where和having哪一个执行更快?

复制代码
sql语句的书写顺序和执行顺序时不同的,而是按照下面的顺序来执行:
from--where--group by--having--select--order by。
from:须要从哪一个数据表检索数据 
where:过滤表中数据的条件 
group by:如何将上面过滤出的数据分组 
having:对上面已经分组的数据进行过滤的条件  
select:查看结果集中的哪一个列,或列的计算结果 
order by :按照什么样的顺序来查看返回的数据 

经过执行顺序发现where实际上是比having先执行,也就是说where速度更快。

where和having的区别:
 “Where” 是一个约束声明,使用Where来约束来自数据库的数据,Where是在结果返回以前起做用的,且Where中不能使用聚合函数(例如Sum)。
 “Having”是一个过滤声明,是在查询返回结果集之后对查询结果进行的过滤操做,在Having中能够使用聚合函数。
复制代码

 

 

10一、图片管理为何使用FastDFS?为何不用云端?它的好处是什么?

复制代码
FastDFS是开源的轻量级分布式文件存储系统。它解决了大数据量存储和负载均衡等问题。特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务。

优点:
0. FastDFS比七牛云等云端存储更便宜!
1. 只能经过专用的API访问,不支持posix,下降了系统的复杂度,处理效率高
2. 支持在线扩容,加强系统的可扩展性
3. 支持软RAID,加强系统的并发处理能力及数据容错能力。Storage是按照分组来存储文件,同组内的服务器上存储相同的文件,不一样组存储不一样的文件。Storage-server之间不会互相通讯。
4. 主备Tracker,加强系统的可用性。
5. 支持主从文件,支持自定义扩展名
6.文件存储不分块,上传的文件和os文件系统中的文件一一对应。
7. 相同内容的文件只存储一份,节约磁盘存储空间。对于上传相同的文件会使用散列方式处理文件内容,假如是一致就不会存储后上传的文件,只是把原来上传的文件在Storage中存储的id和ULR返回给客户端。
复制代码

 

 

10二、为何使用celery不用线程?

主要是由于并发比较大的时候,线程切换会有开销时间,假如使用线程池会限制并发的数量;同时多线程间的数据共享维护比较麻烦。
而celery是异步任务处理,是分布式的任务队列。它可让任务的执行同主程序彻底脱离,甚至不在同一台主机内。它经过队列来调度任务,不用担忧并发量高时系统负载过大。它能够用来处理复杂系统性能问题,却又至关灵活易用。

 

10二、什么是线程安全?

线程安全是在多线程的环境下,可以保证多个线程同时执行时程序依旧运行正确, 并且要保证对于共享的数据能够由多个线程存取,可是同一时刻只能有一个线程进行存取。
多线程环境下解决资源竞争问题的办法是加锁来保证存取操做的惟一性。

 

10三、GIL锁对多线程的影响?

复制代码
GIL的全称是Global Interpreter Lock(全局解释器锁),这把锁只存在于CPython解释器当中,与Python语言自己毫无关系,主要为了数据安全所作的决定。它保证了每一个CPU在同一时间只能执行一个线程(在单核CPU下的多线程其实都只是并发,不是并行。并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。)

在Python多线程下,每一个线程的执行方式:

一、获取GIL

二、执行代码直到sleep或者是python虚拟机将其挂起。

三、释放GIL

因此某个线程想要执行,必须先拿到GIL,咱们能够把GIL看做是“通行证”,而且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不容许进入CPU执行。

GIL的释放逻辑是当前线程执⾏超时后会⾃动释放;在当前线程执⾏阻塞操做时会⾃动释放;当前执⾏完毕时会释放。而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。而且因为GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行)。

IO密集型代码(文件处理、网络爬虫等),多线程可以有效提高效率(单线程下有IO操做会进行IO等待,形成没必要要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,能够不浪费CPU的资源,从而能提高程序执行效率),因此多线程对IO密集型代码比较友好。
复制代码

 

 

 

  

10三、简述一次HTTP请求经历了什么?

复制代码
一、client(浏览器)与server 经过 http 协议通信,http 协议是基于 tcp 协议的一种应用层协议,因此client 与 server 主要经过socket 进行通信;
二、浏览器输入网址,本地的DNS服务器尝试解析域名以此获取域名对应的IP地址
三、若本地DNS服务器不能解析,将会把域名发送到远程的DNS服务器上进行解析
四、DNS服务器域名解析成功,返回IP地址给浏览器,浏览器向IP地址发送链接请求
五、浏览器和服务器通过三次握手创建TCP链接
六、server服务器这边 Nginx 首先拿到请求,进行一些验证,好比负载均衡、黑名单拦截之类的,而后 Nginx 直接处理静态资源请求,其余请求 Nginx 转发给后端服务器,如今先假设后端服务器使用的是uWSGI,Nginx 和uWSGI经过uwsgi协议进行通讯,uWSGI 拿到请求能够进行一些逻辑,验证黑名单、判断爬虫等。
  根据 wsgi 标准,WSGI application是一个能够被调用的对象,好比函数方法类(包含call)等,而且它须要接收两个参数,一个字典包含客户端请求的信息,也可认为是请求上下文(environment/environ/env),另外一个用于发送HTTP响应状态和响应头的回调函数,而后将拿到的 environs 参数传递给 Django。
  Django 根据 wsgi 标准接收请求和 env,Django 拿到请求后自上而下执行 middleware中间件内的相关逻辑,而后匹配全部路由到相应 view 执行逻辑,若是出错执行 exception middleware 相关逻辑,接着 response 前执行再次返回到middleware 中间件当中执行相关逻辑,可是这次顺序是自下而上执行的。
  最后经过 wsgi 标准构造 response,拿到须要返回的数据,设置一些 headers、cookies 之类的,最后将response返回,再经过 uWSGI 给 Nginx ,Nginx 返回给浏览器。浏览器-服务器经过http协议进行数据交互,http 协议是无状态协议(post、get、RESTFul设计、服务器 server 模型 epoll、select)
七、浏览器接收到数据以后经过浏览器本身的渲染功能来显示这个网页。

八、浏览器和服务器四次挥手断开链接,先断开链接的一方须要再次等待2msl报文最大生存的时间
复制代码

 

 

10四、Mysql 数据库存储的原理

复制代码
储存过程是一个可编程的函数,它在数据库中建立并保存。它能够有 SQL 语句和一些特殊的控制结构组成。当但愿在不一样的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是很是有用的。
 数据库中的存储过程能够看作是对编程中面向对象方法的模拟。它容许控制数据的访问方式。存储过程一般有如下优势:   一、存储过程能实现较快的执行速度   二、存储过程容许标准组件是编程。   三、存储过程能够用流程控制语句编写,有很强的灵活性,能够完成复杂的判断和较复杂的运算。   四、存储过程可被做为一种安全机制来充分利用。   五、存储过程可以减小网络流量
复制代码

 

 

10五、事务的特性

一、原子性(Atomicity):事务中的所有操做在数据库中是不可分割的,要么所有完成,要么均不执行。
二、一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致。
三、隔离性(Isolation):事务的执行不受其余事务的干扰,事务执行的中间结果对其余事务必须是透明的。
四、持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即便数据库出现故障

 

 

10六、数据库的索引是什么?

复制代码
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现一般使用 B_TREE。
B_TREE 索引加速了数据访问,由于存储引擎不会再去扫描整张表获得须要的数据;相反,它从根节点开始,根节点保存了子节点的指针,存储引擎会根据指针快速寻找数据。

MyISAM引擎使用B+Tree做为索引结构,叶节点的data域存放的是数据记录的地址,即:MyISAM索引文件和数据文件是分离的,MyISAM的索引文件仅仅保存数据记录的地址。
  MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,则取出其data域的值,而后以data域的值为地址,读取相应数据记录。MyISAM的索引方式也叫作“非汇集”的。

InnoDB引擎也使用B+Tree做为索引结构,可是InnoDB的数据文件自己就是索引文件,叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,所以InnoDB表数据文件自己就是主索引。这种索引叫作汇集索引
复制代码

 

 

10七、Python的参数传递是值传递仍是引用传递 ?

复制代码
Python的参数传递有:位置参数、默认参数、可变参数、关键字参数

函数的传值究竟是值传递仍是引用传递,要分状况:

# 不可变参数用值传递:

像整数和字符串这样的不可变对象,是经过拷贝进行传递的,由于你不管如何都不可能在原处改变不可变对象

# 可变参数是引用传递的:

好比像列表,字典这样的对象是经过引用传递、和C语言里面的用指针传递数组很类似,可变对象能在函数内部改变。
复制代码

 

 

10七、迭代器和生成器函数的区别?

复制代码
迭代器是一个更抽象的概念,任何对象,若是它的类有next方法和iter方法返回本身自己,对于string、list、dict、tuple等这类容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数,iter()是python的内置函数。
  iter()会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python的内置函数。在没有后续元素时,next()会抛出一个StopIteration异常。

生成器(Generator)是建立迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在须要返回数据的时候使用 yield 语句。每次 next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和全部的数据值)

区别:生成器能作到迭代器能作的全部事,并且由于自动建立了iter()和 next()方法,生成器显得特别简洁,并且生成器也是高效的,使用生成器表达式取代列表解析能够同时节省内存。除了建立和保存程序状态的自动方法,当发生器终结时,还会自动抛出 StopIteration 异常
复制代码

 

10八、谈谈你对面向对象的理解

复制代码
面向对象是相对于面向过程而言的。

面向过程语言是一种基于功能分析的、以算法为中心的程序设计方法;

而面向对象是一种基于结构分析的、以数据为中心的程序设计思想。

在面向对象语言中有一个有很重要东西,叫作类。

面向对象有三大特性:封装、继承、多态。
复制代码

 

 

10八、打印星星形状

复制代码
# 九九乘法表
for i in range(1, 10):
     for j in range(1, i+1):
             print('{}*{}={}'.format(j, i, j*i), end='\t')
     print()


# 菱形星星
def info():
    k = 1
    while k <= 9:
        if k <= 5:
            print(" " * (5 - k), "*" * (2 * k - 1))
        else:
            print(" " * (k - 5), "*" * ((10 - k) * 2 - 1))

        k += 1
复制代码

 

 

10九、线上服务宕机?

使用supervisor,对用户定义的进程进行启动,关闭,重启。针对意外关闭的进程能够进行重启 ,仅需简单配置便可,且有web端,状态、日志查看清晰明了。

 

1十、Redis宕机?

复制代码
主从模式下的宕机区分来看:
1. slave从redis宕机
   在Redis中从库从新启动后会自动加入到主从架构中,自动完成同步数据;
若是从数据库实现了持久化,只要从新假如到主从架构中会实现增量同步。
2. Master 宕机
   假如主从都没数据持久化,此时千万不要立马重启服务,不然可能会形成数据丢失,正确的操做以下:
  1. 在slave数据上执行SLAVEOF ON ONE,来断开主从关系并把slave升级为主库
  2. 此时从新启动主数据库,执行SLAVEOF,把它设置为从库,自动备份数据。
以上过程很容易配置错误,能够使用简单的方法:redis的哨兵(sentinel)的功能。

哨兵(sentinel)的原理:Redis提供了sentinel(哨兵)机制经过sentinel模式启动redis后,自动监控master/slave的运行状态,基本原理是:心跳机制+投票裁决 心跳机制:每一个sentinel会向其它sentinal、master、slave定时发送消息,以确认对方是否“活”着,若是发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的“主观认为宕机” Subjective Down,简称SDOWN)。
 
    投票裁决:若"哨兵群"中的多数sentinel,都报告某一master没响应,系统才认为该master"完全死亡"(即:客观上的真正down机,Objective Down,简称ODOWN),经过必定的vote算法,从剩下的slave节点中,选一台提高为master,而后自动修改相关配置。
复制代码

 

 

1十一、Redis缓存满了怎么办?

① 给缓存服务,选择合适的缓存逐出算法,好比最多见的LRU。

② 针对当前设置的容量,设置适当的警惕值,好比10G的缓存,当缓存数据达到8G的时候,就开始发出报警,提早排查问题或者扩容。

③ 给一些没有必要长期保存的key,尽可能设置过时时间。

 

 

1十二、数组、链表、堆栈、队列的区别

复制代码
数据结构:是指相互之间存在一种或多种特定关系的数据元素集合,简单理解:数据结构就是描述对象间逻辑关系的学科。好比队列是一种先进先出的逻辑结构,桟是一种先进后出的逻辑结构,家谱是一种树形的逻辑结构,

数据存储结构:是描述数据在计算机中存储方式的学科,经常使用的数据存储方式:顺序存储和非顺序存储。顺序存储是将数据存储在一块连续的存储介质中(好比硬盘或内存),数组就是采用的顺序存储;--举个例子:从内存中拿出第100个字节到1000个字节间的连续位置,存储数据;
        非顺序存储是指数据不必定存储在一块连续的位置上,只要每一个数据知道它前面的数据和后面的数据就能够把数据连续起来了,链表便是采用的非顺序存储。
队列、栈是线性数据结构的典型表明,而数组、链表是经常使用的两种数据存储结构;队列和栈都可以用数组或链表的存储方式实现它的功能!

数组和列表:数组初始化后大小固定,长度不可再变,且数据都已经被赋值,数组中存放的数据类型必须一致;list中能够存放不一样类型数据,list的长度是根据元素的多少而相应的发生改变;
      数组不能删除指定位置的元素,除非重建数组对象;list移除某一元素后,后续元素会前移;
数组和链表:数组是使用一块连续的内存空间保存数据,保存的数据的个数在分配内存的时候就是肯定的;链表是在非连续的内存单元中保存数据,而且经过指针将各个内存单元连接在一块儿,每一个节点的存储位置保存着它的前驱和后继结点,最后一个节点的指针指向 NULL,
      链表不须要提早分配固定大小存储空间,当须要存储数据的时候分配一块内存并将这块内存插入链表中。
数组和链表的区别:

    1.占用的内存空间:链表存放的内存空间能够是连续的,也能够是不连续的,数组则是连续的一段内存空间。通常状况下存放相同多的数据数组占用较小的内存,而链表还须要存放其前驱和后继的空间。

    2.长度的可变性:链表的长度是按实际须要能够伸缩的,而数组的长度是在定义时要给定的,若是存放的数据个数超过了数组的初始大小,则会出现溢出现象。

    3.对数据的访问:链表方便数据的移动而访问数据比较麻烦;数组访问数据很快捷而移动数据比较麻烦。

    链表和数组的差别决定了它们的不一样使用场景,若是须要不少对数据的访问,则适合使用数组;若是须要对数据进行不少移位操做,则适合使用链表。

 

 队列:队列实现了先入先出的语义 (FIFO) 。队列也能够使用数组和链表来实现;队列只容许在队尾添加数据,在队头删除数据。可是能够查看队头和队尾的数据。

堆栈:堆栈实现了一种后进先出的语义 (LIFO) 。能够使用数组或者是链表来实现它,对于堆栈中的数据的全部操做都是在栈的顶部完成的,只能够查看栈顶部的数据,并只可以向栈的顶部压入数据,也只能从栈的顶部弹出数据。
堆:堆能够理解它就是个一个可大可小,随意分配的内存操做单元;它的特色就是动态的分配内存,适合存放大的数据量!好比一个对象的全部信息,虽然它的引用指向栈中的某个引用变量;因此堆是用来存放建立出来的对象的。

  栈:栈具备数据结构中栈的特色,后进先出,全部存放在它里面的数据都是生命周期很明确,占有的空间肯定并且占用空间小。

复制代码

 

 

11三、使用linux命令查询最近三个小时打开过的文件

复制代码
# 查找当前目录下.phtml文件中,最近30分钟内修改过的文件。
>find . -name '*.phtml' -type f -mmin -30</code>

# 查找当前目录下.phtml文件中,最近30分钟内修改过的文件,的详细状况。
>find . -name '*.phtml' -type f -mmin -30 -ls

# 查找当前目录下,最近1天内修改过的常规文件。
>find . -type f -mtime -1

# 查找当前目录下,最近1天前(2天内)修改过的常规文件。
>find . -type f -mtime +1
复制代码

 

 

11三、如何实现支付宝支付?

一、在支付宝沙箱环境下进行,在本身电脑生成公钥(解密)和私钥(加密),将本身公钥设置在沙箱应用中,获取支付宝对应公钥,将支付宝公钥和本身电脑私钥置于项目中,用于django网站和支付宝之间的通讯安全

二、用户点击去付款(订单id),请求django对应视图,校验以后使用python工具包调用支付宝支付接口(订单id,总金额,订单标题),支付宝返回支付页面,django引导用户到支付页面,用户登陆并支付,因为本项目没有公网ip,
  支付宝没法返回给django支付结果,django本身调用支付查询接口获取支付结果,返回给客户支付结果

 

 

 

11四、支付宝收不到异步回调通知?

复制代码
一、支付宝的异步通知须要`使用POST的方式接收`; 

二、http的`header头为标准头`;
    例如:application/x-www-form-urlencoded;text/html;charset=utf-8。

三、检查notify_url`外网post访问状态`(不支持除200之外的状态) ;
    选择和服务器不一样域的一台电脑,在chrome浏览器右键「检查」->地址栏输入notify_url地址->查看Network中的Status是不是200。

四、选择和服务器不一样域的一台电脑,`ping服务器地址是否流畅`;
    长时间后查看是否会有不稳定的状况(偶尔一次断开)。这也有可能会出现正好有半夜或何时有一次没有收到的状况。 

五、若是您的地址是https,也就是有证书,需加一步`是不是证书问题`;
    只支持官方机构颁发的正版SSL证书,不支持自签名。
    证书校验地址参照:https://csr.chinassl.net/ssl-checker.html 服务器到根证书链路通畅便可。
    SSL证书校验命令: openssl s_client -connect ${host}:${port} 或参考 SSL验证

六、DNS解析校验:dig ${host} +short 或者 nslookup 回车输入域名再回车查看;

七、链接有效校验: time curl -vk https://${host};

注:主要检查服务器配置,服务器是否开启写入权限、防火墙是否开启、端口443或80是否有开启且不是假死状态也没有被占用、DNS解析是否可以解析支付宝IP等,检查程序运行到alipay_notify文件的notify_verify()函数中,在isSign是否是等于true。
复制代码

 

 

11五、支付宝回调通知延时怎么办?

复制代码
# 一个订单在17:30以前未完成付款则超时关闭,用户在17:29在支付宝完成了支付,可是在17:31才将支付结果回调给咱们,此时单子已被超时关闭了,可是用户也确实是在规定的时间内完成的支付:

1.设置支付订单的时间与支付宝交易单号的自动关闭时间一致;
2.支付宝有主动查询交易状态接口;
3.支付宝可经过接口主动关闭订单;
4.回调时检查订单状态,若订单已关闭则直接向支付宝发起退款请求,交易结束。
复制代码

 

 11五、项目中使用什么调试?

           

复制代码
# 一、在Eclipse+Pydev中调试Django

适用于测试环境。 可进行单步调试,查看变量值,当出现except时,能够用Python标准模块traceback的print_exc()函数查看函数调用链, 是最强大的调试利器。

# 二、使用Django的error page

适用于测试环境。 Django的error page功能很强大,能提供详细的traceback,包括局部变量的值,以及一个纯文本的异常信息。拥有同phpinfo() 同样的做用,能够展现当前应用的相关设置,包括请求中的 GET, POST and COOKIE 数据以及HTTP环境中的全部重要META fields。

# 三、django-debug-toolbar

不肯定是否用于生产环境。据说功能很是强大。

# 四、输出log到开发服务器终端中

适用于生产环境。 借助python的logging模块
复制代码

 

11六、堆内存和栈内存的区别?

复制代码
一、内存分配方面:

  栈(stack):由编译器(Compiler)自动分配释放,存放函数的参数值,局部变量的值等。其操做方式相似于数据结构中的栈,主要存放的是基本类型类型的数据 如int, float, bool, string 和对象句柄。

  堆(heap): 通常由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式相似于链表。可能用到的关键字以下:new、malloc、delete、free等等。

 

二、申请方式方面:

    堆:须要程序员本身申请,并指明大小。在c中malloc函数如 p1=(char *)malloc(10);在C++中用new运算符,可是注意p一、p2自己是在栈中的。由于他们仍是能够认为是局部变量。

    栈:由系统自动分配。例如,声明在函数中一个局部变量 x=2 ;系统会自动在栈中为x开辟空间。

 

三、系统响应方面:

    堆:操做系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,而后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码中的delete语句才能正确的释放本内存空间。另外因为找到的堆结点的大小不必定正好等于申请的大小,系统会自动的将多余的那部分从新放入空闲链表中。

    栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,不然将报异常提示栈溢出。

 

四、大小限制方面:

    堆:是向高地址扩展的数据结构,是不连续的内存区域。这是因为系统是用链表来存储的空闲内存地址的,天然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。因而可知,堆得到的空间比较灵活,也比较大。

    栈:在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是固定的(是一个编译时就肯定的常数),若是申请的空间超过栈的剩余空间时,将提示overflow。所以,能从栈得到的空间较小。

 

五、效率方面

    堆:是由new分配的内存,通常速度比较慢,并且容易产生内存碎片,不过用起来最方便,另外,在WINDOWS下,最好的方式是用 VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。可是速度快,也最灵活。

    栈:由系统自动分配,速度较快。但程序员是没法控制的。

 

六、存放内容方面

    堆:通常是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

    栈:在函数调用时第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址而后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈,而后是函数中的局部变量。 注意: 静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,而后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

 

七、存取效率方面:

    堆:char *s1 = "Hellow Word";是在编译时就肯定的;

    栈:char s1[] = "Hellow Word"; 是在运行时赋值的;用数组比用指针速度要快一些,由于指针在底层汇编中须要用edx寄存器中转一下,而数组在栈上直接读取。

复制代码

 

 

11七、Linux下批量删除空文件

复制代码
# Linux下批量删除空文件(大小等于0的文件)的方法

find . -name "*" -type f -size 0c | xargs -n 1 rm -f

# 删除指定大小的文件,只要修改对应的 -size 参数就行,例如:
# 删除1k大小的文件。(但注意 不要用 -size 1k,这个获得的是占用空间1k,不是文件大小1k的)。
find . -name "*" -type f -size 1024c | xargs -n 1 rm -f


若是只要删除文件夹或者名字链接等,能够相应的改 -type 参数
复制代码

 

  

11八、Linux下批量替换多个文件中的字符串

复制代码
# sed命令能够批量替换多个文件中的字符串。
sed -i "s/原字符串/新字符串/g" `grep 原字符串 -rl 所在目录`

# 具体格式以下:
sed -i "s/oldString/newString/g" `grep oldString -rl /path`

# 实例代码:
sed -i "s/大小多少/日月水火/g" `grep 大小多少 -rl /usr/aa`
sed -i "s/大小多少/日月水火/g" `grep 大小多少 -rl ./`
复制代码

 

 

11九、计算函数运行时间

复制代码
import datetime
import time

# 第一种方法
start_time = datetime.datetime.now()
end_time = datetime.datetime.now()
final_time = endtime - starttime.seconds
print(final_time)

# 第二种方法
start = time.time()    # 获取自纪元以来的当前时间(以秒为单位), 返回浮点类型
end = time.time()
final = end-start
print(final) 

# 第三种方法
start = time.clock()    # 返回程序开始或第一次被调用clock() 以来的CPU时间, 返回浮点类型, 得到的是CPU的执行时间。
end = time.clock()
final = end-start
print(final) 


注:程序执行时间=cpu时间 + io时间 + 休眠或者等待时间
复制代码

 

120、建立TCP协议的socket套接字

复制代码
# 1.建立套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2.绑定本地端口
server_socket.bind(("", 9090))

# 3.设置为监听模式 1>把主动套接字转为被动套接字  2>告诉操做系统建立一个等待链接队伍
server_socket.listen(128)

# 4.等待客户端的连接 accept会阻塞等待,直到有客户端连接
client_socket, client_address = server_socket.accept()  # 返回一个新的套接字和客户端的地址
print("一个新客户端已经连接。。。。")

# 5.接收来自客户端的数据
date = client_socket.recv(1024)
print("接收到的数据:", date.decode(encoding="utf-8"))

# 6.回送数据给客户端
client_socket.send("世界之巅".encode(encoding="utf-8"))

# 7.关闭服务客户端的套接字
client_socket.close()
复制代码

 

 

12一、简述epoll、poll、select三种模型

复制代码
select和epoll都是I/O多路复用的方式,可是select是经过不断轮询监听socket实现,epoll是当socket有变化时经过回掉的方式主动告知用户进程实现。

Select: select函数监视3类文件描述符,分别是writefds、readfds、和exceptfds。调用后select函数后会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except)或者超时函数返回(timeout指定等待时间)。select目前几乎在全部的平台上都支持,良好的跨平台性也是它的一个优势;可是select在单个进程可以监视的文件描述符的数量存在最大限制,在Linux上通常为1024(这个数目与系统内存有关,具体数目能够cat/proc/sys/fs/file-max 查看。而且select对于socket进行扫描时是线性扫描,即采用轮询的方法,效率较低。当套接字比较多的时候,每次select()都要经过遍从来完成调度,无论哪一个Socket是活跃的,都遍历一遍。这会浪费不少CPU时间。


Poll:本质上和select没有区别,他将用户传入的数组拷贝到内核空间,而后查询每一个fd对应的设备状态,若是设备就绪则加入到设备等待队列中并继续遍历,若是遍历完全部fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。由于poll是基于链表进行存储,因此没有最大链接数限制,可是poll和select同样,都是经过遍从来获取已经就绪的socket,而同时链接的大量客户端在同一时间内可能只有不多的处于就绪状态,所以随着监视的描述符数量的增加,效率也会随之降低。仅仅只是改善了select的最大链接数量限制的缺陷。


epoll:没有描述符限制,而是事先经过 epoll_ctl() 预先注册一个文件描述符,使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。一旦某个文件描述符就绪时,内核会采用相似callback的回调机制,迅速激活这个文件描述符,当进程调用 epoll_wait() 时便获得通知。(此处去掉了遍历文件描述符的过程,而是经过监听回调的机制,大大提升了效率)。epoll模型所监视的描述符数量再也不受到限制,没有最大并发链接的限制(1G的内存上能监听约10万个端口,具体数目能够 cat /proc/sys/fs/file-max察看);再也不采用轮询的方式,提高了效率,IO的效率不会随着监视fd的数量的增加而降低,只有活跃可用的FD才会调用callback函数;
复制代码

 

 

12二、Django的用户权限认证原理?

复制代码
Django实现的permission体系,在底层被抽象为authentication backends。而且经过TemplateProcessor和RequestContext在模版系统中能够方便的使用,在界面中经过权限来控制提供给某个用户的显示。
Django中,全部的authentication backends,能够经过配置settings中的一个变量AUTHENTICATION_BACKENDS来作到,这个变量的类型是元组(Tuple),默认Django的设置是:
AUTHENTICATION_BACKENDS=('django.contrib.auth.backends.ModelBackend    ’)
可是,Django并无实现对象级别的权限控制。比方说在论坛系统中,只有管理员和帖子的发布者才有对该帖子对象的修改权限,这就是对象级别而非模型级别的权限控制。
所以,若是须要本身实现对象级别的权限控制,能够很容易的开发或者引用第三方提供的Object level auth
复制代码

 

 

12三、设计一个高并发?

复制代码
一、部署至少2台以上的服务器构成集群,既防止某台服务器忽然宕机,也减轻单台服务器的压力。
二、页面进行动静分离,好比使用Nginx反向代理处理静态资源,并实现负载均衡。
三、对于查询频繁但改动不大的页面进行静态化处理。
四、在代理前添加web缓存,在数据库前增长缓存组件;好比能够使用Redis做为缓存,采用Redis主从+哨兵机制防止宕机,也能够启用Redis集群。
五、对应用服务所在的主机作集群,实现负载均衡。
六、对数据库进行读写分离,静态文件作共享存储。
七、对数据库按照业务不一样进行垂直拆分;分库分表:将一张大表进行水平拆分到不一样的数据库当中;对于数据文件使用分布式存储。
八、使用消息中间件集群,用做于请求的异步化处理,实现流量的削锋效果。好比对于数据库的大量写请求时能够使用消息中间件。
九、将后端代码中的阻塞、耗时任务使用异步框架进行处理,好比celery。
复制代码

 

 

12四、怎样解决数据库高并发的问题?

复制代码
1) 缓存式的 Web 应用程序架构:在 Web 层和 DB(数据库)层之间加一层 cache 层,主要目的:减小数据库读取负担,提升数据读取速度。cache 存取的媒介是内存,能够考虑采用分布式的 cache 层,这样更容易破除内存容量的限制,同时增长了灵活性。

2) 增长 Redis 缓存数据库
3) 增长数据库索引
4) 页面静态化:效率最高、消耗最小的就是纯静态化的 html 页面,因此咱们尽量使咱们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。用户能够直接获取页面,不用像 MVC结构走那么多流程,比较适用于页面信息大量被前台程序调用,可是更新频率很小的状况。
5) 使用存储过程:处理一次请求须要屡次访问数据库的操做,能够把操做整合到储存过程,这样只要一次数据库访问便可。

6) MySQL 主从读写分离:当数据库的写压力增长,cache 层(如 Memcached)只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负。使用主从复制技术(master-slave 模式)来达到读写分离,以提升读写性能和读库的可扩展性。
  读写分离就是只在主服务器上写,只在从服务器上读,基本原理是让主数据库处理事务性查询,而从数据库处理 select 查询,数据库复制被用于把事务性查询(增删改)致使的改变动新同步到集群中的从数据库。一、主从只负责各自的读和写,极大程度缓解 X 锁和 S 锁争用。二、slave 能够配置 MyISAM 引擎,提高查询性能以及节约系统开销。
  三、master 直接写是并发的,slave 经过主库发送来的 binlog 恢复数据是异步的。四、slave 能够单独设置一些参数来提高其读的性能。五、增长冗余,提升可用性。实现主从分离能够使用 MySQL 中间件如:Atlas。

7) 分表分库,在 cache 层的高速缓存,MySQL 的主从复制,读写分离的基础上,这时 MySQL 主库的写压力开始出现瓶颈,而数据量的持续猛增,因为 MyISAM 使用表锁,在高并发下会出现严重的锁问题,大量的高并发 MySQL 应用开始使用 InnoDB 引擎代替 MyISAM。
  采用 Master-Slave 复制模式的 MySQL 架构,只能对数据库的读进行扩展,而对数据的写操做仍是集中在 Master 上。这时须要对数据库的吞吐能力进一步地扩展,以知足高并发访问与海量数据存储的需求。对于访问极为频繁且数据量巨大的单表来讲,首先要作的是减小单表的记录条数,以便减小数据查询所需的时间提升数据库的吞吐,这就是所谓的分表【水平拆分】。
  在分表以前,首先须要选择适当的分表策略(尽可能避免分出来的多表关联查询),使得数据可以较为均衡地分布到多张表中,而且不影响正常的查询。分表可以解决单表数据量过大带来的查询效率降低的问题,可是却没法给数据库的并发处理能力带来质的提高。面对高并发的读写访问,当数据库 master 服务器没法承载写操做压力时,无论如何扩展 Slave 服务器都是没有意义的,对数据库进行拆分,从而提升数据库写入能力,即分库【垂直拆分】

8) 负载均衡集群,将大量的并发请求分担到多个处理节点。因为单个处理节点的故障不影响整个服务,负载均衡集群同时也实现了高可用性。
复制代码

 

 

12五、实现 Python2 & python3 兼容的方法:

复制代码
# python2导入同级目录下的模块无需 . python3导入同级目录下的模块须要加上 .

try:
  from queue import Queue as SameName
except:
  from Queue import Queue as SameName

 
 

from six.moves.queue import Queue

 
复制代码