一. Python
-
Python 的优点和缺点 优点:
- 易扩展:使用pip便可以轻松安装相关的库,而且方便调用。
- 语言融合性:python可以方便的调用其他的语言编写的代码,也方便被其他的语言调用,胶水性语言。
- 开发速度快:大量的,成熟的库使得python在写业务功能的时候拥有非常快的速度。
- 简单,容易学习:语法简单,非常适合编程新手学习。
- 可读性:python的语法简单,造成在阅读python 的代码的时候并不需要很多的经验,方便理解代码。
- 面向对象:python支持面向对象的编程范式,也支持面向过程的。
- 开源:python是免费提供的,不仅仅是免费的试用,而且可以方便的获取python的源码,而且可以根据自己的需求修改。
- 跨平台性:只要不使用涉及带平台底层的库,一次编写代码,便可以在各个平台运行。
- 解释性代码:代码运行过程不是一次性编译的时候转换成二进制的运行程序,而是在运行的时候一行行的解释代码成二进制命令,一行行的运行,所以调试的时候更方便一些。
缺点:
- 运行速度慢:解释型语言,代码运行的时候速度慢,性能难受。
- Web型:python是一个出色的服务端语言,但是在客户端的编写上基本上是见不到python的。
- 设计限制:动态类型,方便理解和阅读,但是同时也容易出现bug。
- 数据库访问层不够发达:相对于jdbc 和 odbc,python的数据库访问不够完美,所以大型企业很少试用。
- 简单:太过于简单了,所以新手学习了python之后在学习其他的语言会很吃力,而且会排斥其他的语言。
-
深拷贝和浅拷贝的区别
用指针的概念解释会比较好,深拷贝是将A的数据里面的值拷贝到B,浅拷贝就是将A的指针拷贝到B,一个拷贝的是值,一个拷贝的是引用。 -
列表和元组的区别
列表是可以变动的,元组是不可变动的,list和 tuple是可以相互转换的。不是很清楚元组存在的意义,实际上元组可以干的活,列表全部可以干,但是列表可以干的,元组不可以干。 -
Python 三元运算
a, b = 2, 3 min = a if a < b else b
-
Python 中的多线程和多进程
Python 的多线程可以通过自带的threading 库实现,但是python 的GIL(全局解释器锁)导致python的多线程实际上同时只会有一个线程运行的,python3对GIL的获取方法做了改动,采用计时器的方式来改变GIL使用权,cpu密集型的处理有所改善,但是还是不够好。
Io密集型的情景下是可以使用多线程的,cpu密集型的情况下不建议使用多线程,cpu密集性实际上也不建议使用python处理,python运行的效率很低,完全不适合cpu密集性的代码。多核多线程情况下使用python,效率会更低下,多个cpu核去获取GIL效果会更差,所以python一般使用多进程处理,每个进程只使用一个线程。Python 一般是用subprocess 模块处理多进程, 如果只使用linux的情况下可以使用os.fork()建立新的进程。
还有一种方法,是多种语言的配合使用,将cpu计算密集性的代码使用c,c++等效率高的语言去写多线程的计算模块,使用python去写业务性代码,并调用其他代码,两种代码结合使用会更舒服的。 -
Python的继承
Python 支持 多层继承,就是 son 的father 也可以有father,一层层的继承下来,也支持多多重继承,就是一个son可以有多个father。
father的方法和属性son是都可以调用的,son类内部使用super调用,son类的对象调用的话就像自己方法一样调用。多重继承的情况下,调用一次super.a(),可以同时调用每个father的方法a方法,只要该father 有,也可以直接使用father的类名来调用方法。重写就是在son里面重新定义father的方法和属性就可以了。
Python不支持方法重载,可以使用默认传参,或者*args 或者 **kwargs 来实现类似重载的方法,或者是@ classmethod来实现的。 -
Python的内存管理
Python 使用一个私有的堆空间来保存所有的对象和数据结构,开发者是无法直接访问,是由解释器来管理它。 每一个对象被创建的时候,便会对该对象开始计数,计数会因为该对象的每一次被函数调用,被加入到容器中,或者赋值给其他的对象而增加,相应的从每一次函数结束,容器中移除,调用对象的销毁,或者重新赋值而减少,等到该计数为0的时候,就是该对象应该被销毁的时候。
循环调用的对象,a调用b,b调用a,会使用循环垃圾回收器来处理,当没有任何其他的方式引用 a或者b的时候,a和b同时销毁。
Python将多有的对象分级,分成0,1,2三代,所有新创建的对象会放入到0代中,当某代对象在经历一次垃圾回收之后还存活的话,就会放入到下一代,在经历一定次数0代的垃圾回收,才会启动0代,1代的扫描清理, 同样,在经历一定次数的1代的垃圾回收之后才会启动0,1,2代的扫描清理。
Python是有内存池的,内存池是针对小对象的(大小小于256bits),被销毁的对象的空间并不是返回给操作系统的,而是放入到相应的内存池里面的,整数,浮点数,字符串,list等都是由独立的内存池,这些内存池相互之间是不共享的,整数内存池里面的内存是不能用来申请浮点数的。
小数据(1-256)使用的内存在申请之后是不会销毁的,其他对象再次使用该数据的时候,直接引用之前申请的内存。
常用字符组成的字符串在没有销毁之前,再次申请的情况下是不会重新申请内存的,而是共用内存。
对象大小大于256个bit的时候是直接从操作系统的内存空间申请的空间。 -
Python里面的help() 和 dir() 方法
这两个方法都是python的内置方法,也都是用来了解函数或者模块的。
Help() 方法显示的是函数的用途和详细说面,一般显示的是固定的方法注释的内容,成熟的模块一般都会在这些注释中写明调用方法和使用场景,还有调用的效果。
dir() 方法在不带参数的时候显示的是当前范围的变量,方法和定义,带参数的时候显示的是参数的属性和方法列表。 -
Python在退出的时候,是不是释放了全部内存
并不会,循环应用的对象,还有引用自全局命名空间的对象并不会被完全释放。
调用的其他语言写的代码的内存,并不会被python解释器控制,有其他语言自己处理。 -
Python的传参 *args 和 **kwargs 的含义和区别
*args 和 **kwargs 都是在不知道想该方法传递固定参数的情况下的使用的。
*args 是传递的是list 或者 puple。
**kwargs 传递的是一个dict,也可以在传参的时候多个k=v的方式传参。 -
Python 负索引和数组的切片
负索引和正索引是一样的,都是用来表示list的index的,不过正索引是从左边计数的,负索引是从右边计数的。
数组的切片是[:]的方式实现的,冒号两边就是list的开始和结束索引。
数组的切片也可以是[::]的方式,前两个参数和两个的是一样,也是list的开始和结束的索引,第三个参数的符号表示 正序还是倒序,正整数就是正序, 负整数就是倒序, 第三个参数的绝对值就是隔几个获取一个值, 比如[::2] 就是获取index为0, 2, 4, 6, 8 …… 的值,[::3] 获取的就是 0, 3, 6, 9 ……。 -
Range 在python2 和 python3 的区别
Python2 的range 返回的是一个list,在python3 返回的是一个range的对象,该对象是可迭代的,不是list对象,但是该对象是可以转换成list的。 -
Python的闭包特性
内嵌函数,在一个函数的内部再定义一个新的函数,这个新的函数只能在前面一个函数的内部调用。
闭包就是要求就是,首先要有一对内嵌函数,内嵌函数必须调用外部函数的参数或者变量,而且外部函数必须返回改内嵌函数,而不是参数,或者变量,或者什么计算结果。
闭包本质是一个函数,只要该闭包没有被销毁,那么该闭包里面包含的部分变量可以一直保存下去。闭包避免了使用全局变量,将函数和他操作的部分数据关联起来,就类似于面向对象的类一样,一般来说一个类只有一个方法的时候,就可以使用闭包。
常见的闭包,装饰器,单例模式,非面向对象语言的实现的面向对象的方式。 -
Python 中不常见的运算符 //, % 和 **
% 就是取余,和其他的语言一样的。
// 是向下取整, 就是 (a-a%b)/b。
** 就是取幂, a**b 就是 a。 -
为什么不建议一下划线作为标识符的开头
Python是没有私有变量的概念的,所以约定俗成的将下划线开头的变量当成私有变量,所以最好不使用使用下划线开头标识符表示有变量的。 -
迭代器和生成器的区别
迭代器协议,对象需要提供next()方法,该方法返回迭代中的下一代,要么反返回一个StopIteration 的异常,来终止迭代。实现内置函数__iter__() 和 next() 方法就可以实现对象的迭代器。
可迭代的对象,list, tuple, dict, str 等。
生成器是用来创建类似迭代器效果的一个非常简单的工具,只需要在返回数据的时候使用yield语句,便可以创建一个类似于迭代器的效果,每次调用使用生成器的函数,该函数都会在上一次中断的地方继续运行,并返回结果,同时将运行参数和环境保存下来,提供给下一次使用。
迭代器一般是针对一个类似于可迭代对象的容器。
生成器一般是针对类似于一个循环的过程中,每一次循环都需要返回一个结果的。 -
装饰器的作用和常用场景
装饰器实际上是一个闭包,他的作用就是在不修改函数代码的情况下添加额外的功能。
装饰器的常用场景,函数的运行时间计算,函数的运行次数(web api接口的计数器 ),flask的路由传参,函数的运行的日志,事务处理(多个函数处理过程必须同时运行成功,或者同时运行失败),缓存处理,权限的校验等功能。 -
猴子补丁是什么
猴子补丁是指运行的时候给某一个对象添加或者替换属性。
猴子补丁可以方便代码的功能的编写,方便测试,但是也容易搞乱源代码的关系,不容易理解,也不够优雅。不建议使用。 -
@property,@classmethod, @staticmethod
三个都是用来标记特殊方法的装饰器。
@property 是表示将方法当成属性使用,可以用调用 对象.方法名 的方式运行该类对象.方法名()。
@staticmethod 是指 全局静态方法,直接使用 类名.方法名() 调用。
@classmethod 是指 类方法,类方法必须将类对象当成第一个传参,就类似普通类方法的self一样,可以只用类名.方法名()调用,也可以使用 对象.方法名()调用,实际上这个方法一般是用来类对象申明方法的重载。 -
Python的内置方法 call() 的作用
在类中声明__call__() 方法会让这个类申明的对象可以当场函数去使用,当这个对象当成函数使用的时候,实际上调用的就是__call__() 方法。 -
Python的可变对象和不可变对象
int, string, float, tuple 都是不可变对象,在这些类型的变量发生变动的时候,是原内存的内容不变,重新申请内存保存新的值,然后这些对象重新指向新的内存,可以理解城这中就是,值不变,该变量的指针发生了变动。
list,dict 是可变对象,是修改时,指针不变,指向的内存的值发生变动。 -
Python is 和 == 的区别
is 判断的是两个对象的id是不是一样的,实际上就是指针是不是一样的。
== 判断的是两个对象的值是不是一样的。 -
类变量和实例变量
类变量是所有实例之间共享的值,他们是不会单独分配给每个实例。
实例变量是每个实例单独有的变量。class Test(object): num_of_instance = 0 def __init__(self, name): self.name = name Test.num_of_instance += 1
这个例子中 num_of_instance 就是类变量,name就是实例变量。
-
Python的自省
实际上就是反射,就是可以通过一些方法获取到对象的属性和方法,对象的类型,像 type(), dir(), getattr(), hasattr(), setattr(), isinstance()。 -
鸭子类型
“鸭子类型”的语言是这么推断的:一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那它就可以被当做鸭子。也就是说,它不关注对象的类型,而是关注对象具有的行为(方法)。
“鸭子类型”没有任何静态检查,如类型检查、属性检查、方法签名检查等。
“鸭子类型” 适用动态语言 和 某些静态语言。
比如说A对象有song() 方法,B对象也有song() 方法,那么在调用song() 的方法的时候,可以直接将B当成A使用,而不会管B到底是不是A。 -
单例模式
Python的单例有多种实现方式:- new 方法实现
class Singleton(object): def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): orig = super(Singleton, cls) cls._instance = orig.__new__(cls, *args, **kw) return cls._instance class MyClass(Singleton): a = 1
- 类变量的方法实现,类变量是所有实例共享的
class Borg(object): _state = {} def __new__(cls, *args, **kw): ob = super(Borg, cls).__new__(cls, *args, **kw) ob.__dict__ = cls._state return ob class MyClass2(Borg): a = 1
- 装饰器实现
def singleton(cls): instances = {} def getinstance(*args, **kw): if cls not in instances: instances[cls] = cls(*args, **kw) return instances[cls] return getinstance @singleton class MyClass: ...
- 多文件,import方法实现。
# mysingleton.py 文件 class My_Singleton(object): def foo(self): pass my_singleton = My_Singleton() # to use 文件 from mysingleton import my_singleton my_singleton.foo()
-
协程
协程实际上就是执行一段代码的时候,中断该程序运行,转而运行其他的子程序,等适当的时候在返回回来继续执行,python的协程是通过生成器的方式来实现的。由于协程实际上还是单线程,共享数据不需要控制锁,所以性能优势很明显。 传统的生产者-消费者是一个线程写,一个线程取,通过队列来传递和控制。而改用协程之后,生产者产生消息后,可以直接yield跳转到消费者使用消息,消费者执行完毕之后,在切换都生产者生产,这样的效率有极大的提升,而且实际上还是单线程运行的。 -
read(), readline(), readlines(), xreadlines()
read() 是一次性读取所有的文件内容,返回一个字符串。
readline() 是读取一行的内容,返回一个字符串,多次调用该方法以遍历整个大文件。
readlines() 也是读取文件的所有内容,但是以行为单位,放置到一个列表中,返回一个列表,一次性读取,不适合处理大文件。
xreadlines() 返回的是一个生成器,不是一次性处理,是在每次运行的时候才,和readline很像,但是不需要每次读取下一行的时候都调用一遍, 可以自己实现循环 。 -
python2 的range, xrange, 和 python3的range
python2的range 返回的是一个数组。
python2的xrange 返回的是一个生成器。
python3的range实际上就是python2的xrange,返回的是一个生成器。 -
字典式推导、列表式推导
列表式推导,就是使用[ for in if ]的方式处理生成列表,直接在在[]里面实现循环,并且判断。使用[] 得到的是一个列表,使用() 得到的是一个生成器。
字典是推导,就是使用{k:v for in if}的方式处理成字典的。 -
新式类和旧式类
Python3 取消了经典类,默认都是新式类
Python2 默认的是经典类,只有使用Class Person(object):pass 的方法声明的类才是新式类。
新式类和旧式类的区别最大的实际上是多继承的搜索顺序的改变。
新式类采用的是并非是广度优先,而是C3算法。在同根节点的同级节点时候使用从左到右搜索,不同根节点的同级节点的时候,会在一个和紫薯搜索完了之后搜索到父级节点。
旧式类采用的是深度优先搜索,从继承树的低到上的搜索方式。 -
作用域
实际上就是一个变量声明之后可以被访问的范围。
Python的作用域是静态的,声明的位置决定该变量可以被访问的范围。
值得注意的是在if-elif-else、for-else、while、try-except\try-finally等关键字的语句块中并不会产成作用域。也就是在改代码块结束之后,可以访问到改代码块中声明的变量的。
不同的作用域也决定代码在运行的时候使用的是什么变量。
LEGB是代码运行过程中用来判断name是在那个作用域里面找到的,LEGB的顺序是 Local, enclosing, globals, __buitins__。- locals 是函数内的名字空间,包括局部变量和形参。
- enclosing 外部嵌套函数的名字空间(闭包中常见)。
- globals 全局变量,函数定义所在模块的名字空间。
- __buitins__ 内置模块的名字空间。
二. Linux
- 常用命令 cd ls ll mv ps top fg vim rm wc kill which whereis cat mkdir touch tail less ln chmod chown
三. Mysql
- 建表六大约束
- 主键约束
- 外键约束
- 唯一约束
- 非空约束
- 默认约束
- 自增约束
-
tinyint,smallint,mediumint,int,bigint区别
都是整数类型,但是存储的字节数不同,保存整数的大小范围也不一样 tinyint 保存占1个字节,有符号范围是 -2^7 – 2^7-1,无符号范围是 0 0 - 2^8 -1 smallint 保存占2个字节,有符号范围是 -2^15 – 2^15-1,无符号范围是 0 - 2^16 -1 mediumint 保存占3个字节,有符号范围是 -2^23 – 2^23-1,无符号范围是 0 - 2^24 -1 int 保存占4个字节,有符号范围是 -2^31 – 2^31-1,无符号是 0 - 2^32 -1 bigint 保存占8个字节,有符号范围是 -2^63 – 2^63-1,无符号范围是 0 - 2^64 -1 -
存储引擎类型
MyISAM, mysql5.1 之前的默认存储引擎。 优势是:读取是速度快,占用资源少。 劣势是:不支持事务;不支持行级锁;只有表级锁,更新数据的时候锁定整个表,极大地降低了并发性能,而且读写会阻塞;只能缓存索引,不能缓存数据;不支持外键约束,外键查询只能全文索引。 适合场景,单一业务场景,不存在太多的表关联,读也频繁但是并发量低。而且读的量比写的量高的。 InnoDB:mysql5.1 之后的默认存储引擎。 Innodb 是一个事务型存储引擎,有行级锁和外键约束。会在内存中建立缓冲池,用来缓存数据和索引,但是不会保存表的行数。读取数据默认不会锁表,有主键的自增约束 适合出现在业务复杂的场景,大量的表与表关联的场景,大量的并发的数据更新场景。而且非常适合可靠性要求很高的场景。 Innodb由于事务的原因,拥有了提交,回滚,数据库崩溃回复等能力,重做日志,可以通过日志方便的恢复数据库 Memery,堆内存,实际上就是用内存创建表,使用默认的hash索引,由于数据保存在内存中,读写速度都非常快,保存的时候保存在一个磁盘文件里面,关闭服务器之前如果没有保存数据的话,数据将会丢失。 适合做统计的中间表,可以高效的从内存中分析得到最终结果,可以保存的数据比较小,而且具有临时性。 Merge 实际上是多个相同的MyISAM表的集合,同一个表大量的数据的时候,是可以将一个表分成多个MyISAM表,然后再聚合成一个Merge表做数据查询。 主要就是innodb和MyISAM的区别。 Innodb支持事务,而MyISAM不支持。所以MyISAM在增删改查的时候速度较快,Innodb在不涉及事物的时候,性能较快,设计的时候性能较低,但是事务支持回滚等功能。 Innodb支持行级锁,MyISAM只支持表级锁。所以Innodb在并发的时候效率要远高于MyISAM。 行数保存,innodb不支持行数保存,查询的时候需要全表扫描,而MyISAM不需要 索引存储。Innodb 索引适合数据捆绑保存的,而MyISAM索引和数据是分开的。MyISAM的缓存的索引要多很多。 两个表的数据备份,innodb 不支持 load table from master的方式复制表,导致数据备份和转移比较麻烦,数据量大的时候更是痛苦。 -
事务
事务拥有四个特性:
- 原子性:事务作为一个整体被执行,一个事务里面的操作要么全部被执行,要么全部不执行。
- 一致性:事务应该确保数据库的状态,从一个一致的状态转换成另一个一致状态,一直状态就是数据库中的数据应蛮族完整性约束。
- 隔离性:多个事务并发的时候,每个事务的执行是不会相互影响的
- 持久性:已提交的事务对数据的修改是会永久保存在数据库中的
- 数据库的隔离状态
当多个线程都开启事务操作数据库的时候,数据库系统需要做到隔离操作,以保证各个线程获取的数据的准确性。不考虑数据库隔离性的情况下,会发生以下几种状态:
- 脏读:A事务处理过程中读取了B事务多次修改而未提交的中间过程的脏数据,
- 不可重复读:A事务在多次读取某一数据的时候,B事务修改了该数据并提交,导致A多次读取的数据结果不一致
- 幻读:A事务将某一数据从1修改成2,B又从2修改到1,C事务循环读取的时候,读取到了一次2,如同幻觉一样 Mysql数据库提供四种隔离级别 Serializable 所有事务串行运行,不可并发 Repeatable read 一旦有事务开始读取某数据,其他事物就不可修改该数据(mysql默认) Read commited 不可读取未提交的数据,可避免脏读 Read uncommitted 最低级别,什么都无法保证 隔离级别越高,数据安全性越高,但是性能越低
-
索引
全文索引 只有MyISAM 引擎支持,只能在char,varchar,text中使用,主要是用来解决like之类的模糊查询效率低的问题的 Hash索引 就是通常意义的key-value的hash,hash索引,hash索引在定位==的时候效果极好,但是对于范围查询,>, < 等运算是无法处理的,而且hash无法避免排序操作,在使用组合索引的时候无法使用前面的一个或几个字段的来查询。Hash无法避免全表扫描,hash在hash值相等的情况下,效率极其低下 Btree索引 将索引值按照一定的算法,存入到一个平衡查找的树形的数据结构里面,每次查询都是从根节点到叶子节点。 所有的叶子节点都是在树形结构的最后一层。 B+tree索引,Btree的改编版本,B+tree的key只保存在也叶子节点,而且每个叶子节点都有一个有序链表的指针。 Rtree索引 Rtree的优势是在范围查找上面。 普通索引 …… 唯一索引 和普通索引的区别就是索引值不允许出现重复,但是允许出现空值 主键索引 专用与主键的索引,唯一索引,但是不允许出现空值,一般在建表的时候就会创建的 组合索引 多个字段组合的索引,使用组合索引遵循最左前缀集合。 全文索引 -
Expain
关键字段,id,type,key,rows,extra id 显示是每个子语句的执行顺序,id值越高优先级越高,越早被执行 type 显示的是执行语句的访问类型,该字段表示语句运行之后,是否是直接匹配到,还是直接索引一次,还是扫描索引,还是没用使用索引,还是便利全表。 key 显示的是使用的索引,如果没有使用索引,显示的就是空 rows 根据统计信息和索引使用情况,显示找到数据索要的读取的行数 extra 显示的是执行的时候显示的额外信息,就类似于DEBUG信息一样 -
海量数据存储优化
sql 和 索引优化,根据索引和explain结果优化查询语句,尽可能不会出现慢查询。 添加缓存,memcached,redis等方法添加内存缓存 主从复制,读写分离,可以自己在业务层添加处理 使用mysql自带的分区表,根据分区原理优化sql语句,避免全面扫描所有分区 垂直拆分,根据系统的耦合性,将大的业务系统分割成多个子系统。每个子系统根据自己的数据特性优化自己的引擎,索引,sql语句之类的。 水平拆分,针对数据量大的表,选择合理的sharing key,将一个表分成多个小表,sql语句也要尽可能优化,避免扫描全部的表 -
连表查询
在真正做业务的时候不建议试用连表查询,这样有可能会出现慢查询,从而导致数据库卡住了。但还是需要了解一下的。
嵌套查询:试用多个sql语句,将一个sql的查询结果作为外层sql的查询条件查询 select * from A where A.id in (select id from B where B.name = “李三”)
内联查询:一次查询多个表,使用多个表格的相同的字段作为连接手段。
比如: 隐式内联: select A.* from A, B where A.id = B.id and B.name = “李三”;
Inner join(显示内联): select * from table1 inner join table2 on table1.id = table2.id where table2.name = “李三”;
Left join(外联,左): select * from table1 left join table2 on table1.id = table2.id,返回table1 的符合条件的行数全部显示出来,没有和table2 关联的行,数据显示为空,有关联的行,显示table2的数据
right join(外联,右):select * from table1 right join table2 on table1.id = table2.id,返回table2 的 符合条件的行数全部显示出来,没有和table1 关联的行,数据显示为空,有关联的行,显示table1的数据。
full outer join(外联: 全): select * from table1 full outer join table2 on table1.id = table2.id 返回table1 和table2 的 所有行,一个表在另一个表里面没有匹配的行的时候显示空。
四. Nginx
-
nginx 负载均衡是怎么实现的
轮询:根据请求时间顺序分配不同的后端服务器 加权轮询:更具服务器性能,配置每个服务器的权重,根据权重分配每个服务器的访问量 least_conn 最少连接,新来的访问,分配给当前连接量最小的服务器 ip hash:根据ip的hash结果分配访问后端服务器,每个访客访问的后端服务器都是固定的 响应时间分配(第三方):响应时间越短的服务器,分配的访问量越多 url hash(第三方):更具url的hash的结果分配访问服务器 -
nginx优势
高扩展,跨平台,高可靠,低内存消耗 单线程非阻塞事件处理:并发处理的数量高 内置的服务器检查机制,down的服务器,后期的请求就不会在发送到down的服务器上 内存消耗小 nginx可以动静分离,静态文件处理快,耗费内存少 代理,不管是正向的代理还是反向的代理, Master/worker进程,master专用管理,worker是实际工作线程 负载均衡 -
nginx 怎么配置负载均衡
在配置文件里面使用upstrem 配置 -
nginx 参数调优
work_process:进程数量,不超过cpu的总核数 work_rlimit_nofile: nginx最大打开文件数量的限制,但是也受系统可以打开的最大打开数量限制 work_connections: 设置work进程可以同时打开的最大连接数,但是这个参数也受系统socket连接数限制 use: linux 应该是 epoll,允许单个线程处理多个客户端请求 keepalive_timeout:配置keep alive 的 超时时间 keepalive_requests:配置keep alive 的可以发送的请求数量 reset_timeout_connection: 服务器发送玩应答之后关闭连接 client_body_timeout 客户端请求超时时间,默认60s -
uwsgi工作原理
wsgi 是python语言定义的,web服务器和web应用程序之间沟通的简单通用的接口,flask 和 django都自带wsgi的服务器,但是实际性能不好,所以生产环境一般都是nginx+uWSGI+相应的框架配合使用。 uWSGI 是实现http, uwsgi, http等协议的web服务器
五. Docker
- Docker 镜像怎么精简
首先精简原始镜像,尽可能选择体积小的,而且功能齐全的原始镜像; 在就是修改dockerfile文件,将镜像里面的Run apt yum等命令合并成一行,清理和我们系统无用的组件,删除安装过程形成的中间文件,比如说pyc文件,安装包文件。 使用upload 导出成压缩包文件。
六. Redis
-
Redis 简单原理
Redis是一个基于内存的key-value数据库。 redis会周期性的将内存的数据写入到磁盘里面。 Redis速度极快,因为Redis完全基于内存,而且所有的数据结构都是专门设计的,单线程串行,避免上下文切换和多进程多线程切换所造成的消耗,不需要考虑资源锁,多路I/O复用模型,就是使用单线程处理多个连接请求,减少i/o 处理的消耗,实际上就是某路等待i/o的时候同时处理其他路的连接。官方给出的数据是每秒10W+。 -
Redis数据结构
- String, 普通c语言字符串实际上就是一个char的数组,而redis的string保存的有char数组的长度,还有char数组空闲的长度。 这样可以避免在获取长度的时候对char数组的遍历。 还在每次字符串处理的时候检测字符串长度,避免溢出。 重新分配内存的时候,并不是分配的刚刚好,而是会多分配一定的空间,可能在下次少量添加字符串的时候,不用分配空间,以减少分配空间的次数。 缩小字符串的时候,不会主动释放空间,避免下次扩充的时候需要再次分配空间,知道调用相应的api来释放剩余空间 有len属性,可以避免字符串中间出现的空字符串照成的字符串中断
- List: 没有环的双向列表,而且节点保存的value不是固定类型的,保存有list的len,而且保存有tail和head节点
- Hash:和普通的hash不同的是,当hash值相同的时候,value并保存的是一个单项连表结构,而不是普通hash的value,这样解决了普通hash的键值冲突的问题。在对hash处理的时候,hash列表的长度会发生变动,redis的处理是逐步重新散列的方式处理。Hash是有两个table的,开始重新散列之后,每次操作hash的时候,将一条数据从主表移除,hash保存到附表里面去,直到主表的为空,再将主表释放,将附表定义成主表,再重新申请空间形成附表。
- Set:实际上就是就是一个value为空的hash,通过hash的方式去重
- Sorted Set,有序集合,实际上就是更具优先级排序的集合。实际上保存的数据结构是一个基于插入排序跳跃表。跳跃表实际上是一个多级链表,每级链表都是一个有序链表,最底层链表保存所有节点。上层节点集合是下层节点集合的子集,每个节点都指向同层的下一个节点,也指向下层的同一个节点。 redis 所有的对象分配内存都是通过redis自己实现的内存分配方式实现的,和普通内存分配方法的区别就是会在内存快的前面分配一块内存,来保存该对象的长度。
七. 系统
-
线程和进程
进程是资源分配的基本单位,比如是内存,磁盘空间,i/o等的资源分配。 线程是进程中执行运算的最小单位,也是cpu调度的基本单位。 一个线程只能属于一个进程,一个进程可以有多个线程,但是必须要有一个线程。同一个进程里面的多个线程是共享该进程的所有资源的,比如内存堆存储,代码,常量,全局变量和静态变量,但每个线程都有自己的内存栈存储,保存的是局部变量和临时变量。 进程间通讯必须要建立相应的通讯机制,线程间的通讯只需要通过读取和写入进程变量来控制。 -
进程间通讯
- 管道:一种半双工的通讯方式,数据只能单项流动,只能父子进程通讯,一种很古老的通讯方式。
- 命名管道,也是一种半双工的通讯方式,但是允许没有关系的进程之间通讯
- 信号量:实际上就是一个带锁的计数器,通过锁保证同时只有一个进程访问。主要是用来作为同步手段
- 消息队列:实际上是消息的链表,避免前面集中了传递信息量少,数据格式限制的缺点
- 信号: 实际上是一个通知机制,该机制比较复杂,信号种类多,一个进程修改信号,其他进程根据信号变动做出相应的反应
- 共享内存:在内存中开辟一块空间,一个进程开辟,但可以被多个进程访问,一般都是和信号量配合使用,共享内存运行效率是最快的
- Socke:socket是唯一的一个可以在不同机器的进程的通讯,具体在网络模块说明
八. 设计模式
- 单例
一个类只能实例化一个对象
懒汉方式:不会主动实例化对象,只有在第一次调用的时候实例化,最好是在实例化的时候加锁,以避免并发的时候实例化多个对象
饿汉方式:在程序刚开始运行的时候实例化。 - 工厂
- 消费者
九. 算法和数据算法
-
怎么判单链表中是否有环
快慢指针,一个指针每次移动一个节点,另一个指针每次移动2个节点,只要这两个指针最后在某一个节点相逢,那个这个链表中接存在环结构 -
深度优先搜索和广度优先搜索
-
快排和冒泡时间复杂度
-
二分法
十. 网络
- Socket
- tcp/udp
- 三次握手,四次挥手, time_wait 为什么等待
- http
- http 和 https
- cookies 和 session
- session 表结构设计
- OSI七层模型
- DNS
- 网页访问的流程