第一章 Python综合
不定长参数有两种:*args和**kwargs;
*args:是不定长参数,用来将参数打包成tuple给函数体调用;
**kwargs:是关键字参数,打包关键字参数成dict给函数体调用;
在定义函数的时候不确定要传入的参数个数会有多少个的时候就可以使用不定长参数作为形参。
缺省参数指在调用函数的时候没有传入参数的情况下,调用默认的参数,在调用函数的同时赋值时,所传入的参数会替代默认参数。
lambda函数是匿名函数;使用lambda函数能创建小型匿名函数。这种函数得名于省略了用def声明函数的标准步骤;
(1)python中的拷贝分为浅拷贝和深拷贝,若不特殊说明拷贝一般是浅拷贝
(2)浅拷贝是将对象顶层拷贝,拷贝了引用,并没有拷贝内容,原对象改变新对象也跟着改变
(3)深拷贝是对一个对象的所有层次的拷贝(递归),但是修改原来的值,新对象不受影响
(4)浅拷贝对于可变类型和不可变类型是不同的,对于可变类型只拷贝顶层,不可变类型依然是原来的对象
(1)可变不可变指的是内存中的值是否可以被改变,不可变类型指的是对象所在内存块里面的值不可以改变,有数值、字符串、元组;可变类型则是可以改变,主要有列表、字典
(2)元组的顶层元素中包含可变类型,在可变类型中修改或添加字典id不会改变
(1)在Python中,一边循环一边计算的机制,称为生成器:generator,生成器是可以迭代对象,但是生成器可以通过send传值返回到前面;
(2)迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
(1)range(start,stop,step)函数按照从start到stop每个step生成一个数值,生成的是列表对象,一次性将所有数据都返回;
(2)xrange(start,stop,step)函数按照从start到stop每个step生成一个数值,返回的是可迭代对象,每次调用返回其中的一个值
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象
import time
def timeit(func):
def wrapper():
start = time.clock()
end =time.clock()
print 'used:', end - start
return wrapper
@timeit
def foo():
print 'in foo()'foo()
(1)确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。
Windows的Task Manager(任务管理器)、Recycle Bin(回收站)、网站计数器
(2)单例模式应用的场景一般发现在以下条件下:
资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
控制资源的情况下,方便资源之间的互相通信。如线程池等
(1)线程是并发,进程是并行;进程之间相互独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源
(2)并行:同一时刻多个任务同时在运行
(3)并发:在同一时间间隔内多个任务都在运行,但是并不会在同一时刻同时运行,存在交替执行的情况
(4)实现并行的库有:multiprocessing
(5)实现并发的库有:threading
(6)程序需要执行较多的读写、请求和回复任务的需要大量的IO操作,IO密集型操作使用并发更好
CPU运算量大的程序程序,使用并行会更好
(1)每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
(2)同一个进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,另一个线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果
内存管理机制:引用计数、垃圾回收、内存池
(1)引用计数
引用计数是一种非常高效的内存管理手段, 当一个Python对象被引 用时其引用计数增加1, 当其不再被一个变量引用时则计数减1. 当引用计数等于0时对象被删除.
(2)垃圾回收
引用计数也是一种垃圾收集机制,而且也是一种最直观,最简单的垃圾收集技术。当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为1。如果引用被删除,对象的引用计数为0,那么该对象就可以被垃圾回收。
不过如果出现循环引用的话,引用计数机制就不再起有效的作用了
如果两个对象的引用计数都为1,但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被回收的,也就是说,它们的引用计数虽然表现为非0,但实际上有效的引用计数为0。所以先将循环引用摘掉,就会得出这两个对象的有效计数。
从前面“标记-清除”这样的垃圾收集机制来看,这种垃圾收集机制所带来的额外操作实际上与系统中总的内存块的数量是相关的,当需要回收的内存块越多时,垃圾检测带来的额外操作就越多,而垃圾回收带来的额外操作就越少;反之,当需回收的内存块越少时,垃圾检测就将比垃圾回收带来更少的额外操作。
(3)内存池
1.Python的内存机制呈现金字塔形状,-1,-2层主要有操作系统进行操作;
2.第0层是C中的malloc,free等内存分配和释放函数进行操作;
3.第1层和第2层是内存池,有Python的接口函数PyMem_Malloc函数实现,当对象小于256K时有该层直接分配内存;
4.第3层是最上层,也就是我们对Python对象的直接操作;
Python在运行期间会大量地执行malloc和free的操作,频繁地在用户态和核心态之间进行切换,这将严重影响Python的执行效率。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。
Python内部默认的小块内存与大块内存的分界点定在256个字节,当申请的内存小于256字节时,PyObject_Malloc会在内存池中申请内存;当申请的内存大于256字节时,PyObject_Malloc的行为将蜕化为malloc的行为。当然,通过修改Python源代码,我们可以改变这个默认值,从而改变Python的默认内存管理行为。
(4)调优手段(了解)
1.手动垃圾回收
2.调高垃圾回收阈值
3.避免循环引用(手动解循环引用和使用弱引用)
read:读取整个文件
readline:读取下一行,使用生成器方法
readlines:读取整个文件到一个迭代器以供我们遍历
死锁:在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁
给互斥锁添加超时时间
程序设计时要尽量避免(银行家算法)
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行字节码。
线程释放GIL锁的情况:
在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL
Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100
Python使用多进程是可以利用多核的CPU资源的。
多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁
有日志;
python自带logging模块,调用logging.basicConfig()方法,配置需要的日志等级和相应的参数,python解释器会按照配置的参数生成相应的日志。
(1)TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
(2)TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
(3)TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
(4)每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
(5)TCP首部开销20字节;UDP的首部开销小,只有8个字节
(6)TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
(1)GET请求,请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。URL的编码格式采用的是ASCII编码,而不是uniclde,即是说所有的非ASCII字符都要编码之后再传输。
POST请求:POST请求会把请求的数据放置在HTTP请求包的包体中。上面的item=bandsaw就是实际的传输数据。
因此,GET请求的数据会暴露在地址栏中,而POST请求则不会。
(2)传输数据的大小
在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的长度有限制。因此,在使用GET请求时,传输数据会受到URL长度的限制。
对于POST,由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制,Apache、IIS都有各自的配置。
(3)安全性
POST的安全性比GET的高。这里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,上面提到的安全仅仅是不修改服务器的数据。比如,在进行登录操作,通过GET请求,用户名和密码都会暴露再URL上,因为登录页面有可能被浏览器缓存以及其他人查看浏览器的历史记录的原因,此时的用户名和密码就很容易被他人拿到了。除此之外,GET请求提交的数据还可能会造成Cross-site request frogery攻击。
(1)cookie数据存放在客户的浏览器上,session数据放在服务器上。
(2)cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
(3)session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE。
(4)单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
(5)建议:
将登陆信息等重要信息存放为SESSION
其他信息如果需要保留,可以放在COOKIE中
(1)2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,
(2)当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,
必须在此状态上停留两倍的MSL时间,等待2MSL时间主要目的是怕最后一个 ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。
(3)在TIME_WAIT状态 时两端的端口不能使用,要等到2MSL时间结束才可继续使用。
(4)当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。
不过在实际应用中可以通过设置 SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。
(1)孤儿进程:父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被init进程(进 程号为1)所收养,并由init进程对它们完成状态收集工作。
(2)僵尸进程:进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的这些进程是僵尸进程。
(3)避免僵尸进程的方法:
1.fork两次用孙子进程去完成子进程的任务。
2.用wait()函数使父进程阻塞。
3.使用信号量,在signal handler中调用 waitpid,这样父进程不用阻塞。
(1)短连接
连接->传输数据->关闭连接
HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束后就中断连接。短连接是指SOCKET连接后发送后接收完数据后马上断开连接。
(2)长连接
连接->传输数据->保持连接->传输数据->....->关闭连接
长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。
作用:告诉用户当前操作所处的状态
200 OK 服务器成功处理了请求(这个是我们见到最多的)
301/302 Moved Permanently(重定向)请求的URL已移走。Response中应该包含一个Location URL, 说明资源现在所处的位置
304 Not Modified(未修改)客户的缓存资源是最新的, 要客户端使用缓存
404 Not Found 未找到资源
501 Internal Server Error服务器遇到一个错误,使其无法对请求提供服
(1)原因: 1.竞争资源2.程序推进顺序不当
(2)必要条件:1.互斥条件2.请求和保持条件3.不剥夺条件4.环路等待条件
(3)处理死锁基本方法:
1、预防死锁(摒弃除1以外的条件)
2、避免死锁(银行家算法)
3、检测死锁(资源分配图)
(4)解除死锁:1.剥夺死锁 2.撤销进程
(浏览器发送一个请求到返回一个页面的具体过程)
第一步,解析域名,找到ip
浏览器会缓存DNS一段时间,一般2-30分钟不等,如果有缓存,直接返回ip,否则下一步。
缓存中无法找到ip,浏览器会进行一个系统调用,查询hosts文件。如果找到,直接返回ip,否则下一步。
进行1 和2 本地查询无果,只能借助于网络,路由器一般都会有自己的DNS缓存,ISP服务商DNS缓存,这时一般都能够得到相应的ip,如果还是无果,只能借助于DNS递归解析了。
这时ISP的DNS服务器就会开始从根域名服务器开始递归搜索,从.com 顶级域名服务器,到baidu的域名服务器。
到这里,浏览器就获得网络ip,在DNS解析过程中,常常解析出不同的IP。
第二步,浏览器与网站建立TCP连接
浏览器利用ip直接网站主机通信,浏览器发出TCP连接请求,主机返回TCP应答报文,浏览器收到应答报文发现ACK标志位为1,表示连接请求确认,浏览器返回TCP()确认报文,主机收到确认报文,三次握手,TCP连接建立完成。
第三步, 浏览器发起默认的GET请求
浏览器向主机发起一个HTTP-GET方法报文请求,请求中包含访问的URL,也就是http://www.baidu.com/还有User-Agent用户浏览器操作系统信息,编码等,值得一提的是Accep-Encoding和Cookies项。Accept-Encoding一般采用gzip,压缩之后传输html文件,Cookies如果是首次访问,会提示服务器简历用户缓存信息,如果不是,可以利用Cookies对应键值,找到相应缓存,缓存里面存放着用户名,密码和一些用户设置项
第四步,显示页面或返回其他
返回状态码200 OK,表示服务器可以响应请求,返回报文,由于在报头中Content-type为“text/html”,浏览器以HTML形式呈现,而不是下载文件。
但是对于大型网站存在多个主机站点,往往不会直接返回请求页面,而是重定向。返回的状态码就不是 200 OK, 而是301,302以3开头的重定向吗。浏览器在获取了重定向响应后,在响应报文中Location项找到重定向地址,浏览器重新第一步访问即可。
第三章 数据库
储存过程是一个可编程的函数,它在数据库中创建并保存。它可以有SQL语句和一些特殊的控制结构组成。当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的。数据库中的存储过程可以看做是对编程中面向对象方法的模拟。它允许控制数据的访问方式。存储过程通常有以下优点:
a、存储过程能实现较快的执行速度。
b、存储过程允许标准组件是编程。
c、存储过程可以用流程控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。
d、存储过程可被作为一种安全机制来充分利用。
e、存储过程能够减少网络流量。
(1)原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行。
(2)一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致。
(3)隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
(4)持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障
MYSQL 事务处理主要有两种方法:
1、用 BEGIN, ROLLBACK, COMMIT来实现
BEGIN 开始一个事务
ROLLBACK 事务回滚
COMMIT 事务确认
2、直接用 SET 来改变 MySQL 的自动提交模式:
SET AUTOCOMMIT=0 禁止自动提交
SET AUTOCOMMIT=1 开启自动提交
4.数据库索引
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B_TREE。B_TREE索引加速了数据访问,因为存储引擎不会再去扫描整张表得到需要的数据;相反,它从根节点开始,根节点保存了子节点的指针,存储引擎会根据指针快速寻找数据。
在表格上面创建某个一个唯一的索引。唯一的索引意味着两个行不能拥有相同的索引值。
CREATE UNIQUE INDEX 索引名称
ON 表名称 (列名称)
“列名称”规定你需要索引的列
原理:索引问题就是一个查找问题。
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
6.在关系型数据库中,索引(index)存在的意义是什么?
(1)通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
(2)可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
(3)可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
(4)在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
(5)通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
7.BTree索引和Hash索引的优缺点各是什么?
(1)BTree(多路搜索树,并不是二叉的)是一种常见的数据结构。使用BTree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。这个数据结构一般用于数据库的索引,综合效率较高。
不适合:单列索引的列不能包含null的记录,复合索引的各个列不能包含同时为null的记录,否则会全表扫描;
不适合键值较少的列(重复数据较多的列);前导模糊查询不能利用索引(like '%XX'或者like '%XX%')
(2)Hash散列索引
Hash散列索引是根据HASH算法来构建的索引。虽然 Hash 索引效率高,但是 Hash 索引本身由于其特殊性也带来了很多限制和弊端,主要有以下这些。
适合:精确查找非常快(包括= <> 和in),其检索效率非常高,索引的检索可以一次定位,不像BTree 索引需要从根节点到枝节点,所以 Hash 索引的查询效率要远高于 B-Tree 索引。
不适合:
1、不适合模糊查询和范围查询(包括like,>,<,between……and等),由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样;
2、不适合排序,数据库无法利用索引的数据来提升排序性能,同样是因为Hash值的大小不确定;
复合索引不能利用部分索引字段查询,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。
同样不适合键值较少的列(重复值较多的列);
8.redis 和 mysql 的区别
readis 是内存数据库, 数据保存在内存中, 速度快。
mysql 是关系型数据库,持久化存储,存放在磁盘里面,功能强大。检索的话, 会涉及到一定的 IO, 数据访问也就慢。
9.你用的mysql是哪个引擎,各引擎之间有什么区别?
主要 MyISAM 与 InnoDB 两个引擎,其主要区别如下:
InnoDB 支持事务,MyISAM 不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而 MyISAM就不可以了;
MyISAM 适合查询以及插入为主的应用,InnoDB 适合频繁修改以及涉及到安全性较高的应用;
InnoDB 支持外键,MyISAM 不支持;
MyISAM 是默认引擎,InnoDB 需要指定;
InnoDB 不支持 FULLTEXT 类型的索引;
InnoDB 中不保存表的行数,如 select count(*) from table 时,InnoDB;需要扫描一遍整个表来计算有多少行,但是 MyISAM 只要简单的读出保存好的行数即可。注意的是,当 count(*)语句包含 where 条件时 MyISAM 也需要扫描整个表;
对于自增长的字段,InnoDB 中必须包含只有该字段的索引,但是在 MyISAM表中可以和其他字段一起建立联合索引;清空整个表时,InnoDB 是一行一行的删除,效率非常慢。MyISAM 则会重建表;
InnoDB 支持行锁(某些情况下还是锁整表,如 update table set a=1 where user like '%lee%'
10.Redis和MongoDB的优缺点
MongoDB 和 Redis 都是 NoSQL, 采用结构型数据存储。 二者在使用场景中, 存在一定的区别, 这也主要由于二者在内存映射的处理过程, 持久化的处理方法不同。 MongoDB 建议集群部署, 更多的考虑到集群方案,Redis 更偏重于进程顺序写入, 虽然支持集群, 也仅限于主-从模式.
Redis 优点:
读写性能优异
支持数据持久化, 支持 AOF 和 RDB 两种持久化方式
支持主从复制, 主机会自动将数据同步到从机, 可以进行读写分离。
数据结构丰富: 除了支持 string 类型的 value 外还支持string、 hash、 set、 sortedset、 list 等数据结构。
Redis缺点:
Redis 不具备自动容错和恢复功能, 主机从机的宕机都会导致前端部分读写请求失败, 需要等待机器重启或者手动切换前端的 IP才能恢复。
主机宕机, 宕机前有部分数据未能及时同步到从机, 切换 IP后还会引入数据不一致的问题, 降低了系统的可用性。
Redis 的主从复制采用全量复制, 复制过程中主机会 fork 出一个子进程对内存做一份快照, 并将子进程的内存快照保存为文件发送给从机, 这一过程需要确保主机有足够多的空余内存。 若快照文件较大, 对集群的服务能力会产生较大的影响, 而且复制过程是在从机新加入集群或者从机和主机网络断开重连时都会进行, 也就是网络波动都会造成主机和从机间的一次全量的数据复制, 这对实际的系统运营造成了不小的麻烦。
Redis 较难支持在线扩容, 在集群容量达到上限时在线扩容会变得很复杂。 为避免这一问题, 运维人员在系统上线时必须确保有足够的空间, 这对资源造成了很大的浪费。
MongoDB优点:
弱一致性(最终一致) , 更能保证用户的访问速度文档结构的存储方式, 能够更便捷的获取数内置 GridFS, 高效存储二进制大对象 (比如照片和视频)
支持复制集、 主备、 互为主备、 自动分片等特性
动态查询
全索引支持,扩展到内部对象和内嵌数组
MongoDB缺点:
不支持事务
MongoDB 占用空间过大
维护工具不够成熟
10.数据库优化方案
优化索引、SQL 语句、分析慢查询
设计表的时候严格根据数据库的设计范式来设计数据库
使用缓存,把经常访问到的数据而且不需要经常变化的数据放在缓存中,能节约磁盘IO
优化硬件;采用SSD,使用磁盘队列技术(RAID0,RAID1,RDID5)等;
采用MySQL 内部自带的表分区技术,把数据分层不同的文件,能够提高磁盘的读取效率
垂直分表;把一些不经常读的数据放在一张表里,节约磁盘I/O
主从分离读写;采用主从复制把数据库的读操作和写入操作分离开来
分库分表分机器(数据量特别大),主要的的原理就是数据路由
选择合适的表引擎,参数上的优化
进行架构级别的缓存,静态化和分布式
不采用全文索引
采用更快的存储方式,例如NoSql存储经常访问的数据
11.怎样解决海量数据的存储和访问造成系统设计瓶颈的问题?
- 水平切分数据库: 可以降低单台机器的负载, 同时最大限度的降低了宕机造成的损失; 分库降低了单点机器的负载; 分表, 提高了数据操作的效率,
- 负载均衡策略: 可以降低单台机器的访问负载, 降低宕机的可能性;
- 集群方案:解决了数据库宕机带来的单点数据库不能访问的问题;
- 读写分离策略:最大限度了提高了应用中读取数据的速度和并发量;
12.怎样解决数据库高并发的问题?
解决数据库高并发:
分表分库
数据库索引
redis缓存数据库
读写分离
负载均衡集群:将大量的并发请求分担到多个处理节点。由于单个处理节点的故障不影响整个服务,负载均衡集群同时也实现了高可用性。
12.数据库负载均衡
负载均衡集群是由一组相互独立的计算机系统构成,通过常规网络或专用网络进行连接, 由路由器衔接在一起, 各节点相互协作、 共同负载、 均衡压力, 对客户端来说, 整个群集可以视为一台具有超高性能的独立服务器。
1、 实现原理
实现数据库的负载均衡技术,首先要有一个可以控制连接数据库的控制端。 在这里, 它截断了数据库和程序的直接连接, 由所有的程序来访问这个中间层, 然后再由中间层来访问数据库。 这样, 我们就可以具体控制访问某个数据库了,然后还可以根据数据库的当前负载采取有效的均衡策略, 来调整每次连接到哪个数据库。
2、 实现多据库数据同步
对于负载均衡,最重要的就是所有服务器的数据都是实时同步的。这是一个集群所必需的, 因为, 如果数不据实时、 不同步, 那么用户从一台服务器读出的数据, 就有别于从另一台服务器读出的数据, 这是不能允许的。 所以必须实现数据库的数据同步。 这样, 在查询的时候就可以有多个资源, 实现均衡。比较常用的方法是 Moebius for SQL Server 集群, Moebius for SQL Server 集群采用将核心程序驻留在每个机器的数据库中的办法, 这个核心程序称为 Moebius for SQL Server 中间件, 主要作用是监测数据库内数据的变化并将变化的数据同步到其他数据库中。 数据同步完成后客户端才会得到响应, 同步过程是并发完成的,所以同步到多个数据库和同步到一个数据库的时间基本相等; 另外同步的过程是在事务的环境下完成的, 保证了多份数据在任何时刻数据的一致性。 正因为 Moebius 中间件宿主在数据库中的创新, 让中间件不但能知道数据的变化, 而且知道引起数据变化的 SQL 语句,根据 SQL 语句的类型智能的采取不同的数据同步的策略以保证数据同步成本的最小化。数据条数很少, 数据内容也不大, 则直接同步数据数据条数很少, 但是里面包含大数据类型, 比如文本, 二进制数
据等, 则先对数据进行压缩然后再同步, 从而减少网络带宽的占用和传输所用的时间。数据条数很多,此时中间件会拿到造成数据变化的 SQL 语句, 然后对 SQL 语句进行解析, 分析其执行计划和执行成本, 并选择是同步数据还是同步 SQL 语句到其他的数据库中。此种情况应用在对表结构进行调整或者批量更改数据的时候非常有用。
3、 优缺点优点:
**优点:**
- (1) 扩展性强: 当系统要更高数据库处理速度时, 只要简单地增加数据库服务器就 可以得到扩展。
- (2) 可维护性: 当某节点发生故障时, 系统会自动检测故障并转移故障节点的应用, 保证数据库的持续工作。
- (3) 安全性: 因为数据会同步的多台服务器上, 可以实现数据集的冗余, 通过多份数据来保证安全性。 另外它成功地将数据库放到了内网之中, 更好地保护了数据库的安全性。
- (4) 易用性: 对应用来说完全透明, 集群暴露出来的就是一个IP
**缺点:**
- (1) 不能够按照 Web 服务器的处理能力分配负载。
- (2) 负载均衡器(控制端)故障, 会导致整个数据库系统瘫痪。
13.数据库备份怎么做,服务器挂了怎么办?
备份数据库
shell> mysqldump -h host -u root -p dbname >dbname_backup.sql
恢复数据库
shell> mysqladmin -h myhost -u root -p create dbname
shell> mysqldump -h host -u root -p dbname < dbname_backup.sql
14.如何把整个数据库导出来,再导入指定数据库中
导出:
mysqldump [-h主机] -u用户名 -p 数据库名 > 导出的数据库名.sql
导入指定的数据库中:
第一种方法:
mysqldump [-h主机] -u用户名 -p 数据库名 < 导出的数据库名.sql
第二种方法:
先创建好数据库,因为导出的文件里没有创建数据库的语句,如果数据库已经建好,则不用再创建。
create database example charset=utf8;(数据库名可以不一样)
切换数据库:
use example;
导入指定sql文件:
mysql>source /path/example.sql;
15.数据库怎么优化查询效率?
1、储存引擎选择:如果数据表需要事务处理,应该考虑使用InnoDB,因为它完全符合ACID特性。如果不需要事务处理,使用默认存储引擎MyISAM是比较明智的
2、分表分库,主从。
3、对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
4、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
5、应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描
6、应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
7、Update 语句,如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志
8、对于多张大数据量(这里几百条就算大了)的表JOIN,要先分页再JOIN,否则逻辑读会很高,性能很差。
1、尽量使数据库一次性写入Data File
2、减少数据库的checkpoint 操作
3、程序上尽量缓冲数据,进行批量式插入与提交
4、减少系统的IO冲突
1,slow_query_log
这个参数设置为ON,可以捕获执行时间超过一定数值的SQL语句。
2,long_query_time
当SQL语句执行时间超过此数值时,就会被记录到日志中,建议设置为1或者更短。
第四章 前端
1.什么是css初始化?有什么好处?
- CSS初始化是指重设浏览器的样式。不同的浏览器默认的样式可能不尽相同,如果没对CSS初始化往往会出现浏览器之间的页面差异。
- 好处:能够统一标签在各大主流浏览器中的默认样式,使得我们开发网页内容时更加方便简洁,同时减少CSS代码量,节约网页下载时间。
2.简述浮动的特征和清除浮动的方法?
- **浮动的特征:**
- 浮动元素有左浮动(float:left)和右浮动(float:right)两种
- 浮动的元素会向左或向右浮动,碰到父元素边界、其他元素才停下来
- 相邻浮动的块元素可以并在一行,超出父级宽度就换行
- 浮动让行内元素或块元素转化为有浮动特性的行内块元素(此时不会有行内块元素间隙问题)
- 父元素如果没有设置尺寸(一般是高度不设置),父元素内整体浮动的子元素无法撑开父元素,父元素需要清除浮动
- **清除浮动的方法:**
- 父级上增加属性overflow:hidden
- 在最后一个子元素的后面加一个空的div,给它样式属性 clear:both
- 使用成熟的清浮动样式类,clearfix
```css
.clearfix:after,.clearfix:before{ content: "";display: table;}
.clearfix:after{ clear:both;}
.clearfix{zoom:1;}
```
React是一个用于构建用户界面的js库 React主要用于构建UI, 是 MVC 中的 V(视图)拥有较高的性能,代码逻辑非常简单
Vue 是一套用于构建用户界面的渐进式框架Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
第五章 web框架
1.MVC解读
M:Model,模型,和数据库进行交互
V:View,视图,负责产生Html页面
C:Controller,控制器,接收请求,进行处理,与M和V进行交互,返回应答。
1、 用户点击注按钮,将要注册的信息发送给网站服务器。
2、 Controller控制器接收到用户的注册信息,Controller会告诉Model层将用户的注册信息保存到数据库
3、 Model层将用户的注册信息保存到数据库
4、 数据保存之后将保存的结果返回给Model模型,
5、 Model层将保存的结果返回给Controller控制器。
6、 Controller控制器收到保存的结果之后,或告诉View视图,view视图产生一个html页面。
7、 View将产生的Html页面的内容给了Controller控制器。
8、 Controller将Html页面的内容返回给浏览器。
9、 浏览器接受到服务器Controller返回的Html页面进行解析展示。
manage.py是项目运行的入口,指定配置文件路径。
与项目同名的目录,包含项目的配置文件
___init.py是一个空文件,作用是这个目录可以被当作包使用。
settings.py是项目的整体配置文件。
urls.py是项目的URL配置文件。
wsgi.py是项目与WSGI兼容的Web服务器入口
3.MVT解读
M:Model,模型,和MVC中的M功能相同,和数据库进行交互。
V:view,视图,和MVC中的C功能相同,接收请求,进行处理,与M和T进行交互,返回应答。
T:Template,模板,和MVC中的V功能相同,产生Html页面
1、 用户点击注册按钮,将要注册的内容发送给网站的服务器。
2、 View视图,接收到用户发来的注册数据,View告诉Model将用户的注册信息保存进数据库。
3、 Model层将用户的注册信息保存到数据库中。
4、 数据库将保存的结果返回给Model
5、 Model将保存的结果给View视图。
6、 View视图告诉Template模板去产生一个Html页面。
7、 Template生成html内容返回给View视图。
8、 View将html页面内容返回给浏览器。
9、 浏览器拿到view返回的html页面内容进行解析,展示。
(新增)说一下Django,MIDDLEWARES中间件的作用?
中间件是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。
(新增)django中那里用到了线程,那里用到了协程,那里用到了进程?
```
1.django利用多线程增加异步任务.celery消息队列.
2.django中使用多线程发送邮件.send_mail().
3.django原生为单线程序,当第一个请求没有完成时,第二个请求阻塞,直到第一个请求完成,第二个请 求才会执行.
使用uwsgi编程多并发,使用nginx+uwsgi提供高并发,nginx的并发能力超高,单台并发能力过万 (不绝对).
4.django自带的development server为多线程模式,但是他还有一个小问题就是它不是线程安全的。 可能在请求很多时会出现数据不同步,当然,这一般不是问题,因为我们通常只在自己机器上调试时才用Development Server。
```
4.django关闭浏览器,怎么清除cookies和session?
cookie是有过期时间的,如果不指定,默认关闭浏览器之后cookie就会过期。
如果 SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 False ,cookie可以在用户浏览器中保持有效达 SESSION_COOKIE_AGE 秒(存活时间)。
如果不想用户每次打开浏览器都必须重新登陆的话,可以用这个参数。
如果 SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 True ,当浏览器关闭时,Django会使cookie失效
Django中操作session:
session也是有过期时间,如果不指定,默认两周就会过期。
request.session.set_expiry(0);那么当浏览器关闭时,session失效
删除session:del request[key]
5.Django里QuerySet的get和filter方法的区别
1、输入参数
- get 的参数只能是model中定义的那些字段,只支持严格匹配
- filter 的参数可以是字段,也可以是扩展的where查询关键字,如in,like等
2、返回值
- get 返回值是一个定义的model对象
- filter 返回值是一个新的QuerySet对象,然后可以对QuerySet在进行查询返回新的QuerySet对象,支持链式操作QuerySet一个集合对象,可使用迭代或者遍历,切片等,但是不等于list类型(使用一定要注意)
3、异常
- get 只有一条记录返回的时候才正常,也就说明get的查询字段必须是主键或者唯一约束的字段。当返回多条记录或者是没有找到记录的时候都会抛出异常
- filter 有没有匹配的记录都可以。
6.简述Django对http请求的执行流程
> 在接受一个Http请求之前的准备
> 启动一个支持WSGI网关协议的服务器监听端口等待外界的Http请求,比如Django自带的开发者服务器或者uWSGI服务器。
> 服务器根据WSGI协议指定相应的Handler来处理Http请求,并且初始化该Handler,在Django框架中由框架自身负责实现这一个Handler。
> 此时服务器已处于监听状态,可以接受外界的Http请求
> 当一个http请求到达服务器的时候
> 服务器根据WSGI协议从Http请求中提取出必要的参数组成一个字典(environ)并传入Handler中进行处理。
> 在Handler中对已经符合WSGI协议标准规定的http请求进行分析,比如加载Django提供的中间件,路由分配,调用路由匹配的视图等。
> 返回一个可以被浏览器解析的符合Http协议的HttpResponse。
7.简述Django下的(内建)缓存机制
> Django根据设置的缓存方式,浏览器第一次请求时,cache会缓存单个变量或整个网页等内容到硬盘或者内存中,同时设置response头部,当浏览器再次发起请求时,附带f-Modified-Since请求时间到Django,Django 发现f-Modified-Since会先去参数之后,会与缓存中的过期时间相比较,如果缓存时间比较新,则会重新请求数据,并缓存起来然后返回response给客户端,如果缓存没有过期,则直接从缓存中提取数据,返回给response给客户端。
8.Django中model的SlugField类型字段有什么用途?
> SlugField字段是将输入的内容中的空格都替换成‘-’之后保存,Slug 是一个新闻术语,通常是某些东西的短标签。一个slug只能包含字母、数字、下划线或者是连字符,通常用来作为短标签。通常它们是用来放在URL里的。
>
> SlugField字段的Field.db_index自动设置为True。
>
> 通常根据另一个值自动生成slug来填充到SlugField的值
聚合支付:ping++,付钱拉,Paymax...
直接对接:支付宝,微信,qq钱包,银联,百度钱包...
建议:去该类官网了解接口相关知识
1、简单加密
目的是为了保证上传的参数信息没有被篡改,主要分成三部分
接口参数 : 需要和第三方对接的参数
加密类型 : 使用什么类型加密,一般为MD5
加密密文 : 使用接口参数和第三方生成的Code值(固定salt),进行MD5加密成密文
Md5作为数字签名 H(A)=P 已知A P,在特殊情况下可以伪造A1 满足H(A1)=p
2、生成加密的密钥Key
随机生成16位的加密密钥Key,用于对上一步的内容进行对称加密
3、使用证书加密密钥Key
对随机生成的密钥key进行加密。防止在传输过程中被截获破解。
使用了三种加密算法:
摘要算法:验证原文是否被篡改
对称加密算法:使用密钥对原文进行加密(AES)
非对称加密算法:对密钥进行分发
发送方:
入参 + H(入参+分配的code)= P 得到键值对 ParamMap
AES ( ParamMap + 随机生成的Key ) 进行对称加密得到 DecodeString
随机生成的Key + 私钥证书加密, 得到 EnCodeKey
Http发送请求, 将 DecodeString 和 EnCodeKey发送给第三方支付公司
接收方:
证书加密的随机数,使用私钥解密得到 Akey
AES(AKey) = sourceCode("入参","P")
验证:H(sourceCode + code) = P
(新增)Form表单的提交和ajax的提交
1、提交方式
form表单通常是通过在HTML中定义的action,method及submit来进行表单提交,另外也可以通过在js中调用submit函数来进行表单提交。
具体的提交方式有很多种,比如可以通过封装成XMLHttpRequest对象进行提交。Ajax是基于XMLHttpRequest进行的。
2、页面刷新
Form提交,更新数据完成后,需要转到一个空白页面再对原页面进行提交后处理。哪怕是提交给自己本身的页面,也是需要刷新的,因此局限性很大。
Ajax可以实现页面的局部刷新,整个页面不会刷新。
3、请求由谁来提交
Form提交是浏览器完成的,无论浏览器是否开启JS,都可以提交表单。
Ajax是通过js来提交请求,请求与响应均由js引擎来处理,因此不启用JS的浏览器,无法完成该操作。
4、是否可以上传文件
最初,ajax出于安全性考虑,不能对文件进行操作,所以就不能通过ajax来实现文件上传,但是通过隐藏form提交则可以实现这个功能,所以这也是用隐藏form提交的主要用途。
后来XMLHttpRequest引入了FormData类型,使得通过Ajax也可以实现文件上传。
(新增)类似qq在线人数,用ajax怎么实现?
可以使用ajax的异步处理,(单使一个一个进程或线程)每隔一段时间请求数据库人数数据。
Ajax主要功能是实现异步,事件触发,Ajax 会创建一个xmlhttprequest,对象,把http方法和目标url,以及回调函数函数设置到xmlhttprequest对象,通过xmlhttprequest对象向浏览器发送请求,请求发送后继续响应用户,并交互,只有等到请求真正从服务器返回的时候才调用callback()函数,对响应数据进行处理.
关键的技术: xhtml css ,用来格式化输出页面,dom (动态修改文档的内容和结构,),xml进行数据交换和处理, javascript 进行上述技术的捆绑,使其协同工作.
**总结:**
```
Ajax 利用javascript创建xmlhttpscript对象,再由javascript调用xmljtmlscript对象完成随异步通讯最后通过javascript 调用dom的属性和方法进行页面不完全刷新
(新增)项目中使用什么调试?
- 1、在Eclipse+Pydev中调试Django
适用于测试环境。
可进行单步调试,查看变量值,当出现except时,可以用Python标准模块traceback的print_exc()函数查看函数调用链,
是最强大的调试利器。
- 2、使用Django的error page
适用于测试环境。
Django的error page功能很强大,能提供详细的traceback,包括局部变量的值,以及一个纯文本的异常信息。拥有同phpinfo()
一样的作用,可以展示当前应用的相关设置,包括请求中的 GET, POST and COOKIE 数据以及HTTP环境中的所有重要META fields。
- 3、django-debug-toolbar
不确定是否用于生产环境。听说功能非常强大。
- 4、输出log到开发服务器终端中
适用于生产环境。
借助python的logging模块
10.项目怎么优化?提过哪些建议?
对于开发人员来说,网站性能优化一般包括Web前端性能优化、应用服务器性能优化、存储服务器性能优化三类。
**Web前端性能优化:**
- 1、减少http请求 http协议是无状态的应用层协议,意味着每次http请求都需要建立通信链路、进行数据传输,而在服务器端,每个http请求都需要启动独立的线程去处理。减少http请求的数目可有效提高访问性能。**减少http的主要手段是合并CSS、合并javascript、合并图片。**
- 2、使用浏览器缓存 对一个网站而言,CSS、javascript、logo、图标,这些静态资源文件更新的频率都比较低,而这些文件又几乎是每次http请求都需要的。如果将这些文件缓存在浏览器中,可以极好的改善性能。通过设置http头中的cache-control和expires的属性,可设定浏览器缓存,缓存时间可以自定义。
- 3、启用压缩 在服务器端对文件进行压缩,在浏览器端对文件解压缩,可有效减少通信传输的数据量。如果可以的话,尽可能的将外部的脚本、样式进行合并,多个合为一个。文本文件的压缩效率可达到80%以上,因此HTML、CSS、javascript文件启用GZip压缩可达到较好的效果。但是压缩对服务器和浏览器产生一定的压力,在网络带宽良好,而服务器资源不足的情况下要综合考虑。
- 4、CSS放在页面最上部,javascript放在页面最下面 浏览器会在下载完成全部CSS之后才对整个页面进行渲染,因此最好的做法是将CSS放在页面最上面,让浏览器尽快下载CSS。 Javascript则相反,浏览器在加载javascript后立即执行,有可能会阻塞整个页面,造成页面显示缓慢,因此javascript最好放在页面最下面。
**应用服务器优化**
应用服务器也就是处理网站业务的服务器,网站的业务代码都部署在这里,主要优化方案有缓存、异步、集群等。
**1、合理使用缓存**
当网站遇到性能瓶颈时,第一个解决方案一般是缓存。在整个网站应用中,缓存几乎无处不在,无论是客户端,还是应用服务器,或是数据库服务器。在客户端和服务器的交互中,无论是数据、文件都可以缓存,合理使用缓存对网站性能优化非常重要。
缓存一般用来存放那些读写次数比较高,变化较少的数据,比如网站首页的信息、商品的信息等。应用程序读取数据时,一般是先从缓存中读取,如果读取不到或数据已失效,再访问磁盘数据库,并将数据再次写入缓存。
缓存的基本原理是将数据存储在相对有较高访问速度的存储介质中,比如内存。一方面缓存访问速度快,另一方面,如果缓存的数据是需要经过计算处理得到的,那使用缓存还可以减少服务器处理数据的计算时间。
使用缓存并不是没有缺陷:内存资源是比较宝贵的,不可能将所有数据都缓存,一般频繁修改的数据不建议使用缓存,这会导致数据不一致。
网站数据缓存一般遵循二八定律,即80%的访问都在20%的数据上。所以,一般将这20%的数据缓存,可以起到改善系统性能,提高服务器读取效率。
**2、异步操作**
使用消息队列将调用异步化,可以改善网站系统的性能。
在不使用消息队列的情况下,用户的请求直接写入数据库,在高并发的情况下,会对数据库造成非常大的压力,也会延迟响应时间。
在使用消息队列后,用户请求的数据会发送给消息队列服务器,消息队列服务器会开启进程,将数据异步写入数据库。消息队列服务器的处理速度远超过数据库,因此用户的响应延迟可得到改善。
消息队列可以将短时间内的高并发产生的事务消息,存储在消息队列中,从而提高网站的并发处理能力。在电商网站的促销活动中,合理使用消息队列,可以抵御短时间内用户高并发的冲击。
**3、使用集群**
在网站高并发访问的情况下,使用负载均衡技术,可以为一个应用构建由多台服务器组成的服务器集群,将并发访问请求,分发到多台服务器上处理,避免单一服务器因负载过大,而导致响应延迟。
**4、代码优化**
网站的业务逻辑代码主要部署在应用服务器上,需要处理复杂的并发事务。合理优化业务代码,也可以改善网站性能。
任何web网站都会遇到多用户的并发访问,大型网站的并发用户会达到数万。每个用户请求都会创建一个独立的系统进程去处理。由于线程比进程更轻量,占用资源更少,所以,目前主流的web应用服务器都采用多线程的方式,处理并发用户的请求,因此,网站开发多数都是多线程编程。
使用多线程的另一个原因是服务器有多个CPU,现在手机都到了8核CPU的时代,一般的服务器至少是16核CPU,要想最大限度的使用这些CPU,必须启动多线程。
那么,启动多少线程合适呢?
启动线程数和CPU内核数量成正比,和IO等待时间成正比。如果都是计算型的任务,那么线程数最多不要超过CPU内核数,因为启动再多,CPU也来不及调用。如果任务是等待读写磁盘、网络响应,那么多启动线程会提高任务并发度,提高服务器性能。
或者用个简化的公式来描述:
启动线程数 = (任务执行时间/(任务执行事件 - IO等待时间)) * CPU内核数
**5、存储优化**
数据的读写是网站处理并发访问的另一瓶颈。使用缓存虽然可以解决一部分数据读写压力,但很多时候,磁盘仍然是系统最严重的瓶颈。而且磁盘是网站最重要的资产,磁盘的可用性和容错性也至关重要。
机械硬盘和固态硬盘 机械硬盘是目前最常用的硬盘,通过马达带动磁头到指定磁盘的位置访问数据,每次访问数据都需要移动磁头,在读取连续数据和随机访问上,磁头移动的次数相差巨大,因此机械硬盘的性能表现差别巨大,读写效率较低。而在网站应用中,大多数数据的访问都是随机的,在这种情况下,固态硬盘具有更高的性能。但目前固态硬盘在工艺上、数据可靠性上还有待提升,因此固态硬盘的使用尚未普及,从发展趋势看,取代机械硬盘应该是迟早的事情。
总结:
网站性能优化是在用户高并发访问,网站遇到问题时的解决方案。所以网站性能优化的主要内容是改善高并发用户访问情况下的网站响应速度。
网站性能优化的最终目的是改善用户的体验。但性能优化本身也是需要综合考虑的。比如说,性能提高一倍,服务器数量也要增加一倍,这样的优化是否可以考虑?
技术是由业务驱动的,离开业务的支撑,任何性能优化都是空中楼阁。
11.Flask和Django路由映射的区别?
在django中,路由是浏览器访问服务器时,先访问的项目中的url,再由项目中的url找到应用中url,这些url是放在一个列表里,遵从从前往后匹配的规则。在flask中,路由是通过装饰器给每个视图函数提供的,而且根据请求方式的不同可以一个url用于不同的作用。
@app.route('
第一步:写正则类,继承BaseConverter,将匹配到的值设置为regex的值
class RegexUrl(BaseConverter):
def __init__(self, url_map, *args):
super(RegexUrl, self).__init__(url_map)
self.regex = args[0]
第二步:把正则类赋值给我们定义的正则规则
app.url_map.converters['re'] = RegexUrl
第三步:在URL中使用正则
@app.route('/regex/
def regex111(id):
return 'id:%s'%id
current_app、g就是应用上下文
requests、session就是请求上下文
手动创建上下文的两种方法:
with app.app_context()
app = current_app._get_current_object()
{{ expression | filter1 | filter2 | ... }} 即 表达式(expression)使用filter1过滤后再使用filter2过滤..
作用:可以配置请求执行完逻辑之后自动提交,而不用我们每次都手动调用session.commit()
补充:
监听数据库中的数据,当发生改变,就会显示一些内容
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=True
显示打印的数据以及sql语句,建议不设置,默认为False
app.config['SQLALCHEMY_ECHO'] = True
filter()模糊查询:把过滤器添加到原查询上,返回一个新查询
filter_by()精确查询:把等值过滤器添加到原查询上,返回一个新查询
1.蓝图定义:
蓝图 /Blueprint 是Flask应用程序 组件化 的方法,可以在一个应用内或跨越 多个项目共用蓝图。使用蓝图可以极大地简化大型应用的开发难度,也为Flask扩展 提供了一种在应用中注册服务的集中式机制。
2.蓝图的应用场景
1,把一个应用分解为一个蓝图的集合。这对大型应用是理想的。一个项目可以实例化一个应用对象,初始化几个扩展,并注册一集合的蓝图。
2,以 URL 前缀和/或子域名,在应用上注册一个蓝图。 URL 前缀/子域名中的参数即成为这个蓝图下的所有视图函数的共同的视图参数(默认情况下)。
3,在一个应用中用不同的 URL 规则多次注册一个蓝图。
4,通过蓝图提供模板过滤器、静态文件、模板和其它功能。一个蓝图不一定要实现应用或者视图函数。
5,初始化一个 Flask 扩展时,在这些情况中注册一个蓝图。
3.蓝图的缺点
不能在应用创建后撤销注册一个蓝图而不销毁整个应用对象。
4.使用蓝图的三个步骤
1.创建 一个蓝图对象
blue = Blueprint("blue",__name__)
2.在这个蓝图对象上进行 操作 ,例如注册路由、指定静态文件夹、注册模板过滤器...
@blue.route('/')
def blue_index():
return 'Welcome to my blueprint'
3.在应用对象上 注册 这个蓝图对象
app.register_blueprint(blue,url_prefix='/blue')
图中Browse是浏览器,WebServerA是受信任网站/被攻击网站A,WebServerB是恶意网站/攻击网站B。
(1)一开始用户打开浏览器,访问受信任网站A,输入用户名和密码登陆请求登陆网站A。
(2)网站A验证用户信息,用户信息通过验证后,网站A产生Cookie信息并返回给浏览器。
(3)用户登陆网站A成功后,可以正常请求网站A。
(4)用户未退出网站A之前,在同一浏览器中,打开一个TAB访问网站B。
(5)网站B看到有人方式后,他会返回一些攻击性代码。
(6)浏览器在接受到这些攻击性代码后,促使用户不知情的情况下浏览器携带Cookie(包括sessionId)信息,请求网站A。这种请求有可能更新密码,添加用户什么的操作。
从上面CSRF攻击原理可以看出,要完成一次CSRF攻击,需要被攻击者完成两个步骤:
1.登陆受信任网站A,并在本地生成COOKIE。
2.在不登出A的情况下,访问危险网站 B。
如果不满足以上两个条件中的一个,就不会受到CSRF的攻击,以下情况可能会导致CSRF:
1.登录了一个网站后,打开一个tab页面并访问另外的网站。
2.关闭浏览器了后,本地的Cookie尚未过期,你上次的会话还没有已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了……)
解决办法:就是在表单中添加from.csrf_token
flask中有三个session:
第一个:数据库中的session,例如:db.session.add()
第二个:在flask_session扩展中的session,使用:from flask_session import Session,使用第三方扩展的session可以把信息存储在服务器中,客户端浏览器中只存储sessionid
第三个:flask自带的session,是一个请求上下文, 使用:from flask import session。自带的session把信息加密后都存储在客户端的浏览器cookie中
可以传入的参数:
1,字符串:‘hello’,
但是‘abc’,不行,因为abc是python内置的模块
2,__name__,约定俗成
不可以插入的参数
1,python内置的模块,re,urllib,abc等
2,数字
请求钩子是通过装饰器的形式实现的,支持以下四种:
1,before_first_request在处理第一个请求前运行
2,before_request:在每次请求前运行
3,after_request:如果没有未处理的异常抛出,在每次请求后运行
4,teardown_request:即使有未处理的异常抛出,在每次请求后运行
应用:
# 请求钩子
@api.after_request
def after_request(response):
"""设置默认的响应报文格式为application/json"""
# 如果响应报文response的Content-Type是以text开头,则将其改为默认的json类型
if response.headers.get("Content-Type").startswith("text"):
response.headers["Content-Type"] = "application/json"
return response
第一步:先定义自定义过滤器函数
def count_substring(string, substring):
return string.count(substring)
第二步:注册自己定义的过滤器
app.jinja_env.filters['count_substring'] = count_substring
第三步:最后在模板文件html中直接使用注册时的键名
{#前面的作为原字符串string,传入的作为子字符串substring#}
{{ 'A long long long long long long longabc string ' | count_substring('long') }}
第六章 爬虫
通过在settings.py中设置DEPTH_LIMIT的值可以限制爬取深度,这个深度是与start_urls中定义url的相对值。也就是相对url的深度。若定义url为http://www.domz.com/game/,DEPTH_LIMIT=1那么限制爬取的只能是此url下一级的网页。深度大于设置值的将被忽视。
使用FormRequest
class mySpider(scrapy.Spider):
# start_urls = ["http://www.taobao.com/"]
def start_requests(self):
url = 'http://http://www.taobao.com//login'
# FormRequest 是Scrapy发送POST请求的方法
yield scrapy.FormRequest(
url = url,
formdata = {"email" : "xxx", "password" : "xxxxx"},
callback = self.parse_page
)
def parse_page(self, response):
# do something
user-agent、代理池等
答:可以使用正则、xpath、json、pyquery、bs4等(任选一二即可)。
[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$
透明代理的意思是客户端根本不需要知道有代理服务器的存在,但是它传送的仍然是真实的IP。你要想隐藏的话,不要用这个。
普通匿名代理能隐藏客户机的真实IP,但会改变我们的请求信息,服务器端有可能会认为我们使用了代理。不过使用此种代理时,虽然被访问的网站不能知道你的ip地址,但仍然可以知道你在使用代理,当然某些能够侦测ip的网页仍然可以查到你的ip。
高匿名代理不改变客户机的请求,这样在服务器看来就像有个真正的客户浏览器在访问它,这时客户的真实IP是隐藏的,服务器端不会认为我们使用了代理。
存放在数据库(redis、mysql等)
维护多个代理网站
一般代理的存活时间往往在十几分钟左右,定时任务,加上代理IP去访问网页,验证其是否可用,如果返回状态为200,表示这个代理是可以使用的。
Selenium是一个Web的自动化测试工具,可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。Selenium 自己不带浏览器,不支持浏览器的功能,它需要与第三方浏览器结合在一起才能使用。但是我们有时候需要让它内嵌在代码中运行,所以我们可以用一个叫 PhantomJS 的工具代替真实的浏览器。Selenium 库里有个叫 WebDriver 的 API。WebDriver 有点儿像可以加载网站的浏览器,但是它也可以像 BeautifulSoup 或者其他 Selector 对象一样用来查找页面元素,与页面上的元素进行交互 (发送文本、点击等),以及执行其他动作来运行网络爬虫。
PhantomJS 是一个基于Webkit的“无界面”(headless)浏览器,它会把网站加载到内存并执行页面上的 JavaScript,因为不会展示图形界面,所以运行起来比完整的浏览器要高效。
如果我们把 Selenium 和 PhantomJS 结合在一起,就可以运行一个非常强大的网络爬虫了,这个爬虫可以处理 JavaScrip、Cookie、headers,以及任何我们真实用户需要做的事情。
1.selenium控制鼠标实现,速度太机械化,成功率比较低
2.计算缺口的偏移量(推荐博客:http://blog.csdn.net/paololiu/article/details/52514504?%3E)
3.“极验”滑动验证码需要具体网站具体分析,一般牵扯算法乃至深度学习相关知识
分布式只是提高爬虫功能和效率的一个环节而已,它从来不是爬虫的本质东西。
爬虫的本质是网络请求和数据处理,如何稳定地访问网页拿到数据,如何精准地提取出高质量的数据才是核心问题。
分布式爬虫只有当爬虫任务量很大的时候才会凸显优势,一般情况下也确实不必动用这个大杀器,所以要明确你的目标是什么
1)ip
2)带宽
3)cpu
4)io
5)大量任务
传统定义:分布式存储系统是大量PC服务器通过Internet互联,对外提供一个整体的服务。
分布式存储系统具有以下的几个特性:
可扩展 :分布式存储系统可以扩展到几百台甚至几千台这样的一个集群规模,系统的整体性能线性增长。
低成本 :分布式存储系统的自动容错、自动负载均衡的特性,允许分布式存储系统可以构建在低成本的服务器上。另外,线性的扩展能力也使得增加、减少服务器的成本低,实现分布式存储系统的自动运维。
高性能 :无论是针对单台服务器,还是针对整个分布式的存储集群,都要求分布式存储系统具备高性能。
易用 :分布式存储系统需要对外提供方便易用的接口,另外,也需要具备完善的监控、运维工具,并且可以方便的与其他的系统进行集成。
分布式存储系统的挑战主要在于数据和状态信息的持久化,要求在自动迁移、自动容错和并发读写的过程中,保证数据的一致性。分布式存储所涉及到的技术主要来自于两个领域:分布式系统以及数据库,如下所示:
数据分布:如何将数据均匀的分布到整个分布式存储集群中的各台服务器?如何从分布式存储集群中读取数据?
一致性:如何将数据的多个副本复制到多台服务器,即使在异常情况下,也能保证不同副本之间的数据一致性。
容错:如何可以快速检测到服务器故障,并自动的将在故障服务器上的数据进行迁移
负载均衡:新增的服务器如何在集群中保障负载均衡?数据迁移过程中如何保障不影响现有的服务。
事务与并发控制:如何实现分布式事务。
易用性:如何设计对外接口,使得设计的系统易于使用
压缩/加压缩:如何根据数据的特点设计合理的压缩/解压缩算法?如何平衡压缩/解压缩算法带来的空间和CPU计算资源?
三种分布式爬虫策略:
1.Slaver端从Master端拿任务(Request/url/ID)进行数据抓取,在抓取数据的同时也生成新任务,并将任务抛给Master。Master端只有一个Redis数据库,负责对Slaver提交的任务进行去重、加入待爬队列。
优点: scrapy-redis默认使用的就是这种策略,我们实现起来很简单,因为任务调度等工作scrapy-redis都已经帮我们做好了,我们只需要继承RedisSpider、指定redis_key就行了。
缺点: scrapy-redis调度的任务是Request对象,里面信息量比较大(不仅包含url,还有callback函数、headers等信息),导致的结果就是会降低爬虫速度、而且会占用Redis大量的存储空间。当然我们可以重写方法实现调度url或者用户ID。
2.Master端跑一个程序去生成任务(Request/url/ID)。Master端负责的是生产任务,并把任务去重、加入到待爬队列。Slaver只管从Master端拿任务去爬。
优点: 将生成任务和抓取数据分开,分工明确,减少了Master和Slaver之间的数据交流;Master端生成任务还有一个好处就是:可以很方便地重写判重策略(当数据量大时优化判重的性能和速度还是很重要的)。
缺点: 像QQ或者新浪微博这种网站,发送一个请求,返回的内容里面可能包含几十个待爬的用户ID,即几十个新爬虫任务。但有些网站一个请求只能得到一两个新任务,并且返回的内容里也包含爬虫要抓取的目标信息,如果将生成任务和抓取任务分开反而会降低爬虫抓取效率。毕竟带宽也是爬虫的一个瓶颈问题,我们要秉着发送尽量少的请求为原则,同时也是为了减轻网站服务器的压力,要做一只有道德的Crawler。所以,视情况而定。
3.Master中只有一个集合,它只有查询的作用。Slaver在遇到新任务时询问Master此任务是否已爬,如果未爬则加入Slaver自己的待爬队列中,Master把此任务记为已爬。它和策略一比较像,但明显比策略一简单。策略一的简单是因为有scrapy-redis实现了scheduler中间件,它并不适用于非scrapy框架的爬虫。
优点: 实现简单,非scrapy框架的爬虫也适用。Master端压力比较小,Master与Slaver的数据交流也不大。
缺点: “健壮性”不够,需要另外定时保存待爬队列以实现“断点续爬”功能。各Slaver的待爬任务不通用。
如果把Slaver比作工人,把Master比作工头。策略一就是工人遇到新任务都上报给工头,需要干活的时候就去工头那里领任务;策略二就是工头去找新任务,工人只管从工头那里领任务干活;策略三就是工人遇到新任务时询问工头此任务是否有人做了,没有的话工人就将此任务加到自己的“行程表”。
1.5、6万条数据相对来说数据量比较小,线程数量不做强制要求(做除法得一个合理值即可)
2.多线程使用代理,应保证不在同时一刻使用一个代理IP
使用MD5数字签名
每次下载网页时,把服务器返回的数据流ResponseStream先放在内存缓冲区,然后对ResponseStream生成MD5数字签名S1,下次下载同样生成签名S2,比较S2和S1,如果相同,则页面没有跟新,否则网页就有跟新。
增量爬取即保存上一次状态,本次抓取时与上次比对,如果不在上次的状态中,便视为增量,保存下来。对于scrapy来说,上一次的状态是抓取的特征数据和上次爬取的 request队列(url列表),request队列可以通过request队列可以通过scrapy.core.scheduler的pending_requests成员得到,在爬虫启动时导入上次爬取的特征数据,并且用上次request队列的数据作为start url进行爬取,不在上一次状态中的数据便保存。
选用BloomFilter原因:对爬虫爬取数据的保存有多种形式,可以是数据库,可以是磁盘文件等,不管是数据库,还是磁盘文件,进行扫描和存储都有很大的时间和空间上的开销,为了从时间和空间上提升性能,故选用BloomFilter作为上一次爬取数据的保存。保存的特征数据可以是数据的某几项,即监控这几项数据,一旦这几项数据有变化,便视为增量持久化下来,根据增量的规则可以对保存的状态数据进行约束。比如:可以选网页更新的时间,索引次数或是网页的实际内容,cookie的更新等...
使用生成器读取文件。每次读取65536行,一共进行1500次,当读取不到内容时关闭文件。每次读取,最终要得到100个频数最高的词。每500次,进行一次合并和统计,得到最多50000个词,对这50000个词提取其中频数最高的100个词。最终对最多300个筛选出来的词,进行合并和统计,提取最终频数最高的100个词。
筛选出100个高频词的步骤:
1.统计每个词出现的次数。维护一个Key为词,Value为该词出现次数的hash表。每次读取一个词,如果该字串不在hash表中,那么加入该词,并且将Value值设为1;如果该字串在hash表中,那么将该字串的计数加一即可。
2.根据每个词的引用次数进行排序。冒泡、快排等等都可以
用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平准长度)。然后是找出出现最频繁的前10个词,可以用堆来实现,时间复杂度是O(n*lg10)。所以总的时间复杂度,是O(n*le)与O(n*lg10)中较大的哪一个。
http://www.mamicode.com/info-detail-1037262.html
算法思想:
先做hash,然后求模映射为小文件,求出每个小文件中重复次数最多的一个,并记录重复次数。
然后找出上一步求出的数据中重复次数最多的一个就是所求https://www.cnblogs.com/lianghe01/p/4391804.html
http://blog.csdn.net/u010601183/article/details/56481868
思路和上题一样。不同之处在于,统计完所有整数的引用之后,找出所有引用为1的整数。
unsigned int 的取值范围是0到2^32-1。我们可以申请连续的2^32/8=512M的内存,用每一个bit对应一个unsigned int数字。首先将512M内存都初始化为0,然后每处理一个数字就将其对应的bit设置为1。当需要查询时,直接找到对应bit,看其值是0还是1即可
线性回归使用的是线性方程。通过样本X获得预测值Y,然后通过最小化所有样本预测值Y和真实值Y`的误差,获得模型参数。
逻辑斯蒂回归使用的是。逻辑斯蒂回归用于二分类问题,所以它不是通过样本值得到预测值,而是计算两个结果各自的概率
区别:1.使用的数学模型不同;2.应用场景不同
聚类分析:将大量的对象,根据某些特征进行分类的行为
常见聚类算法:k-means,层次聚类算法,SOM聚类算法,FCM聚类算法
k-means聚类算法:算法的核心-“距离”
步骤:1.随机设置k个特征空间内的点,作为聚类中心
2.对其他每个点,分别计算到各个聚类中心的距离。根据距离,选择最近的聚类中心作为这个未知点的标记类别。
3.每次将一个未知点标记类别后,重新计算所属类别的聚类中心。
4.重复以上过程,直到准则函数收敛。准则函数计算的是 空间内所有点到各聚类中心的距离之和。
数列本身就是有序的。
numpy对数组的前n项进行排序:
1.ndarray.sort(axis=, kind=, order= )
参数说明:axis:沿着什么方向进行排序(0:按列,1:按行)
kind:使用什么排序方式(可选的有quicksort,mergesort,heapsort)
2.numpy.sort(a, axis=, kind=, order=)
第一个参数是要排序的数组,其他参数和函数1相同
3.numpy.argsort(a, axis=, kind=, order)
参数和函数2完全相同。
区别在于:函数2直接修改原始数组
numpy.array支持更多的索引方式
numpy.array类型支持[n,m]形式的检索方式。
例如a为array([1,2,3],
[4,5,6],
[7,8,9])。a[:,1]输出为[2,5,8]。
Zipline是一个交易算法库,该系统是对现场交易系统如何运转的一个近似,可以对历史数据进行投资算法的回溯检验。Zipline目前作为Quantopian的回溯检验引擎。
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口,中文文档:
https://elasticsearch.cn/book/elasticsearch_definitive_guide_2.x/
hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能,可以将sql语句转换为MapReduce任务进行运行。
官方文档:https://hive.apache.org/
中文文档:
http://blog.csdn.net/u011812294/article/details/53610210?utm_source=itdadao&utm_medium=referral
A=[{“dt”:”2016-06-06”,”a”:100},{“dt”:”2016-06-07”,”b”:200}]
B=[{“dt”:”2016-06-06”,”b”:200},{“dt”:”2016-06-07”,”a”:400}]
请使用pandas进行数据处理并得到如下结果:
C=[{“dt”:”2016-06-06”,”a”:100,”b”:200},
{“dt”:”2016-06-07”,”a”:400,”b”:200}
import pandas as pd
A = [{'dt': '2016-06-06', 'a': 100}, {'dt': '2016-06-07', 'b': 200}]
B = [{"dt": '2016-06-06', 'b': 200}, {'dt': '2016-06-07', 'a': 400}]
dfa = pd.DataFrame(A)
dfb = pd.DataFrame(B)
dfa.update(dfb)
dfa.to_dict(orient='index')
res = list()
for i in dfa.to_dict(orient='index').values():
res.append(i)
print res
第八章 算法与数据结构
数据结构是以某种形式将数据组织在一起的集合,不仅存储数据, 还支持访问和处理数据的操作.
基础的数据结构有:线性表(数组,链表),栈与队列,树与二叉树,图等
冒泡排序的思想: 每次比较两个相邻的元素, 如果他们的顺序错误就把他们交换位置。
def bubble_improve(l):
print l
flag = 1
for index in range(len(l) - 1, 0 , -1):
if flag:
flag = 0
for two_index in range(index):
if l[two_index] > l[two_index + 1]:
l[two_index], l[two_index + 1] = l[two_index + 1], l[two_index]
flag = 1
else:
break
print l
l = [10, 20, 40, 50, 30, 60]
bubble_improve(l)
快速排序使用分治法策略来把一个序列分为两个子序列。
步骤:
从数列中挑出一个元素,称为 "基准"(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分割结束之后,该基准就处于数列的中间位置。这个称为分割(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
def quickSort(alist,first,last):
if first
splitpoint = findpos(alist,first,last)
quickSort(alist,first,splitpoint-1)
quickSort(alist,splitpoint+1,last)
class TreeNode(object):
def __init__(self, left=None, right=None, data=None):
self.data = data
self.left = left
self.right = right
def preorder(root): #前序遍历
if root is None:
return
else:
print root.data
preorder(root.left)
preorder(root.right)
def inorder(root): #中序遍历
if root is None:
return
else:
inorder(root.left)
print root.data
inorder(root.right)
def postorder(root): # 后序遍历
if root is None:
return
else :
postorder(root.left)
postorder(root.right)
print root.data
计算一个二叉树的最大距离有两个情况:
情况A: 路径经过左子树的最深节点,通过根节点,再到右子树的最深节点。
情况B: 路径不穿过根节点,而是左子树或右子树的最大距离路径,取其大者。
只需要计算这两个情况的路径距离,并取其大者,就是该二叉树的最大距离。
1) 二叉树的深度优先遍历的非递归的通用做法是采用栈,广度优先遍历的非递归的通用做法是采用队列。
2) 深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。要特别注意的是,二叉树的深度优先遍历比较特殊,可以细分为先序遍历、中序遍历、后序遍历。具体说明如下:
• 先序遍历:对任一子树,先访问根,然后遍历其左子树,最后遍历其右子树。
• 中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树。
• 后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。
广度优先遍历:又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。
3)深度优先搜素算法:不全部保留结点,占用空间少;有回溯操作(即有入栈、出栈操作),运行速度慢。
广度优先搜索算法:保留全部结点,占用空间大; 无回溯操作(即无入栈、出栈操作),运行速度快。
通常 深度优先搜索法不全部保留结点,扩展完的结点从数据库中弹出删去,这样,一般在数据库中存储的结点数就是深度值,因此它占用空间较少。所以,当搜索树的结点较多,用其它方法易产生内存溢出时,深度优先搜索不失为一种有效的求解方法。
广度优先搜索算法,一般需存储产生的所有结点,占用的存储空间要比深度优先搜索大得多,因此,程序设计中,必须考虑溢出和节省内存空间的问题。但广度优先搜索法一般无回溯操作,即入栈和出栈的操作,所以运行速度比深度优先搜索要快些
第一,因为WSGI相对来说性能不照,测试还行,要是正是发布,百来个人一点
就歇菜了;
第二,虽然有了uWSGi这个web服务器,但是加上Nginx就会让服务器更安全、性能更有保障,还能作为电子邮件代理服务器。
Django对静态资源的处理nginx不能代理(可能吧)。总之这种事不该让Django做,因为nginx在处理静态资源上能力更强,对于静态资源,就让nginx处理吧。
收集静态文件
Django有一个工具可以将应用里用到的所有静态文件收集起来,方便nginx解析。具体:
在setting.py中设置STATIC_ROOT = os.path.join(BASE_DIR, '../collectedstatic')
这样收集的静态文件就都放进上面的目录里了。如何运行这个工具?python3 manager.py collectstatic