如果表达式为True,就执行[on true]中的语句。否则,就执行[on false]中的语句
GIL的全称是:Global Interpreter Lock,意思就是全局解释器锁,
CPython在执行多线程的时候并不是线程安全的,所以为了程序的稳定性,加一把全局解释锁,能够确保任何时候都只有一个Python线程执行
注意:基类就是父类,派生类就是子类
当一个类继承另一个类,它就被称为一个子类/派生类,继承父类/基类/超类。
继承能让我们重新使用代码,也能更容易的创建和维护应用
单继承:一个类继承单个基类
多继承:一个类继承多个基类
多级继承:一个类继承自单个基类,后者继承自另一个基类
分层继承:多个类继承自单个基类
混合继承:两种或多种类型继承的混合
help函数是一个内置函数,用于查看函数或模块用途的详细说明
dir()函数时python的内置函数,dir()函数不带参数时,返回当前范围内的变量、方法和定义的类型列表,带参数时,返回参数的属性、方法列表
属性在运行时的动态替换,叫做猴子补丁(Monkey Patch)。
注意:运行时刻是指一个程序在运行(或者在被执行)的状态
当我们不知道向函数传递多少参数时,比如我们向传递一个列表或元组,我们就使用*args。
在我们不知道该传递多少关键字参数时,使用**kwargs来收集关键字参数。
负索引和正索引不同,它是从右边开始检索。
Join()能让我们将指定字符添加至字符串中。
Split()能让我们用指定字符分割字符串。
我们使用方法lstrip()可以将它从字符串中移除。
后导空格 rstrip
当一个嵌套函数在其外部区域引用了一个值时,该嵌套函数就是一[个闭包。其意义就是会记录这个值。
切片是Python中的一种方法,能让我们只检索列表、元素或字符串的一部分。在切片时,我们使用切片操作符[]。
如果我们需要一个只有单一表达式的函数,我们可以匿名定义它。拉姆达表达式通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。
在调用一个函数的过程中,直接或间接地调用了函数本身这个就叫递归。但为了避免出现死循环,必须要有一个结束条件
生成器会生成一系列的值用于迭代,这样看它又是一种可迭代对象。它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。
迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。我们使用inter()函数创建迭代器。
在创建生成器时,我们创建一个函数;
在使用迭代器时,我们使用内置函数iter()和next()。
在生成器中,我们使用关键字‘yield’来每次生成/返回一个对象。 生成器中有多少‘yield’语句,你可以自定义。 每次‘yield’暂停循环时,生成器会保存本地变量的状态。而迭代器并不会使用局部变量,它只需要一个可迭代对象进行迭代。 使用类可以实现你自己的迭代器,但无法实现生成器。 生成器运行速度快,语法简洁,更简单。 迭代器更能节约内存。
yield简单说来就是一个生成器,这样函数它记住上次返 回时在函数体中的位置。对生成器第 二次(或n 次)调用跳转至该函 次)调用跳转至该函数。
Python使用按引用传递(pass-by-reference)将参数传递到函数中。如果你改变一个函数内的参数,会影响到函数的调用。这是Python的默认操作。不过,如果我们传递字面参数,比如字符串、数字或元组,它们是按值传递,这是因为它们是不可变的。
python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象--相当于通过“传值’来传递对象。
值传递仅仅传递的是值
引用传递,传递的是内存地址,修改后会改变内存地址对应储存的值。
Python中创建包是比较方便的,只需要在当前目录建立一个文件夹,文件夹中包含一个init.py文件和若干个模块文件,其中init.py可以是一个空文件,但还是建议将包中所有需要导出的变量放到all中,这样可以确保包的接口清晰明了,易于使用。
元类是类的类对象,换言之类是元类的实例,Python中默认的元类为type,可以通过自定义元类的方式实现对类创建的控制。
自省就是面向对象的语言所写的程序在运行时,能够知道对象的类型。简单一句就是,运行时能够获知对象的类型。
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。
希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;
随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
归并排序是采用分治法的一个非常典型的应用。
归并排序的思想就是先递归分解数组,再合并数组。
将数组分解最小之后,然后合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。
然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。
def binary_chop(alist, data):
"""
非递归解决二分查找
"""
n = len(alist)
first = 0
last = n - 1
while first <= last:
mid = (last+first)//2
if alist[mid] > data:
last = mid - 1
elif alist[mid] < data:
first = mid + 1
else:
return True
return False
def binary_chop2(alist, data):
"""
递归解决二分查找
"""
n = len(alist)
if n < 1:
return False
mid = n // 2
if alist[mid] > data:
return binary_chop2(alist[0:mid], data)
elif alist[mid] < data:
return binary_chop2(alist[mid+1:], data)
else:
return True
if __name__ == "__main__":
lis = [2,4, 5, 12, 14, 23]
if binary_chop(lis, 12):
print('ok')
else:
print('false')
def search(list, key):
left = 0 # 左边界
right = len(list) - 1 # 右边界
while left <= right:
mid = (left + right) // 2 # 取得中间索引
if key > list[mid]:
left = mid + 1
elif key < list[mid]:
right = mid - 1
else:
return mid
else:
return -1
list = [2, 5, 13, 21, 26, 33, 37]
print(search(list, 5))
调用方式上
静态方法依赖于类,通过类.静态方法调用;实例方法依赖于类的对象,需要创建对象后,对象.实例方法使用
使用上
实例方法内部不能定义静态变量,会出现编译错误;实例方法可以直接调用静态方法;静态方法内部可以定义和使用实例变量,静态方法无法直接调用实例方法(因静态方法加载时类还没有实例化,实例方法依赖于类的对象)
上下文协议:enter、exit
像map()函数这种能够接收函数作为参数的函数,称之为高阶函数
Factory Method(工厂方法)
Abstract Factory(抽象工厂)
Builder(建造者)
Prototype(原型)
Singleton(单例)
Prototype(原型)
单例
意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性:
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
传统测试无非就是自己运行一下程序查看结果,或者前后端服务进行联调,这里要说的是走正规流程的单元测试,那到底什么是单元测试呢?
顾名思义,只测试当前单元的程序或者代码,也可以理解当前模块的代码块,单元测试假设所有的内部或外部的依赖应该是稳定的, 已经在别处进行测试过的。
使用mock 就可以对外部依赖组件实现进行模拟并且替换掉, 从而使得单元测试将焦点只放在当前的单元功能。
简单地说,mock就是帮我们解决测试依赖的一个模块,在Python3中,mock已经被集成到了unittest单元测试框架中,所以不需要单独安装,可以直接使用。
__new__ 和 __init__
__new__ 为类方法,__init__ 为实例方法。__new__ 类方法创建实例对象,__init__ 实例方法初始化实例对象。
__get__, __getattr__, __getattribute__ 的区别
均是访问属性的方法,注意是属性 __getattr__(self, name)
当访问属性无法找到时,默认异常,可以自定义其返回值或者 AttributeError 异常 __getattribute__(self, name):
2.7 在新式类中引入,如果定义,则无条件执行,如果实行不存在时,也不执行 __getattr__(相当于被屏蔽掉) __get__ (self, instance, owner) :
如果class定义了它,则这个class就可以称为descriptor。
owner是所有者的类,instance是访问descriptor的实例,如果不是通过实例访问,而是通过类访问的话,instance则为None。
(descriptor的实例自己访问自己是不会触发__get__,而会触发__call__,只有descriptor作为其它类的属性才有意义。)
插值查找,有序表的一种查找方式。插值查找是根据查找关键子与查找表中最大最小记录关键字比较后的查找方法。插值查找基于二分查找,将查找点的选择改进为自适应选择,提高查找效率。
class LNode:
def __init__(self, elem):
self.elem = elem
self.pnext = None
def exitLoop(LList):
p1 = p2 = LList
while p2 and p2.pnext: #当链表为空或者只有一个结点时,就不执行循环体里的程序,返回False
p1 = p1.pnext
p2 = p2.pnext.pnext
if p1 == p2:
return True
return False
if __name__=="__main__":
LList = LNode(1)
p1 = LNode(2)
p2 = LNode(3)
p3 = LNode(4)
p4 = LNode(5)
p5 = LNode(6)
LList.pnext = p1
p1.pnext = p2
p2.pnext = p3
p3.pnext = p4
p4.pnext = p5
p5.pnext = p2
其实红黑树也可以作为索引的,但是出于某种原因,最终还是没有选择它,某种原因肯定是效率的问题了,没有选择它,肯定是有比它更
所有关键字存储在叶子节点,非叶子节点不存储真正的data
为所有叶子节点增加了一个链指针
async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
async函数返回一个 Promise 对象,可以使用then方法添加回调函数。
当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
yield 语句只能出现在 iterator 块中,这种块可作为方法、运算符或访问器的主体实现
python 为了将语义更加明确, 就引入了async和await关键词用于定义原生的协程
await 只能出现在 async里面
async里面不能有yield
Greenlet是python的一个C扩展,来源于Stackless python,旨在提供可自行调度的‘微线程’, 即协程。
generator实现的协程在yield value时只能将value返回给调用者(caller)。
而在greenlet中,target.switch(value)可以切换到指定的协程(target), 然后yield value。greenlet用switch来表示协程的切换,从一个协程切换到另一个协程需要显式指定。
没有
根据hash值排序的
Django走的是大而全的方向,开发效率高。它的MTV框架,自带的ORM,admin后台管理,自带的sqlite数据库和开发测试用的服务器 给开发者提高了超高的开发效率
Flask是轻量级的框架,自由,灵活,可扩展性很强,核心基于Werkzeug WSGI工具和jinja2模板引擎
Tornado走的是少而精的方向,性能优越。它最出名的是异步非阻塞的设计方式
Tornado的两大核心模块: 1.iostraem:对非阻塞式的socket进行简单的封装 2.ioloop:对I/O多路复用的封装,它实现了一个单例
WSGI,描述web server如何与web application通信的一种规范
WSGI协议主要包括server和application两部分:
WSGI server负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端; WSGI application接收由server转发的request,处理请求,并将处理结果返回给server。
application中可以包括多个栈式的中间件(middlewares),这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:
对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。
FBV(function base views) 基于函数的视图
CBV(class base views) 基于类的视图
使用fbv的模式,在url匹配成功之后,会直接执行对应的视图函数
使用cbv模式,在url匹配成功之后,会找到视图函数中对应的类,然后这个类回到请求头中找到对应的
Request Method
用户发送url请求,Django会依次遍历路由映射表中的所有记录,
一旦路由映射表其中的一条匹配成功了,就执行视图函数中对应的函数名,这就是fbv的执行流程。
当服务端使用cbv模式的时候,用户发给服务端的请求包含url和method,这两个信息都是字符串类型
服务端通过路由映射表匹配成功后会自动去找dispatch方法,然后Django会通过dispatch反射的方式找到类中对应的方法并执行。
类中的方法执行完毕之后,会把客户端想要的数据返回给dispatch方法,由dispatch方法把数据返回经客户端
uWSGI是C语言编写,Gunicorn是Python,性能上前者要高。
对于Django项目用uWSGI就够了,不用在去研究Gunicorn
浏览器向django服务器发起请求
urls.py
中间件
views.py
templates
层层返回给浏览器
process_request : 请求进来时,权限认证
process_view : 路由匹配之后,能够得到视图函数
process_exception : 异常时执行
process_template_responseprocess : 模板渲染时执行
process_response : 请求有响应时执行
使用 ORM 最大的优点就是快速开发,让我们将更多的精力放在业务上而不是数据库上,下面是 ORM 的几个优点
隐藏了数据访问细节,使通用数据库交互变得简单易行。
同时 ORM 避免了不规范、冗余、风格不统一的 SQL 语句,可以避免很多人为的 bug,方便编码风格的统一和后期维护。
将数据库表和对象模型关联,我们只需针对相关的对象模型进行编码,无须考虑对象模型和数据库表之间的转化,大大提高了程序的开发效率。
方便数据库的迁移。当需要迁移到新的数据库时,不需要修改对象模型,只需要修改数据库的配置。
ORM 的最令人诟病的地方就是性能问题,不过现在已经提高了很多,下面是 ORM 的几个缺点
性能问题 自动化进行数据库关系的映射需要消耗系统资源 程序员编码 在处理多表联查、where 条件复杂的查询时,ORM 可能会生成的效率低下的 SQL 通过 Lazy load 和 Cache 很大程度上改善了性能问题 SQL 调优。
SQL 语句是由 ORM 框架自动生成,虽然减少了 SQL 语句错误的发生,但是也给 SQL 调优带来了困难。
越是功能强大的 ORM 越消耗内存,因为一个 ORM Object 会带有很多成员变量和成员函数。 对象和关系之间并不是完美映射 一般来说 ORM 足以满足我们的需求。
如果对性能要求特别高或者查询十分复杂,可以考虑使用原生 SQL 和 ORM 共用的方式 使用原生sql优点:
使用原生sql缺点:
QuerySet | 从数据库中查询出来的结果一般是一个集合,这个集合叫做 |
---|---|
filter | 过滤 |
exclude | 排除 |
annotate | 聚合 |
order_by | 排序 |
reverse | 反向排序 |
distinct | 去除查询结果中重复的行 |
values | 迭代时返回字典而不是模型实例对象 |
values_list | 迭代时返回元组而不是字典 |
dates | 表示特定种类的所有可用日期 |
datetimes | 表示特定种类的所有可用日期 |
none | 不返回任何对象 |
all | 返回所有结果 |
select_related | 外键查询 |
prefetch_related | 在单个批处理中自动检索每个指定查找的相关对象 |
defer | 告诉django不要查询某些字段 |
using | 多个数据库时控制 QuerySet在哪个数据库上求值 |
runserver 方法是调试 Django 时经常用到的运行方式,它使用Django自带的 WSGI Server 运行,主要在测试和开发中使用,并且 runserver 开启的方式也是单进程 。
uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http 等协议。注意uwsgi是一种通信协议,而uWSGI是实现uwsgi协议和WSGI协议的 Web 服务器。
uWSGI具有超快的性能、低内存占用和多app管理等优点,并且搭配着Nginx就是一个生产环境了,能够将用户访问请求与应用 app 隔离开,实现真正的部署 。相比来讲,支持的并发量更高,方便管理多进程,发挥多核的优势,提升性能。
restful 提倡面向资源编程,在url接口中尽量要使用名词,不要使用动词
在url接口中推荐使用Https协议,让网络接口更加安全 https://baidum/v1/mycss?page=3 (Https是Http的安全版,即HTTP下加入SSL层,HTTPS的安全 基础是SSL, 因此加密的详细内容就需要SSL(安全套接层协议))
在url中可以体现版本号 https://v1.bootcss.com/mycss
不同的版本可以有不同的接口,使其更加简洁,清晰
url中可以体现是否是API接口 https://baidum/api/mycss
url中可以添加条件去筛选匹配 https://baidum/v1/mycss?page=3
可以根据Http不同的method,进行不同的资源操作 (5种方法:GET / POST / PUT / DELETE / PATCH)
响应式应该设置状态码
有返回值,而且格式为统一的json格式
返回错误信息
返回结果中要提供帮助链接,即API最好做到接口文档
高负载高并发环境下,数据业务层、数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器负载,如此多的数据库连接操作,数据库必然会崩溃,数据库如果宕机的话,后果更是不堪设想。
这时候,我们会考虑如何减少数据库的连接,一方面采用优秀的代码框架,进行代码的优化,采用优秀的数据缓存技术如:redis,如果资金丰厚的话,必然会想到架设mysql服务集群,来分担主数据库的压力。
总结一下利用MySQL主从配置,实现读写分离,减轻数据库压力。
mysql主从同步的原理很简单,
从库生成两个线程,一个I/O线程,一个SQL线程;
i/o线程去请求主库 的binlog(二进制日志),并将得到的binlog日志写到relay log(中继日志) 文件中;
主库会生成一个 log dump 线程,用来给从库 i/o线程传binlog;
SQL 线程,会读取relay log文件中的日志,并解析成具体操作,来实现主从的操作一致,而最终数据一致。
binlog是二进制日志文件,用于记录mysql的数据更新或者潜在更新(比如DELETE语句执行删除而实际并没有符合条件的数据)
总结,mysql压力小,延迟自然会变小。
总结,硬件强劲,延迟自然会变小。一句话,缩小延迟的解决方案就是花钱和花时间。
乐观锁不是数据库自带的,需要我们自己去实现。
乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁
触发程序是与表有关的命名数据库对象,当该表出现特定事件时,将激活该对象
监听:记录的增加、修改、删除。
事务是指逻辑上的一组操作,组成这组操作的各个单元,要不全成功要不全失败
原子性(Atomicity)
事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务前后数据的完整性必须保持一致。
隔离性(Isolation)
多个用户并发访问数据库时,一个用户的事务不能被其它用户的事物所干扰,多个并发事务之间的数据要相互隔离。
持久性(Durability)
一个事务一旦被提交,它对数据库中的数据改变就是永久性的。
事务是指逻辑上的一组操作,组成这组操作的各个单元,要不全成功要不全失败。
支持连续SQL的集体成功或集体撤销。
事务是数据库在数据晚自习方面的一个功能。
需要利用 InnoDB 或 BDB 存储引擎,对自动提交的特性支持完成。
InnoDB被称为事务安全型引擎。
事务隔离级别:
未提交读(Read uncommitted),已提交读(Read committed),可重复读(Repeatable read),可序列化(Serializable)
Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。
本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
Read Committed(读取提交内容)
这是大多数 数据库系统的默认隔离级别(但不是MySQL默认的)。
它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。
这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。
不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。
简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:
脏读(Drity Read):
某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,
前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):
在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
幻读(Phantom Read):
在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,
先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,
导致事务A多次读取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为A B C D E等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,
当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
'小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表'
低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
表锁定只用于防止其它客户端进行不正当地读取和写入
MyISAM 支持表锁,InnoDB 支持行锁
锁定 LOCK TABLES tbl_name [AS alias]
解锁 UNLOCK TABLES
覆盖索引,这一点是最重要的,重所周知非主键索引会先查到主键索引的值再从主键索引上拿到想要的值,这样多一次查询索引下推。但是覆盖索引可以直接在非主键索引上拿到相应的值,减少一次查询。
在一张大表中如果有 (a,b,c)联合索引就等于同时加上了 (a) (ab) (abc) 三个索引减少了存储上的一部分的开销和操作开销
梯度漏斗,比如 select *from t where a = 1 and b = 2 and c = 3; 就等于在满足 a = 1 的一部分数据中过滤掉b = 2 的 再从 a = 1 and b = 2 过滤掉 c = 3 的,越多查询越高效。
BTREE 每个节点都是一个二元数组: [key, data],所有节点都可以存储数据。key为索引key,data为除key之外的数据。
查找算法:首先从根节点进行折半查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或未找到节点返回空指针
B+Tree有以下不同点:
总结:B+Tree 在 B-Tree 的基础上有两点变化:
连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,
当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。
使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。
而连接的建立、断开都由连接池自身来管理。
同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。
也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
有点类似单例模式的概念
inet_aton()算法
设计数据库时:
存储引擎如何选择?
早期问题:如何选择MyISAM和Innodb? 现在不存在这个问题了,Innodb不断完善,从各个方面赶超MyISAM,也是MySQL默认使用的。
存储引擎Storage engine:MySQL中的数据、索引以及其他对象是如何存储的,是一套文件系统的实现。
功能差异 show engines EngineSupportCommentInnoDBDEFAULTSupports transactions, row-level locking, and foreign keysMyISAMYESMyISAM storage engine
存储差异 MyISAMInnodb文件格式数据和索引是分别存储的,数据.MYD,索引.MYI数据和索引是集中存储的,
.ibd文件能否移动能,一张表就对应.frm、MYD、MYI3个文件否,
因为关联的还有data下的其它文件记录存储顺序按记录插入顺序保存按主键大小有序插入空间碎片(删除记录并flush table 表名之后,表文件大小不变)产生。
定时整理:使用命令optimize table 表名实现不产生事务不支持支持外键不支持支持锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的)
表级锁定行级锁定、表级锁定,锁定力度小并发能力高
锁扩展
表级锁(table-level lock):lock tables ,... read/write,unlock tables ,...。其中read是共享锁,一旦锁定任何客户端都不可读;
write是独占/写锁,只有加锁的客户端可读可写,其他客户端既不可读也不可写。
锁定的是一张表或几张表。
行级锁(row-level lock):锁定的是一行或几行记录。
共享锁:select fromwhere <条件> LOCK 鬼故事8 IN SHARE MODE;,
对查询的记录增加共享锁;select from where <条件> FOR UPDATE;,
对查询的记录增加排他锁。
这里值得注意的是:innodb的行锁,其实是一个子范围锁,依据条件锁定部分范围,而不是就映射到具体的行上,因此还有一个学名:间隙锁。
比如select * from stu where id < 20 LOCK IN SHARE MODE会锁定id在20左右以下的范围,你可能无法插入id为18或22的一条新纪录。
选择依据 如果没有特别的需求,使用默认的Innodb即可。
MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。
Innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高,支持事务和外键保证数据完整性。比如OA自动化办公系统。
我们可以通过explain selelct来分析SQL语句执行前的执行计划: 执行计划是:当执行SQL语句时,首先会分析、优化,形成执行计划,在按照执行计划执行。
where
order by 当我们使用order by将查询结果按照某个字段排序时,如果该字段没有建立索引,
那么执行计划会将查询出的所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序,最后合并排序结果),
这个操作是很影响性能的,因为需要将查询涉及到的所有数据从磁盘中读到内存(如果单条数据过大或者数据量过多都会降低效率),更无论读到内存之后的排序了。
但是如果我们对该字段建立索引alter table 表名 add index(字段名),那么由于索引本身是有序的,
因此直接按照索引的顺序和映射关系逐条取出数据即可。
而且如果分页的,那么只用取出索引表某个范围内的索引对应的数据,
而不用像上述那取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)
join
对join语句匹配关系(on)涉及的字段建立索引能够提高效率
like查询,不能以通配符开头 比如搜索标题包含mysql的文章:
select * from article where title like '%mysql%'; 这种SQL的执行计划用不了索引(like语句匹配表达式以通配符开头),
因此只能做全表扫描,效率极低,在实际工程中几乎不被采用。而一般会使用第三方提供的支持中文的全文索引来做。
但是 关键字查询 热搜提醒功能还是可以做的,比如键入mysql之后提醒mysql 教程、mysql 下载、mysql 安装步骤等。用到的语句是:
select * from article where title like 'mysql%'; 这种like是可以利用索引的(当然前提是title字段建立过索引)。
MongoDB 是一个介于关系数据库和非关系数据库之间的开源产品,是最接近于关系型数据库的 NoSQL 数据库。
它在轻量级JSON 交换基础之上进行了扩展,即称为 BSON 的方式来描述其无结构化的数据类型。
尽管如此它同样可以存储较为复杂的数据类型。它和Redis有异曲同工之妙。虽然两者均为 NoSQL ,
但是 MongoDB 相对于 Redis 而言,MongoDB 更像是传统的数据库。
早些年我们是先有了 Relation Database (关系型数据库),然后出现了很多很复杂的query ,里面用到了很多嵌套,很多 join 操作。
所以在设计数据库的时候,我们也考虑到了如何应用他们的关系,使得写 query 可以使 database 效率达到最高。
后来人们发现,不是每个系统,都需要如此复杂的关系型数据库。有些简单的网站,比如博客,比如社交网站,完全可以斩断数据库之间的一切关系。
这样做带来的好处是,设计数据库变得更加简单,写 query 也变得更加简单。然后,query 消耗的时间可能也会变少。
因为 query 简单了,少了许多消耗资源的 join 操作,速度自然会上去。
正如所说的, query 简单了,很有以前 MySQL 可以找到的东西,现在关系没了,通过 Mongo 找不到了。
我们只能将几组数据都抓到本地,然后在本地做 join ,所以在这点上可能会消耗很多资源。
这里我们可以发现。如何选择数据库,完全取决于你所需要处理的数据的模型,即 Data Model 。
如果它们之间,关系错综复杂,千丝万缕,这个时候 MySQL 一定是首选。如果他们的关系并不是那么密切,那么, NoSQL 将会是利器。
MongoDB 和 Redis 一样均为 key-value 存储系统,它具有以下特点:
- 面向集合存储,易存储对象类型的数据。
- 模式自由。
- 支持动态查询。
- 支持完全索引,
- 包含内部对象。
- 支持查询。
- 支持复制和故障恢复。
- 使用高效的二进制数据存储,包括大型对象(如视频等)。
- 自动处理碎片,以支持云计算层次的扩展性 支持
'Python , PHP , Ruby , Java , C , C# , Javascript ,Perl 及 C++ 语言的驱动程序,'
'社区中也提供了对 Erlang 及 .NET 等平台的驱动程序。 文件存储格式为 BSON (一种 JSON 的扩展)。 可通过网络访问。'
像 MySQL 一样, MongoDB 提供了丰富的远远超出了简单的键值存储中提供的功能和功能。
MongoDB 具有查询语言,功能强大的辅助索引(包括文本搜索和地理空间),数据分析功能强大的聚合框架等。
相比使用关系数据库而言,使用MongoDB ,您还可以使用这些功能,跨越更多样化的数据类型和数据规模。
MySQLMongoDB丰富的数据模型否是动态 Schema否是数据类型是是数据本地化否是字段更新是是易于编程否是复杂事务是否审计是是自动分片 否是
MySQL 中的许多概念在 MongoDB 中具有相近的类比。本表概述了每个系统中的一些常见概念。
MySQLMongoDB表集合行文档列字段joins嵌入文档或者链接
MongoDB 的主要目标是在 key-value (键/值)存储方式(提供了高性能和高度伸缩性)以及传统的 RDBMS 系统(丰富的功能)架起一座桥梁,集两者的优势于一身。
MongoDB 适用范围如下:
网站数据:
Mongo 非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
缓存:
由于性能很高, Mongo 也适合作为信息基础设施的缓存层。在系统重启之后,由 Mongo 搭建的持久化缓存层可以避免下层的数据源过载。
大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,
在此之前,很多时候程序员往往会选择传统的文件进行存储。
高伸缩性的场景:
Mongo 非常适合由数十或数百台服务器组成的数据库。
Mongo 的路线图中已经包含对 MapReduce 引擎的内置支持。
用于对象及 JSON 数据的存储:
Mongo 的 BSON 数据格式非常适合文档化格式的存储及查询。
MongoDB 当然也会有以下场景的限制:
高度事物性的系统:
例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。 传统的商业智能应用:
针对特定问题的 BI 数据库会对产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。 需要 SQL 的问题。
Redis本质上是一个Key-Value类型的内存数据库,很像memcached,
整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。
因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。
Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,
不像 memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能。
比方说用他的List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等。
另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。
Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。
所以redis具有快速和数据持久化的特征。
如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。
在内存越来越便宜的今天,redis将会越来越受欢迎。
如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
回答:主要是以下三点
快照(snapshots)
缺省情况情况下,Redis把数据快照存放在磁盘上的二进制文件中,文件名为dump.rdb。你可以配置Redis的持久化策略,例如数据集中每N秒钟有超过M次更新,就将数据写入磁盘;或者你可以手工调用命令SAVE或BGSAVE。
工作原理
Redis forks.
子进程开始将数据写到临时RDB文件中。
当子进程完成写RDB文件,用新文件替换老文件。
这种方式可以使Redis使用copy-on-write技术。
AOF
快照模式并不十分健壮,当系统停止,或者无意中Redis被kill掉,最后写入Redis的数据就会丢失。
这对某些应用也许不是大问题,但对于要求高可靠性的应用来说,Redis就不是一个合适的选择。Append-only文件模式是另一种选择。你可以在配置文件中打开AOF模式
虚拟内存方式
当你的key很小而value很大时,使用VM的效果会比较好.因为这样节约的内存比较大.
当你的key不小时,可以考虑使用一些非常方法将很大的key变成很大的value,比如你可以考虑将key,value组合成一个新的value.
vm-max-threads这个参数,可以设置访问swap文件的线程数,设置最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的.可能会造成比较长时间的延迟,但是对数据完整性有很好的保证.
自己测试的时候发现用虚拟内存性能也不错。如果数据量很大,可以考虑分布式或者其他数据库。
它是什么回事?先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
这时候对方会告诉你说你回答得不错,
然后接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?
这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。
紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,
然后回答:我记得set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。
使用keys指令可以扫出指定模式的key列表。
对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
这个时候你要回答redis关键的一个特性:
redis的单线程的。
keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。
这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。
答:一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
如果对方追问可不可以不用sleep呢?
答:list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。
如果对方追问能不能生产一次消费多次呢?
答:使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
如果对方追问pub/sub有什么缺点?
答:在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
如果对方追问redis如何实现延时队列?
我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。
答:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。
到这里,面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指,在椅子背后。
如果有大量的key需要设置同一时间过期,一般需要注意什么?
如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。
可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。
使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。
Redis可以使用主从同步,从从同步。
Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。
Redis的并发竞争问题,主要是发生在并发写竞争。
考虑到redis没有像db中的sql语句,update val = val + 10 where ...,无法使用这种方式进行对数据的更新。
假如有某个key = "price", value值为10,现在想把value值进行+10操作。
正常逻辑下,就是先把数据key为price的值读回来,加上10,再把值给设置回去。
如果只有一个连接的情况下,这种方式没有问题,可以工作得很好,
但如果有两个连接时,两个连接同时想对还price进行+10操作,就可能会出现问题了。
例如:两个连接同时对price进行写操作,同时加10,最终结果我们知道,应该为30才是正确。
考虑到一种情况:
T1时刻,连接1将price读出,目标设置的数据为10+10 = 20。
T2时刻,连接2也将数据读出,也是为10,目标设置为20。
T3时刻,连接1将price设置为20。
T4时刻,连接2也将price设置为20,则最终结果是一个错误值20。
解决方案
使用乐观锁的方式进行解决(成本较低,非阻塞,性能较高)
如何用乐观锁方式进行解决?
本质上是假设不会进行冲突,使用redis的命令watch进行构造条件。
相对 HTML4 , HTML5 最大的亮点是它为移动设备提供了一些非常有用的功能,使得 HTML5 具备了开发App的能力,
HTML5开发App 最大的好处就是跨平台、快速迭代和上线,节省人力成本和提高效率,
因此很多企业开始对传统的App进行改造,逐渐用H5代替Native,
到2015年的时候,市面上大多数App 或多或少嵌入都了H5 的页面。
jQuery是使用选择器($)选取DOM对象,对其进行赋值、取值、事件绑定等操作,
其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,
而数据和界面是在一起的。比如需要获取label标签的内容:
$("lable").val();,它还是依赖DOM元素的值。
Vue则是通过Vue对象将数据和View完全分离开来了。
对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的,
他们通过Vue对象这个vm实现相互的绑定。这就是传说中的MVVM。
近几年,混合模式移动应用的概念甚嚣尘上,受到了一些中小型企业的青睐,
究其原因,混合模式开发可以比传统移动开发节约大量的开发成本和人力成本。
Hybrid App(混合模式移动应用)是指介于web-app、native-app这两者之间的app,
兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势”。
说白了,如果走传统移动开发路线,公司业务覆盖多端,那么每个平台势必要请一个专属开发人员,
安卓要请一个前端开发,ios同理,那么人力成本则进行了翻倍,
同时,如果多端使用不同的代码,当有功能上的修改或者维护时,成本也是不可想象的。
试想如果开发者编写一套代码,可编译到iOS、Android、H5、小程序等多个平台,这绝对是省时省力的良好方案。
mvc 和 mvvm 其实区别并不大。都是一种设计思想。
主要就是
mvc 中 Controller 演变成 mvvm 中的 viewModel。
mvvm 主要解决了 mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
当 Model 频繁发生变化,开发者需要主动更新到 View 。
低耦合
视图(View)可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同的"View"上,
当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。
可重用性
你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 view 重用这段视图逻辑。 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用 Expression Blend 可以很容易设计界面并生成 xml 代码。
可测试。
界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写。
总共分为 8 个阶段
创建前/后:
在 beforeCreate 阶段,vue 实例的挂载元素 el 还没有。
载入前/后:
在 beforeMount 阶段,vue 实例的$el 和 data 都初始化了,但还是挂载之前为虚拟的 dom 节点,data.message 还未替换。在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染。
更新前/后:
当 data 变化时,会触发 beforeUpdate 和 updated 方法。 销毁前/后:在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已经解除了事件监听以及和 dom 的绑定,但是 dom 结构依然存在
销毁前/后
声明式(标签跳转)router-link
编程式( js 跳转) router.push(‘index’)
webpack 中提供了 require.ensure()来实现按需加载。
以前引入路由是通过 import 这样的方式引入,改为 const 定义的方式进行引入。
不进行页面按需加载引入方式: import home from ‘…/…/common/home.vue’
进行页面按需加载的引入方式: const home = r => require.ensure( [], () => r (require(’…/…/common/home.vue’)))
vue 框架中状态管理。在 main.js 引入 store,注入。
新建了一个目录 store,…… export 。
vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
具体步骤:
第一步:
需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter 这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化
第二步:
compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步:
Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情是:
在自身实例化时往属性订阅器(dep)里面添加自己 自身必须有一个 update()方法 待属性变动 dep.notice()通知时,能调用自身的 update() 方法,并触发 Compile 中绑定的回调,则功成身退。
第四步:
MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据 model 变更的双向绑定效果。