数字类型:
int整型、long长整型 (python 2)、float浮点、complex复数、以及bool布尔值(0和1)
bool类型:
True和False,其分别对应二进制中的0和1;
Flase的值有:None、空(即 ""、[]、{}、()
)、0
str类型:
可以使用单引号 " 或者双引号 “” 来创建字符串
list列表, dict字典, set集合, tuple元组
+ - * / % ** //
= += -= *= /= %= **= II=
== != > < >= <=
and or not
in not in
可变类型 (mutable):列表、字典
不可变类型 (unmutable):数字、字符串、元组
这里的可变不可变,是指内存中的那块内容 (value) 是否可以被改变。
对不可变类型的变量重新赋值,实际上是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象。
不可变类型可用作字典的key, 包括:int, float, str, bool, tuple
普通参数:在函数调用时必须传递的参数。
默认参数:即参数含有默认值,在调用函数时若没有传递参数则使用默认值,默认参数必
须在普通参数的右侧。
*args:元组参数。参数格式化存储在一个元组中,长度没有限制,必须位于普通参数和默
认参数之后。
**kwargs:字典参数。参数格式化存储在一个字典中,必须位于所有参数的最后面。
def fun(name, age=l, *args, **kwargs):
print('Hello', name, age,'年') # Hello World 1 年
print(args) # ('I', 'love', 'it')
for i in args:
print(i)
print(kwargs) # {'my': 'jack', 'like': 'girl'}
for m in kwargs:
print(m, kwargs[m])
fun('World', 1, 'I', 'love', 'it', my = 'Rose', like = 'girl')
匿名函数,简化了函数定义的书写形式。使代码更为简洁,但是使用函数的定义方式更为直观,易理解。
is:同一性运算符,比较两个引用是否指向了同一个对象;判断地址。
==:比较操作符,用来比较判断两个对象的 value(值)是否相等;判断值。
深拷贝是对于一个对象所有层次的拷贝(递归)。
浅拷贝是对于一个对象的顶层(表面一层)拷贝,通俗的理解是:拷贝了引用,并没有拷贝内容。
元类就是用来创建类的,元类就是类的类
python是一门编程语言,一种支持面向对象的解释型脚本语言。
它的优点是开源、跨平台、语法简洁(C代码量的10%)、13万的第三方库、开发工作量小、维护简单、是最高产的程序设计语言。
应用于数据采集、web开发、自动化运维、自动化测试、数据分析、人工智能、3D开发等方面。
定义:如果走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子。
鸭子类型是编程语言中动态类型语言中的一种设计风格。
python不关注类型本身,而是关注行为。你调用sum ()函数加任何类型时,参数自己调用自己类内部对应的方法。虽然类型各有不同,但是都有自己对应的方法。所以,能够进行加法运算的,自然会有自己的方法。你只用考虑它有没有这样的行为,不用考虑它的具体类型是什么。
在python中,元素可以在循环的过程中按照某种算法推算出来,而不必创建完整的list,从而节省大量的空间。这种一边循环一边计算的机制,称为生成器 (generator)。
迭代是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
迭代器只能往前不会后退。
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。
装饰器就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名(或类名)的情况下,给函数増加新的功能。
通常模块为一个文件,直接使用import来导入;
包总是一个目录,可以使用import导入包,或者from + import来导入包中的部分模块。
__init__.py
的第一个作用就是package的标识,如果没有该文件,该目录就不会认为是package;
__init_.py
的另外一个作用就是定义package中的 __all__
,用来模糊导入:__all__ = ["Pack1Class", "Pack1Class1"]
python使用LEGB的顺序来查找一个符号对应的对象
locals -> enclosing function -> globals -> builtins
locals:当前所在命名空间(如函数、模块),函数的参数也属于命名空间内的变量
enclosing:外部嵌套函数的命名空间(闭包中常见)
globals:全局变量,函数定义所在模块的命名空间
buildins:内建模块的命名空间。
__name__
当文件是被调用时,__name__
的值为模块名;
当文件被执行时,__name__
的值为 __main__
。
xx:公有变量
__xxx(双下划线):private,不可直接访问
_xxx(单下划线):protected,受保护的(python 中不太严格)
__xx__
:python内部的名字
xx_(单后置下划线):用于避免与python关键词的冲突
__init__()
方法和 __new__()
方法"new"方法在python中是真正的构造方法,通过这个方法可以产生一个"cls"对应的实例对象,所以说"new"方法一定要有返回。
对于"init"方法,是一个初始化的方法,"self"代表由类产生出来的实例对象,"init"将对这个对象进行相应的初始化操作。
好的代码要达到可维护、可复用、可扩展、灵活性好四个特点。
面向对象编程 (Object Oriented Programming) 就是把一类具有相同属性和动作的实体抽象成为计算机里面的类,处理业务的流程就是对象之间的信息传递。程序中一切皆对象。对象是具有状态和行为的一切实体,类是创建对象的模板,类是对象的抽象。
封装:就是把对象的属性和行为(数据)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节,就是把不想告诉或者不该告诉别人的东西隐藏起来,把可以告诉别人的公开,别人只能用我提供的功能实现需求,而不知道是如何实现的,以増加安全性。
继承:是面向对象最显著的一个特性,继承是从已有的类中派生出新的类称为子类,子类继承父类的数据属性和行为,并能根据自己的需求扩展出新的行为,提高了代码的复用性。
多态:指允许不同的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)。封装和继承几乎都是为多态而准备的,在执行期间判断引用对象的实际类型,根据其实际类型调用其相应的方法。
Guido的关键点之一是:代码更多是用来读而不是写。编码规范旨在改善python代码的可读性。风格指南强调一致性。项目、模块或函数保持一致都很重要。
促进团队合作
减少bug处理
提高可读性,降低维护成本
有助于代码审查
养成习惯,有助于程序员自身的成长
每级缩进用4个空格
限制所有行的最大行宽为79字符
两行空行分割顶层函数和类的定义
类的方法定义用单个空行分割
命名规范
python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略
进程是系统进行资源分配和调度的一个独立单位。一个程序在一个数据集中的一次动态执行过程,可以简单理解为“正在执行的程序”。
进程一般由程序、数据集、进程控制块三部分组成。程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。
进程的局限是创建、撤销和切换的开销比较大。
线程是进程的一个实体,也叫轻量级进程,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
协程,又称微线程,纤程。
协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
协程是一个线程执行,但执行有点像多线程。协程执行过程中,在协程内部可中断,然后转而执行别的协程,在适当的时候再返回来接着执行。协程之间不是调用者与被调用者的关系,而是彼此对称、平等的,通过相互协作共同完成任务。
优势:
协程的执行效率极高。
因为协程切换不是线程切换,而是由程序自身控制。因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
不需要多线程的锁机制。
因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
python对用在generator中的yield可以一定程度上实现协程。通过yield方式转移执行权。
进程:能够完成多任务,比如在一台电脑上能够同时运行多个QQ
线程:能够完成多任务,比如一个QQ中的多个聊天窗口
一个程序至少有一个进程,一个进程至少有一个线程。
线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
线程不能够独立执行,必须依存在进程中。
线程属于内核级别的,即由操作系统控制调度。
协程,应用程序级别(而非操作系统)控制切换,以此来提升效率。
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进
程正相反。
线程不安全,没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结
果不可预期。
可以通过线程同步来进行解决
系统调用t1,然后获取到num的值为0。此时上一把锁,即不允许其他线程操作num
对num的值进行+1
解锁,此时num的值为1,其他的线程就可以使用num了,而且是num的值不是0而是1
同理其他线程在对num进行修改时,都要先上锁,处理完后再解锁,在上锁的整个过程中不允许其他线程访问,就保证了数据的正确性
当多个线程同时修改某一个共享数据的时候,需要进行同步控制。线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。
互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
系统中的资源可以分为两类:
可剥夺资源。是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺。CPU和主存均属于可剥夺性资源。
不可剥夺资源。当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。
产生死锁的原因之一是竞争不可剥夺资源。例如:系统中只有一台打印机,假定P1已占用了打印机,若P2继续要求打印,打印将阻塞。
产生死锁的原因之二指的是竞争临时资源(包括硬件中断、信号、消息、缓冲区内的消息等)。通常消息通信顺序进行不当,则会产生死锁。
若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态。因为这两个进程再向前推进,便可能发生死锁。
例如,当P1运行到P1: Request (R2) 时,将因R2已被P2占用而阻塞;当P2运行到P2: Request (R1) 时,也将因R1已被P1占用而阻塞,于是发生进程死锁。
互斥条件 (Mutual exclusion):资源不能被共享,只能由一个进程使用。
请求与保持条件 (Hold and wait):进程已获得了一些资源,但因请求其它资源被阻塞时,对已获得的资源保持不放。
不可抢占条件 (No pre-emption):有些系统资源是不可抢占的,当某个进程已获得这种资源后,系统不能强行收回,只能由进程使用完时自己释放。
循环等待条件 (Circular wait):若干个进程形成环形链,每个都占用对方申请的下一个资源。
忽略该问题。例如鸵鸟算法。
检测死锁并且恢复。
仔细地对资源进行动态分配,以避免死锁。
通过破除死锁四个必要条件之一,来防止死锁产生。
该算法可以应用在极少发生死锁的的情况下。为什么叫鸵鸟算法呢,因为传说中鸵鸟看到危险就把头埋在地底下,可能鸵鸟觉得看不到危险也就没危险了吧。跟掩耳盗铃有点像。
所谓银行家算法,是指在分配资源之前先看清楚,资源分配后是否会导致系统死锁。如果会死锁,则不分配,否则就分配。
按照银行家算法的思想,当进程请求资源时,系统将按如下原则分配系统资源:
当一个进程对资源的最大需求量不超过系统中的资源数时可以接纳该进程。
进程可以分期请求资源,但请求的总数不能超过最大需求量。
当系统现有的资源不能满足进程所需资源数时,对进程的请求可以推迟分配,但总能使进程在有限的时间里得到资源。
当系统现有的资源能满足进程所需资源数时,必须测试系统现存的资源能否满足该进程所需的最大资源数,若能满足则按当前的申请量分配资源,否则也推迟分配。
对待死锁的策略主要有:
死锁预防:破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。
死锁避免:避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。
死锁检测:死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。
死锁解除:这是与死锁检测结合使用的,它使用的方式就是剥夺。即将某进程所拥有的资源强行收回,分配给其他的进程。
死锁的预防是通过破坏产生条件来阻止死锁的产生,但这种方法破坏了系统的并行性和并发性。
死锁产生的前三个条件是死锁产生的必要条件,也就是说要产生死锁必须具备的条件,而不是存在这三个条件就一定产生死锁,那么只要在逻辑上回避了第四个条件就可以避免死锁。
避免死锁采用的是允许前三个条件存在,但通过合理的资源分配算法来确保永远不会形成环形等待的封闭进程链,从而避免死锁。该方法支持多个进程的并行执行,为了避免死锁,系统动态的确定是否分配一个资源给请求的进程。方法如下:
如果一个进程的当前请求的资源会导致死锁,系统拒绝启动该进程。
如果一个资源的分配会导致下一步的死锁,系统就拒绝本次的分配。
显然要避免死锁,必须事先知道系统拥有的资源数量及其属性。
同步就是发起一个请求,直到请求返回结果之后,才进行下一步操作。
简单来说,同步就是必须一件事一件事的做,等前一件做完了,才能做下一件事。
当一个异步操作发出后,调用者在没有得到结果之前,可以继续执行后续操作。这就是异步。
请求发出后,是否需要等待请求结果,才能继续执行其他操作。
在调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会被唤醒执行后续的操作。
在结果没有返回之前,该调用不会阻塞住当前线程。
任务:用水壶烧水,烧开后就去泡咖啡。
同步阻塞:用水壶烧水,并且站在煤气灶旁边,啥事不干,两眼直勾勾的盯着水壶,等水烧开。烧开后就去泡咖啡。
同步非阻塞:用水壶煮水,不过此时不再傻傻地站在那里看水开没开,而是去玩局游戏,每当自己死了,就过来看看水开了没有。如果水开了就去泡咖啡。
异步阻塞:用水壶烧水,仍然站在煤气灶旁边,不过此时不两眼直勾勾的盯着壶了,而是听响,因为水开时会用响声通知你。
异步非阻塞:用水壶烧水,然后去玩局游戏,等听到响声后,再去泡咖啡。
python的tornado框架就是一种异步非阻塞框架。
并发指应用能够交替执行不同的任务。
当有多个线程在一个CPU运行,操作系统只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,不同时间段,快速切换不同的线程代码运行。
例子:吃一口饭暍一口水
并行指应用能够同时执行不同的任务。
系统有多个CPU,执行多个线程,可以一个CPU执行一个线程,线程之间互不抢占CPU资源,可以同时进行。
例子:边听歌边上网
一个是交替执行,一个是同时执行.
不同计算机之间进行数据传递,要有一种大家都认可遵守的协议,这个称为网络通信协议
早期的计算机网络,都是由各厂商自己规定一套协议,如IBM、Apple、Microsoft都有各自的网络协议,互不兼容。为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议。为了实现这个目标,就需要一个通用的协议标准——互联网协议簇。
TCP/IP协议是互联网通讯协议的工业标准。其中包含了上百种协议标准,但是最重要的两个协议是 TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议。
TCP/IP 协议分为四层,分别为应用层、传输层、网际层和网络接口层。
国际标准化组织 (ISO) 和国际电报电话咨询委员会 (CCITT) 联合制定的开放系统互连参考模型,为开放式互连信息系统提供了一种功能结构的框架。
它从低到高分别是:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
IP地址是用来在网络中标记一台电脑的一串数字
—台拥有IP地址的主机可以提供许多服务,比如HTTP(万维网服务)\FTP(文件传输)、SMTP(电子邮件)等
这些服务完全可以通过1个IP地址来实现。
IP地址与网络服务的关系是一对多的关系,通常是用"IP地址+端口号"来区分不同的网络服务的。
socket(简称套接字)是进程间通信的一种方式。
它与其他进程间通信的不同是它能实现不同主机间的进程间通信。
网络服务大多都是基于socket来完成通信的。
TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
TCP首部开销20字节;UDP的首部开销小,只有8个字节
TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
首先由Client发出连接请求,SYN=1 ACK=0,并进入SYN_SENT状态,等待服务器确认;
然后Server进行回复确认,即 SYN=1 ACK=1 seq=y,服务器进入SYN_RECV状态;
再然后Client再进行一次确认,但不用SYN了,发送确认包ACK=1,客户端和服务器进入ESTABLISHED(TCP 连接成功)状态。
ACK : TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1。
SYN (SYNchronization) :在连接建立时用来同步序号,TCP规定SYN=1时不能携带数据。
FIN (finis) :即完,终结的意思,用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。
TCP连接是全双工的,因此每个方向都必须单独进行关闭。当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN 只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
TCP客户端发送一个FIN ,用来关闭客户到服务器的数据传送。
服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。
服务器关闭客户端的连接,发送一个FIN给客户端。
客户端发回ACK报文确认,并将确认序号设置为收到序号加1。
FIN (finis) :即完,终结的意思,用来释放一个连接。当FIN=1时,表明此报文段的
发送方的数据已经发送完毕,并要求释放连接。
socket创建一个套接字
bind绑定ip和port
listen使套接字变为可以被动链接
accept等待客户端的链接
recv/send接收发送数据
调用close关闭套接字
又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法,是计算机科学的一个概念。
正则表达式使用单个字符串来描述、匹配一系列某个句法规则字符串。
通常被用来检索、替换那些匹配某个模式的文本。
符号 | 匹配内容 |
---|---|
. | 匹配任意字符(除了\n) |
[] | 匹配列举的字符 |
\d | 匹配数字 |
\w | 匹配单词字符 |
\W | 匹配非单词字符 |
\s | 空白 |
符号 | 匹配内容 |
---|---|
* | 匹配前一个字符0次或多次 |
+ | 匹配前一个字符1次或多次 |
? | 匹配前一个字符0次或1次 |
{m,n} | 匹配前一个字符m到n次 |
符号 | 匹配内容 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串末尾 |
# 引用分组 num 匹配到的字符串
ret = re.match(r"<(\w+)><(\w+)>.+\2>\l>", "www.baidu.com ")
ret.group()
# 命名分组,引用别名为 name 的分组匹配到的字符串
ret = re.match("<(?P\w*)><(?P\w*)>.*(?P=name2)>(?P=namel)>" , "www.baidu.com ")
ret.group()
match:从字符串的头部或者指定位置开始查找一次匹配,只要找到了一个匹配的结果就返回
search:查找字符串的任何位置,只匹配一次,只要找到了一个匹配的结果就返回
贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配 (*)
非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配 (?)
主键:能唯一标识一条记录的字段就是主键。
比如一条记录包括身份证号,姓名,年龄,身份证号是唯一确定这个人的,它就是主键
外键:外键是与另一张表的关联,能确定另一个表中的记录。
约束:一种限制,通过对表的行或列的数据做出限制,来确保数据的完整性、唯一性
比如:在订单记录中,指定的客户编码,必须是客户表中存在的客户商品编号,必须是商品表中存在的商品。
比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对“地址”中某一部分操作的时候将非常方便。
一个表中只能保存一种数据。
订单信息表,要将订单编号和商品编号作为数据库表的联合主键,表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。所以应把订单信息表拆分为商品表和订单表。
不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。
数据库操作,表操作,数据操作
当一个业务逻辑需要一系列操作完成时,如果其中某操作出错,则所有操作都退回。使用事务可以完成退回的功能,保证业务逻辑的正确性。
原子性 (Atomicity) :事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行
一致性 (Consistency) :几个并行执行的事务,其执行结果必须与按某一顺序序串行执行的结果相一致
隔离性 (Isolation) :事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的
持久性 (Durability) :对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障
共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
适用于多写的场景。
传统的关系型数据库里边行锁,表锁,读锁,写锁等就用到了这种锁机制,都是在做操作之前先上锁。
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。
用版本号机制和CAS算法实现。
适用于多读的应用类型,这样可以提高吞吐量。
在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。
当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
compare and swap(比较与交换),是—种有名的无锁算法。
使用锁的情况下实现多线程之间的变量同步。
在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)
CAS算法涉及到三个操作数:
需要读写的内存值V
进行比较的值A
拟写入的新值B
当且仅当V的值等于A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。
ABA问题
如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的"ABA"问题。
循环时间长开销大
自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。
只能保证一个共享变量的原子操作
CAS只对单个共享变量有效,当操作涉及跨多个共享变量时CAS无效。
GIL是全局解释器锁,保证同一时刻只有一个线程可以使用CPU,让我们的多线程没办法真正实现并行。
在一个进程中只有一个GIL锁,那个线程拿到GIL就可以使用CPU。
多个进程有多个GIL锁。
GIL锁:保证同一时刻只有一个线程能使用到CPU。
互斥锁:多线程时,保证修改共享数据时有序的修改,不会产生数据修改混乱。
关系型数据库:SQLite、Oracle、MySQL
非关系型数据库:MongoDB、redis
ISAM对数据库查询进行了优化,ISAM执行读取操作的速度很快,而且不占用大量的内存和存储资源。ISAM的两个主要不足之处在于,它不支持事务处理,也不能够容错。如果你的硬盘崩溃了,那么数据文件就无法恢复了。如果你正在把ISAM用在关键任务应用程序里,那就必须经常备份你所有的实时数据,通过其复制特性,MySQL能够支持这样的备份应用程序。
MylSAM是ISAM扩展格式和缺省的数据库引擎。提供索引和字段管理的功能,使用表格锁定的机制,来优化多个并发的读写操作。MylSAM还有修复数据库文件的MylSAMCHK工具和用来恢复浪费空间的MylSAM PACK工具。MYISAM强调了快速读取操作。所以,大多数虚拟主机提供商INTERNET平台提供商只允许使用MYISAM格式。MylSAM格式的一个重要缺陷就是不能在表损坏后恢复数据。
插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MylSAM能高效率处理。如果应用的完整性、并发性要求比较低,也可以使用。
InnoDB提供了技术就是MYSQL+API. ISAM和MylSAM数据库引擎不支持事务处理 (transaction process) 也不支持外来键。InnoDB要比ISAM和MylSAM引擎慢很多,但是InnoDB包括了对事务处理和夕卜键的支持,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),S瞇择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交 (commit) 和回滚 (rollback)。
MEMORY使用存储在内存中的内容来创建表,而且数据全部放在内存中。每个基于MEMORY存储引擎的表实际对应一个磁盘文件。该文件中只存储表的结构。而其数据文件,都是存储在内存中,这样有利于数据的快速处理,提高整个表的效率。服务器需要有足够的内存来维持MEMORY存储引擎的表的使用。如果重启或者关机,所有数据都会消失。因此,基于MEMORY的表的生命周期很短,一般是一次性的。所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。
Merge:允许MySQL DBA或开发人员将一系列等同的MylSAM表以逻辑方式组合在一起,并作为1个对象引用它们。对于诸如数据仓储等VLDB环境十分适合。
触发器:触发器是一个特殊的存储过程,它是MySQL在insert、update、delete的时候自动执行的代码块。
函数:MySQL中提供了许多内置函数,还可以自定义函数。(实现程序员需要SQL逻辑处理。)
视图:视图是由查询结果形成的一张以表,是表通过某种运算得到的一个投影。
存储过程:把一段代码封装起来,当圈丸行这一段代码的时候,可以通过调用该存储过程来实现。(经过第一次编译后再次调用不需要再次编译,比一个个执行SQL语句效率高。)
索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。
MySQL索引的类型:
普通索引:这是最基本的索引,它没有任何限制
唯一索引:索引列的值必须唯一,但允许有空值,如果是组合索引,则列值的组合必须唯一
全文索引:全文索引仅可用于MylSAM表,可以从CHAR、VARCHAR或TEXT列中作为CREATE TABLE语句的一部分被创建,或是随后使用ALTER TABLE或CREATEINDEX被添加(切记对于大容量的数据表,生成全文索弓I是一个非常消耗时间非常消耗硬盘空间的做法)
单列索引、多列索引:多个单列索弓I与单个多列索引的查询效果不同,因为执行查询时,MySQL只能使用一个索引,会从多个索弓I中选择一个限制最为严格的索引。组合索引(最左前缀):简单的理解就是只从最左面的开始组合(实在单列索引的基础上进一步压榨索引效率的一种方式)
MySQL在使用组合索引查询的时候需要遵循“最左前缀”规则
char:定长,char的存取数度相对快
varchar:不定长,存取速度相对慢
到底如何取舍可以根据一下几个方面考虑:
对于MylSAM表,尽量使用char,它的缺点就是占用磁盘空间;
对于InnoDB表,数据行内部存储格式对固定长度的数据行和可变长度的数据行不加区分,所以使用char类型不见得会比使用varchar类型好。事实上,因为char类型通常要比varchar类型占用更多的空间,所以从减少空间占用量和减少磁盘i/o的角度,使用varchar类型反而更有利。
存储很短的信息,比如门牌号码101,201……这样很短的信息应该用char,因为varchar还要占个byte用于存储信息长度,本来打算节约存储的现在得不偿失。
固定长度的。比如使用uuid作为主键,那用char应该更合适。因为他固定长度,varchar动态根据长度的特性就消失了,而且还要占个长度信息。
十分频繁改变的column。因为varchar每次存储都要有额外的计算,得到长度等工作,如果一个非常频繁改变的,那就要有很多的精力用于计算,而这些对于char来说是不需要的。
数据表设计优化,遵循三范式
SQL语句优化
索引优化
加缓存
读写分离
分区
分布式数据库(垂直切分)
水平切分
NoSQL,全名为Not Only SQL,指的是非关系型的数据库。
随着访问量的上升,网站的数据库性能出现了问题,于是NoSQL被设计出来。
高可扩展性,分布式计算,低成本,架构的灵活性,半结构化数据,没有复杂的关系。
没有标准化,有限的查询功能,最终一致是不直观的程序。
MongoDB将数据存储为一个文档,数据结构由键值 (key=>value) 对组成。
MongoDB文档类似于JSON对象,字段值可以包含其他文档、数组、文档数组。
安装管理MongoDB环境
完成数据库、集合的管理
数据的増加、修改、删除、查询
因为MongoDB设计就是轻量高性能,所以没有传统的锁和复杂的事务的回滚
键值对文档 -> 集合 -> 数据库
redis是 key-value 的数据,所以每个数据都是一个键值对。
键的类型是字符串。
值的类型有五种:
字符串string
哈希hash
列表list
集合set
有序集合zset
HTTP (HyperText Transfer Protocol) 是互联网上应用最为广泛的一种网络协议。是一个基于TCP/IP通信协议来传递数据,一个属于应用层的协议。
浏览器作为HTTP客户端通过URL向HTTP服务端,即Web服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。
主要特点:简单快速,无状态,支持B/S及C/S模式。
HTTP1.0定义了三种请求方法:GET, POST和HEAD方法。
HTTP1.1 新増了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
GET:请求指定的页面信息,并返回实体主体。
HEAD:类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头
POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
PUT:从客户端向服务器传送的数据取代指定的文档的内容。
DELETE:请求服务器删除指定的页面。
CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
OPTIONS:允许客户端查看服务器的性能。
TRACE:回显服务器收到的请求,主要用于测试或诊断。
GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,POST方法是把提交的数据放在HTTP包的body中。
GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方式提交的数据没有限制.
GET方式提交数据,会带来安全问题,如一个登录页面,通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码。
超文本传输协议 (HTTP - Hypertext Transfer Protocol) 是一种详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议。
HTTPS,是HTTP的安全版。HTTP下加入SSL层,对传输的内容进行加密,被广泛用于万维网上安全敏感的通讯,例如交易支付方面。而http是明文传输,不够安全。HTTP比HTTPS传输效率高
大部分开发语言中都有MVC框架
MVC框架的核心思想是:解耦
降低各功能模块之间的耦合性,方便变更,更容易重构代码,最大程度上实现代码的重用。
M:表示model,主要用于对数据库层的封装
V:表示view,用于向用户展示结果
C:controller,是核心,用于处理请求、获取数据、返回结果
Django是一款python的web开发框架。与MVC有所不同,属于MVT框架。
M表示model,负责与数据库交互
V表示view,是核心,负责接收请求、获取数据、返回结果
T表示template,负责呈现内容到浏览器
对象关系映射 (Object/Relation Mapping, ORM)
ORM以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。
主要任务是:
根据对象的类型生成表结构
将对象、列表的操作,转换为SQL语句
将SQL查询到的结果转换为对象、列表
创建Django项目
django-admin startproject test1
创建应用
python manage.py startapp myapp
在setting.py的installed_apps添加应用
在django项目配置文件同目录下的__init__.py
文件中加入以下代码,实现python3下 django 支持 MySQLdb:
import pymysql
pymysql.install_as_MySQLdb()
在setting.py的DATABASES数据库配置
创建数据库
模型M
数据库表的定义
定义模型类
生成迀移文件 python manage.py makemigrations
执行SQL语句生成数据表 python manage.py migrate
模版T
创建templates文件夹,模版的文件夹
按照应用,在templates目录下创建子目录
在setting.py TEMPLATES DIRS设置模版的路径
创建模版文件 (html)
视图V
定义视图函数,处理客户端的请求
查询数据
加载模版
定义要插入上下文的字典数据
把数据插入模版,把渲染内容返回
urlconf 的定义
当用户在浏览器中输入url时,浏览器向服务器发起请求
url经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表
url去除域名部分和参数部分保留虚拟路径部分
在路由中一条一条进行匹配,一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了
视图函数加载模版文件,根据客户端的请求查询相应的数据,并把数据插入模版文件
封装成相应对象返回给客户端
客户端浏览器接收到返回的数据,渲染后显示给用户
在models.py中创建模型类,继承自models.Model
导入 from django.db import models
通过models.Field创建字段类型的对象,赋值给属性
AutoField:—个根据实际ID自动增长的IntegerField
id = models.AutoField(primary_key=True)
BooleanField:true/false字段,此字段的默认表单控制是CheckboxInput
NullBooleanField:支持 null、true、false 三种值
CharField(max_length=字符长度):字符串,默认的表单样式是TextInput
TextField:大文本字段,一般超过4000使用,默认的表单控件是Textarea
IntegerField:整数
DecimalField(max_digits=None, decimal_places= None):使用python的Decimal实例表示的十进制浮点数
DecimalField.max_digits:位数总数
Decimal Field.decimal_places:小数点后的数字位数
FloatField:用Python的float实例来表示的浮点数
DateField(auto_now= False, auto_now_add=False):使用 Python 的datetime.date实例表示的日期
参数DateField.auto_now:每次保存对象时,自动设置该字段为当前时间,用
于”最后一次修改"的时间戳,它总是使用当前日期,默认为false
参数DateField.auto_now_add:当对象第一次被创建时自动设置当前时间,用
于创建的时间戳,它总是使用当前曰期,默认为false
auto_now_add, auto_now, and default这些设置是相互排斥的,他们之间的
任何组合将会发生错误的结果
TimeField:使用python的datetime.time实例表示的时间,参数同DateField
DateTimeField:使用python的datetime.datetime实例表示的曰期和时间,参数同DateField
FileField:—个上传文件的字段
ImageField:继承了 FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image
通过字段选项,可以实现对字段的约束,在字段对象时通过关键字参数指定
null:如果为True,Django将空值以NULL存储到数据库中,默认值是False
blank:如果为True,则该字段允许为空白,默认值是False
对比:null是数据库范畴的概念,blank是表单验证证范畴的
如果一个字段设置为blank=True,表单验证时允许输入一个空值。而blank=False,则该
项必需输入数据。
db_column:字段的名称,如果未指定,则使用属性的名称
db_index:若值为True,则在表中会为此字段创建索引
default:默认值
primary_key:若为True,则该字段会成为模型的主键字段
unique:如果为True,该字段在表中必须有唯一的值
verbose_name:字段的一种说明,在form中不会显示,和label是这个Field在form中会显示的文本提示
关系的类型包括
Foreign Key:—对多,将字段定义在多的端中
ManyToManyField:多对多,将字段定义在两端中
OneToOneField:一对一,将字段定义在任意一端中
用一访问多:对象.模型类小写_set
bookinfo.heroinfoset
用一访问一:对象.模型类小写
heroinfo.bookinfo
str(self):重写object方法,此方法在将对象转换成字符串时会被调用
save():将模型对象保存到数据表中
delete():将模型对象从数据表中删除
返回查询集的方法,称为过滤器
all():检索所有的对象
filter(**kwargs):检索特定的对象返回一个与参数匹配的QuerySet
exclude():返回一个与参数不匹配的QuerySet
order_by(column_name):检索排序后的对象
column_name:排序的列,默认升序,列名前加-降序排序
get():返回单个满足条件的对象
如果未找到会引发“模型类.DoesNotExist”异常。如果多条被返回,会引发’’模型类MultipleObjectsReturned“异常
count():返回当前查询的总条数
first():返回第一个对象
last():返回最后一个对象
exists():判断查询集中是否有数据,如果有则返回True
实现where子名,作为方法filter()、exclude()、get() 的参数
语法:属性名称_比较运算符=值
表示两个下划线,左侧是属性名称,右侧是比较类型
浏览器通过http://127.0.0.1:8000/booktest/向服务器发起请求
url正则表达式匹配,去除域名后的路径:booktest/
项目urls.py内容:
url patterns = [
path('admin/', admin.site.urls),
url(r'^booktest/', include('booktest.urls')),
]
url patterns =[
url(r'^$', views.index),
url(r'^detail/(\d+)/$', views.detail),
]
def index(request):
# 获取图书的信息
booklist = Bookinfo.books.all()
context={
'request':request, 'booklist':booklist}
return render(request, 'booktest/index.html', context)
把数据插入模版,返回客户端渲染
def url(regex, view, kwargs=None, name=None)
功能:凡是与regex匹配的URL请求都会执行到url()函数中对应的第二个参数view代
表的视图函数。
regex:正则表达式
view:视图函数
name:名称(命名空间)
name是用来唯一区分一个视图对应多个urlconf的场景
假如一个project中有多个app,所有的url都通过项目urlconf管理可能会造成比较混
乱的局面。
为了解决这个问题,我们可以用include的方法来配置:
url(r'^booktest/', include('booktest.urls'))
include(arg, namespace=None)
作用:引用每个应用各自的urls.py配置文件
参数1:包含booktest应用的urlconf
参数2:定义命名空间,用于反解析
—个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。
问题:如果在视图、模板中使用硬编码的链接,在urlconf发生改变时,维护非常麻烦
解决:通过指向urlconf的名称,根据正则表达式动态生成链接地址
使用 django.urls.reverse()函数
from django.urls import reverse
from django.http import HttpResponseRedirect
def test(reqeust, id):
# url name 的使用:
return HttpResponseRedirect(reverse('booktest:detail', args=(1,)))
说明:
第一个参数:“bookTest: detail”: 'booktest应用下的name=detail的函数。
booktest应用url conf:
url(r,Abooktest/detail/([0-9]+)/$',views.detail,name='detail')
第二个参数:self.pk会替代正则表达式里面的pk,然后reverse函数去解析视图函数对应的URL。
项目的url conf: url(r'^', include('booktest.urls', namespace='booktest'))
django2.x:{ { book.btitle }}
多个参数的传递:{ { book.btitle }}
应用 url conf: url(r'^detail2/(\d+)/(\d+)/$', views.detail2, name='detail2'),
服务器接收到http协议的请求后,会根据报文创建HttpRequest对象
视图函数的第一个参数是HttpRequest对象
在django.http模块中定义了 HttpRequest对象的API
QueryDict类型的对象
包含get请求方式的所有参数
与url请求地址中的参数对应,位于?后面
参数的格式是键值对,如key1=value1
多个参数之间,使用&连接,如key1=value1&key2=value2
键是开发人员定下来的,值是可变的
一个键传递多个值:gettest3
视图中接收多个值:a=request.GET.getlist('a')
控件要有name属性,则name属性的值为键,value属性的值为值,构成键值对提交。
对于checkbox控件,name属性一样为一组,当控件被选中后会被提交,存在一键多值的情况。
键是开发人员定下来的,值是可变的。
在 django.http模块中定义了HttpResponse对象的API
HttpResponse对象由程序员创建
from django.http import HttpResponse
from django.template import RequestContext, loader
def index(request):
# 获取图书表中所有的记录
booklist = Booklnfo.objects.all()
# 加载index.html模版
template = loade r.get_templateCindex.html')
# 字典,用于插入模版中的数据
context = {
'booklist': booklist}
# 返回模版渲染的结果
return HttpResponse(template.render(context))
from django.shortcuts import render
def getTest2(request):
a = request.GET['a']
b = request.GET['b']
context = {
'a': a, 'b': b}
return render(requestz 'booktest/getTest2.htmI\ context)
redirect(to)
为传递进来的参数返回HttpResponseRedirect
推荐使用反向解析
from django.shortcuts import redirect
from django.urls import reverse
def getTest (request):
return redirect(reverse('booktest:index1'))
url(r'^getTest/$', views.getTest),
Cookie技术来实现记录访问者的一些基本信息。为基于Internet的各种服务系统应运而生。
response.set_cookie(key, value:'', max_age=None, expires=None)
request.COOKIES.get("name")
response. delete_cookie(key)
如果key不存在则什么也不发生
response.set_cookie方法中指定expires即可,具体如下
max_age是一个整数,表示在指定秒数后过期
expires是一个datetime或timedelta对象,会话将在这个指定的日期/时间过期,注意datetime和timedelta值只有在使用PickleSerializer时才可序列化
max_age与expires二选一
如果不指定过期时间,则两个星期后过期
http协议是无状态的:每次请求都是一次新的请求,不会记得之前通信的状态
客户端与服务器端的一次通信,就是一次会话
实现状态保持的方式:在客户端或服务器端存储与会话有关的数据
存储方式包括cookie、session
使用cookie,所有数据存储在客户端,注意不要存储敏感信息
推荐使用sesison方式,所有数据存储在服务器端,在客户端cookie中存储session id
状态保持的目的是在一段时间内跟踪请求者的状态,可以实现跨页面访问当前请求者的数据
注意:不同的请求者之间不会共享这个数据,与请求者一一对应
启用会话后,每个HttpRequest对象将具有一个session属性,它是一个类字典对象
get(key, default=None):根据键获取会话的值
clear():清除所有会话
flush():删除当前的会话数据并删除会话的Cookie
del request.session[‘member_id’]:删除会话
set_expiry(value):设置会话的超时时间
如果没有指定,则两个星期后过期
如果value是一个整数,会话将在values秒没有活动后过期
如果value是一个timedelta对象,会话将在当前时间加上这个指定的日期/时间过期
如果value为0,那么用户会话的Cookie将在用户的浏览器关闭时过期
如果value为None,那么会话永不过期
修改视图中login handle函数,查看效果
def login_handle(request):
req uest.session['uname'] = request.POST['uname']
# request.session.set_expiry(10)
# request.session.set_expiry(timedelta(days=5))
# request.session.set_expiry(0)
# request.session.set_expiry(None)
return redirect(reverse('usertest:index'))
作为Web框架,Django提供了模板,可以很便利的动态生成HTML,致力于外观显示,通过模板语言可以把数据插入到模版中,模板的设计实现了业务逻辑 (view) 与显示内容 (template) 的分离
Django处理模板分为两个阶段,加载和渲染,加载阶段根据给定的标识找到模板后预处理,将它编译好放在内存中,渲染阶段使用把数据插入模板并返回生成的字符串
{ { variable }}
当模版引擎遇到一个变量,将计算这个变量,然后将结果输出
语法:
{% for ... in ... %}
{
{ forloop.counter }}
表示当前是第几次循环
{% empty %}
给出的列表空为或列表不存在时,执行此处
{% endfor %}
if条件语句中变量和==之间一定要有空格!
例如:hero.showName ==
。 如果连在一起写hero.showName==
,程序会将它们看成了一个整体而报错:Could not parse the remainder: '=='1 "from 'sort=='1"
多行注释
{% comment %}
多行注释
{% endcomment %}
范例
{% comment %}
This is a
multi-line comment.
{% endcomment %}
单行注释
{#这是一个注释#}
CSRF全称Cross Site Request Forgery,跨站请求伪造。
某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作,这就是跨站攻击。
在django防御csrf攻击,在客户端页面表单添加csrftoken,服务器端进行验证{% csrf_token %}
block标签:在父模板中预留区域,在子模板中填充
extends继承:继承,写在模板文件的第一行
AJAX:异步JavaScript和XML (Asynchronous JavaScript and XML)。
AJAX是与服务器交换数据的技术,它在不重载全部页面的情况下,实现了对部分网页的更新。
AJAX通过后台加载数据,并在网页上进行显示。
使用AJAX的应用程序案例:谷歌地图、微博、优酷视频等。
Django Haystack是django的一个包,一种全文检索的框架
可以方便地对model里面的内容进行索引、搜索
为Django提供了模块化的搜索,它提供一个统一的、友好的API,允许您插入不同的搜索后端(如Solr, Elasticsearch, Whoosh, Xapian,等等而不需要修改代码。)
支持whoosh, solr, Xapian, Elasticsearc四种全文检索引擎后端
要使用django haystack,首先必须安装它,并且安装一些必要的依赖
whoosh
纯Python编写的全文搜索引擎,虽然性能比不上sphinx、xapian、Elasticsearc等,但是无二进制包,程序不会莫名其妙的崩溃,对于小型的站点,whoosh已经足够使用
jieba
—款免费的中文分词包。把句子拆开分成一个一个的词语,这样能更好的分析句子的特性,这个过程叫做分词.
Whoosh自带的是英文分词,对中文的分词支持不是太好,所以使用jieba替换Whoosh的分词组件。
对于中等流量的网站来说,尽可能地减少开销是必要的。缓存数据就是为了保存那些需要很多计算资源的结果,这样的话就不必在下次重复消耗计算资源。
Django自带了一个健壮的缓存系统来保存动态页面,避免对于每次请求都重新计算。
Django提供了不同级别的缓存粒度:可以缓存特定视图的输出、可以仅仅缓存那些很难生产出来的部分、或者可以缓存整个网站。
引起这个原因的主要因素是高并发下,我们一般设定一个缓存的过期时间时,可能有一些会设置5分钟,10分钟这些;并发很高时可能会出在某一个时间同时生成了很多的缓存,并且过期时间在同一时刻,这个时候就可能引发——当过期时间到后,这些缓存同时失效,请求全部转发到DB,DB可能会压力过重。
一个简单方案就是将缓存失效时间分散开,不要所以缓存时间长度都设置成5分钟或者10分钟;比如我们可以在原有的失效时间基础上増加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
缓存失效时产生的雪崩效应,将所有请求全部放在数据库上,这样很容易就达到数据库的瓶颈,导致服务无法正常提供。尽量避免这种场景的发生。
缓存失效时的雪崩效应尽管对底层系统的冲击非常可怕。但遗憾的是,这个问题目前并没有很完美的解决方案。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。
指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
当在流量较大时,出现这样的情况,一直请求DB,很容易导致服务挂掉。
在封装的缓存SET和GET部分增加个步骤,如果查询一个KEY不存在,就已这个KEY为前缀设定一个标识KEY;以后再查询该KEY的时候,先查询标识KEY,如果标识KEY存在,就返回一个协定好的非false或者NULL值,然后APP做相应的处理,这样缓存层就不会被穿透。当然这个验证KEY的失效时间不能太长。
如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,一般只有几分钟。
采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
当网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时设置缓存的情况,如果并发确实很大,这也可能造成DB压力过大,还有缓存频繁更新的问题。
对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DBM。
从uwsgi、nginx、静态文件三个方面处理
是一种按照一定的规则,自动地抓取互联网信息的程序或者脚本。
所谓网页抓取,就是把URL地址中指定的网络资源从网络流中读取出来,保存到本地。在Python中有很多库可以用来抓取网页。
网页抓取,数据提取,数据存储
1xx:指示信息。表示请求已接收,继续处理。
2xx:成功。表示请求已被成功接收、理解、接受。
3xx:重定向。完成请求必须进行更进一步的操作。
4xx:客户端错误。请求有语法错误或请求无法实现。
5xx:服务器端错误。服务器未能实现合法的请求。
常见状态代码、状态描述、说明:
urllib库,requests库
xpath、bs4,re
selenium+浏览器
模拟ajax请求
设置User-Agent
a. 使用代理ip;
b. 设置随机延迟时间
模拟登陆:
a. 用登陆之后的cookie模拟登陆;
b. 用账户名和密码模拟登陆表单的提交进行登陆;
c. 用selenium自动化实现登陆
a. selenium+浏览器;
b. 模拟ajax请求
a. pytesseract技术;
b. 打码平台;
c. 滑动验证码selenium+打码平台;
d. 点触式验证码
Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架。
用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片。
Scrapy使用了Twisted异步网络框架来处理网络通讯,可以加快下载速度,并且包含了各种中间件接口,可以灵活的完成各种需求。
略
新建项目 (scrapy startproject xxx):新建一个新的爬虫项目
明确目标 (编写items.py):明确你想要抓取的目标
制作爬虫 (spiders/xxspider.py):制作爬虫开始爬取网页
存储内容 (pipelines.py):设计管道存储爬取内容
把提取结构化数据提交给管道
把新的请求对象提交给到请求等待队列
当Item在Spider中被收集之后,它将会被传递到Item Pipeline,这些Item Pipeline组件按定义的顺序处理ltem。
每个Item Pipeline都是实现了简单方法的Python类,比如决定此Item是丢弃而存储。
以下是item pipeline的一些典型应用:
验证爬取的数据(检查item包含哪些字段,比如说name字段)
查重(并丢弃)
将爬取结果保存到文件或者数据库中
Scrapy提取数据有自己的一套机制。它们被称作选择器 (seletors),
Selector有四个基本的方法,最常用的还是xpath:
xpath():传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
extract():序列化该节点为Unicode字符串并返回list
css():传入CSS表达式,返回该表达式所对应的所有节点的selector list列表,语法同BeautifulSoup4
re():根据传入的正则表达式对数据进行提取,返回Unicode字符串list列表
Scrapy使用request对象来爬取web站点。
class scrapy.http.Request(url[, callback, method='GET', headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback])
—个request对象代表一个HTTP请求,通常由Spider产生,经Downloader执行从而产生一个 Response。
url:用于请求的URL
callback:指定一个回调函数,该回调函数以这个request对应的response作为第一个参
数。如果未指定callback,则默认使用spider的parse()方法
method:HTTP请求的方法,默认为GET
meta:指定Request.meta属性的初始值,字典类型。可以在回调函数们之间传递参数
body:请求体
headers:request 的头信息
cookies:cookie
encoding:请求的编码,默认为utf-8
priority:请求的优先级
dont_filter(boolean):指定该请求是否被Scheduler过滤。该参数可以是request重复使
用(Scheduler默认过滤重复请求)。谨慎使用!!!
errback:处理异常的回调函数。
一个Response对象表示的HTTP响应,这通常由下载器提供给到爬虫进行处理。
url
包含响应的URL的字符串。
status
表示响应的HTTP状态的整数。示例:200,404。
headers
包含响应标题的类字典对象。可以使用get()返回具有指定名称的第一个标头值或getlist()
返回具有指定名称的所有标头值来访问值。例如,此调用会为您提供标题中的所有Cookie:response.headers.getlist('Set-Cookie')
body
正文
meta
获得Request.meta从您的爬虫发送的原始属性。
Scrapy-redis是为了更方便地实现Scrapy分布式爬取,而提供了一些以redis为基础的组件(仅有组件)。主体还是是redis和scrapy两个库,Scrapy-redis像胶水一样,把这两个插件粘结了起来。
能实现分布式爬取
可实现去重
持续性爬取,可实现增量式爬虫
遵守Rule规则,可以实现深度爬虫
Scrapy | Scrapy-redis |
---|---|
Scheduler(调度器) | Scheduler(调度器) |
请求的处理在调度器中进行 | 将数据放到redis数据库队列中处理 |
Duplication Filter(重复过滤器) | Duplication Filter(重复过滤器) |
请求指纹,在python集合中处理 | 在redis数据库的set中去重 |
Item Pipeline | Item Pipeline |
决定数据如何处理 | 将数据存放到redis数据库队列中 |
Spider | Base Spider |
普通的Scrapy爬虫类 | 可以从redis中获取url |
继承的是CrawlSpider
需要设置Rule规则,以及callback不能写parse()方法。
它是用来说明Redis的持续性,可实现增量式爬虫
当我们第一次运行dmoz爬虫,然后Ctrl+C停掉之后,再运行dmoz爬虫,之前的爬取记录是保留在Redis里的。
继承了RedisSpider,支持分布式抓取。
需要写parse函数。
不需要写start_urls了,取而代之指定redis_key, scrapy-redis将key从Redis里pop出来,成为请求的url地址。
根据指定的格式,start_urls将在Master端的redis-cli里Ipush到Redis数据库里,RedisSpider将在数据库里获取start_urls。
参考格式:redis_key = 'myspider:start_urls'
爬虫继承了RedisCrawlSpider。
能够支持分布式的抓取。
因为采用的是crawlSpider,所以需要遵守Rule规则,可以实现深度爬虫。
> callback不能设为parse()方法。
不需要写start_urls了,取而代之的是redis_key。
scrapy-redis将key从Redis里pop出来,成为请求的url地址。
将访问过的URL保存到数据库中
缺点:效率低
将访问过的URL保存到set中
优点:只需要O(1)的代价就可以查询URL
缺点:对内存要求高。若有1亿网页,则占用内存为:1000000000*2byte*50
个字符/1024/1024/1024=9G
URL经过md5等方法哈希后保存到set中
优点:可以成倍降低内存占用,Scrapy使用的这种方法
用bitmap或者bloomfilter方法,将访问过的URL通过hash函数映射到某一位
bitmap方法优点:一亿URL占用约12M
bitmap方法:去重没那么精准,存在冲突。
bloomfilter优点:对bitmap进行改进,多重hash函数降低冲突
代理ip采集功能
代理ip有效性验证功能
存储功能
随机提取有效代理ip的API
删除无效代理ip的功能
代理ip池批量管理代理ip地址,提供随机有效的代理ip。
如果需要做大规模抓取,就需要用有很多账号,每次请求随机选取一个账号,这样就降低了单个账号的访问频率,被封的概率又会大大降低。多个账号的登录信息维护,就需要用到Cookies池。
保存多个和登录后的Cookies信息
检测Cookies池中每个Cookies的有效性
删除无效Cookies并模拟登录生成新的Cookies
随机获取Cookies的接口
Scrapyd是一个部署和运行Scrapy爬虫的应用程序。它能够通过JSON API部署(上传)工程,并且控制工程中爬虫地启动、停止、暂停,修改。
在每台服务器上安装scrapyd,并开启scrapyd服务
在开发爬虫的客户端安装上scrapyd的客户端scrapyd-client
通过scrapyd-client把不同网站的爬虫发送到不同的服务器
确保所有服务器主机都已经安装和启动Scrapyd
开发主机安装并运行ScrapydWeb
scrapydweb_settings.py配置文件中进行部署配置
通过浏览器访问并登录http://127.0.0.1:5000访问web UI
希望在页面上直观地查看所有云主机的运行状态
希望能够自由选择部分云主机,批量部署和运行爬虫项目,实现集群管理
希望自动执行日志分析,以及爬虫进度可视化
希望在出现特定类型的异常日志时能够及时通知用户,包括自动停止当前爬虫任务
数据分析是指用适当的统计分析方法对收集来的大量数据进行分析,提取有用信息和形成结论的过程。数据分析可帮助人们作出判断,以便采取适当行动。
数据收集:本地数据或者网络数据的采集与操作.
数据处理:数据的规整,按照某种格式进行整合存储。
数据分析:数据的科学计算,使用相关数据工具进行分析
数据展现:数据可视化,使用相关工具对分析出的数据进行展示。
SAS(statistical analysis system)
SAS公司的统计分析软件,强大的数据库整合平台,做离线的分析或者模型用,价格昂贵,服务于银行或者大企业。
SPSS (Statistical Product and Service Solutions, 统计产品与服务解决方案)
IBM 公司产品,用于统计学分析运算、数据挖掘,预测分析和决策支持任务的。
R/MATLAB
适合做学术性质的数据分析,实际应用上需要额外转换为python和scala来实现。
scala
函数式编程语言,入门门槛高,开发效率高,配合spark适合大规模数据分析和处理,scala运行环境JVM。
python
数据工程领域和机器学习领域有很多成熟的架构和算法库,完全可以只用python可以构建以数据为中心的应用程序,在数据工程领域和机器学习领域,python非常流行。