模块四 MySQL 考核
MySQL 考核
1.进程
# 一 什么是进程
进程:正在进行的一个过程或者说一个任务。而负责执行任务则是cpu。
单核+多道,实现多个进程的并发执行
# 二 进程与程序的区别
程序仅仅只是一堆代码而已,而进程指的是程序的运行过程。
需要强调的是:同一个程序执行两次,那也是两个进程,比如打开暴风影音,虽然都是同一个软件,但是可以播放两个视频
# 三 并发与并行
一 并发:是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发,(并行也属于并发)
二 并行:同时运行,只有具备多个cpu才能实现并行
# 四 同步\异步and阻塞\非阻塞(重点)
同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不会返回。按照这个定义,其实绝大多数函数都是同步调用。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。
异步:异步的概念和同步相对。当一个异步功能调用发出后,调用者不能立刻得到结果。当该异步功能完成后,通过状态、通知或回调来通知调用者。如果异步功能用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一 种很严重的错误)。如果是使用通知的方式,效率则很高,因为异步功能几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起(如遇到io操作)。函数只有在得到结果之后才会将阻塞的线程激活。有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
#举例:
#1. 同步调用:apply一个累计1亿次的任务,该调用会一直等待,直到任务返回结果为止,但并未阻塞住(即便是被抢走cpu的执行权限,那也是处于就绪态);
#2. 阻塞调用:当socket工作在阻塞模式的时候,如果没有数据的情况下调用recv函数,则当前线程就会被挂起,直到有数据为止。
非阻塞:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程。
小结:1. 同步与异步针对的是函数/任务的调用方式:同步就是当一个进程发起一个函数(任务)调用的时候,一直等到函数(任务)完成,而进程继续处于激活状态。而异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是继续往下执行当,函数返回的时候通过状态、通知、事件等方式通知进程任务完成。
2. 阻塞与非阻塞针对的是进程或线程:阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程
# 五 进程的创建(了解)
# 六 进程的终止(了解)
1. 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)
2. 出错退出(自愿,python a.py中a.py不存在)
3. 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)
4. 被其他进程杀死(非自愿,如kill -9)
# 七 进程的层次结构
无论UNIX还是windows,进程只有一个父进程,不同的是:
1. 在UNIX中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员。
2. 在windows中,没有进程层次的概念,所有的进程都是地位相同的,唯一类似于进程层次的暗示,是在创建进程时,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。
# 八 进程的状态
其实在两种情况下会导致一个进程在逻辑上不能运行,
1. 进程挂起是自身原因,遇到I/O阻塞,便要让出CPU让其他进程去执行,这样保证CPU一直在工作
2. 与进程无关,是操作系统层面,可能会因为一个进程占用时间过多,或者优先级等原因,而调用其他的进程去使用CPU。
因而一个进程由三种状态:
1.进程为等待输入而阻塞
2.调度程序选择另外一个程序
3.调度程序选择这个进程
4.出现有效输入
# 九 进程并发的实现(了解)
# 一 multiprocessing模块介绍
multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似
multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件
需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。
# 二 Process类的介绍
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍:
group参数未使用,值始终为None
target表示调用对象,即子进程要执行的任务
args表示调用对象的位置参数元组,args=(1,2,'egon',)
kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
name为子进程的名称
方法介绍:
p.start():启动进程,并调用该子进程中的p.run()
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
属性介绍:
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name:进程的名称
p.pid:进程的pid
p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
# 三 Process类的使用
创建并开启子进程的两种方式
- 开进程的方法一:
import time
import random
from multiprocessing import Process
def piao(name):
print('%s piaoing' %name)
time.sleep(random.randrange(1,5))
print('%s piao end' %name)
p1=Process(target=piao,args=('egon',)) #必须加,号
p2=Process(target=piao,args=('alex',))
p3=Process(target=piao,args=('wupeqi',))
p4=Process(target=piao,args=('yuanhao',))
p1.start()
p2.start()
p3.start()
p4.start()
print('主线程')
- 开进程的方法二:
import time
import random
from multiprocessing import Process
class Piao(Process):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
print('%s piaoing' %self.name)
time.sleep(random.randrange(1,5))
print('%s piao end' %self.name)
p1=Piao('egon')
p2=Piao('alex')
p3=Piao('wupeiqi')
p4=Piao('yuanhao')
p1.start() #start会自动调用run
p2.start()
p3.start()
p4.start()
print('主线程')
# 四 守护进程
# 五 进程同步(锁)
- 加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
虽然可以用文件共享数据实现进程间通信,但问题是:
1.效率低(共享数据基于文件,而文件是硬盘上的数据)
2.需要自己加锁处理
- 因此我们最好找寻一种解决方案能够兼顾:1、效率高(多个进程共享一块内存的数据)2、帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。
1 队列和管道都是将数据存放于内存中
2 队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,
我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。
# 六 队列(推荐使用)
进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的
创建队列的类(底层就是以管道和锁定的方式实现):
Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。
maxsize是队列中允许最大项数,省略则无大小限制。
q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False)
q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样
# 七 管道
# 八 共享数据
# 九 信号量(了解)
# 十 事件(了解)
# 十一 进程池
2.线程
线程是进程中单一连续的控制流程,是进程的一部分,一个进程至少有一个线程,多个线程可以属于同一个进程,且这些线程共享进程的内存空间,所以线程可以访问全局变量。
在python中,有两个包可以创建线程,thread与threading
加入join()方法是为了让线程可以正常结束,否则在多线程的时候可能会造成类似于僵尸进程的僵尸线程
多线程的实现,也就是同时创建多个线程,可以同时执行多个任务。
线程运行一共有三种状态,阻塞,就绪,运行中,再加上创建和终止,也就是线程一共有五种状态
阻塞:访问资源但是资源并未准备好
就绪:资源已经准备好了,等待调度
运行中:调度轮到该线程,然后运行线程代码
threading模块提供了一下集中锁机制
Lock互斥锁
互斥锁是一种独占锁,一个时刻只能有一个线程可以访问共享数据,使用需要先实例化一个锁对象,然后将锁当参数传入函数中,对不可分开的操作进行加锁即可
RLock可重入锁
Semaphore信号
Event事件
Condition条件
3.进程通讯方式
# process
process函数形式
继承Process,实现子类
# pool
进程池,当要创建的进程数量比较多时,建议使用该方式
# queue
进程间通信
使用process创建的进程,使用Queue()
使用进程池创建的进程通信,使用Manager().Queue()
1.Queue.qsize(): 返回当前队列包含的消息数量
2.Queue.empty(): 如果队列为空 返回True 反之 False
3.Queue.full(): 如果队列满了返回True 反之 False
4.Queue.get(): 获取队列中一条消息 然后将其从队列中移除 可传参数 超市时长
5.Queue.get_nowait(): 相当于 Queue.get(False) 取不到值 触发异常
6.Queue.put(): 将一个值添加到数列 可传参数 超时时长
7.Queue.put_nowait():相当于 Queue.get(False) 当队列满时 报错
协程
一 定义
1、进程
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,
进程是系统进行资源分配和调度的一个独立单位。
每个进程都有自己的独立内存空间,
不同进程通过进程间通信来通信。
由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
2、线程
线程是进程的一个实体, 是CPU调度和分派的基本单位,
它是比进程更小的能独立运行的基本单位.
线程自己基本上不拥有系统资源, 只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),
但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
3、协程
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,
在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
协程:自己创建出来,内部做IO监测,资源复用,IO多路复用是协程的方式之一
二、区别:
1、进程多与线程比较
线程是指进程内的一个执行单元, 也是进程内的可调度实体。线程与进程的区别:
1) 地址空间: 线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间
2) 资源拥有: 进程是资源分配和拥有的单位, 同一个进程内的线程共享进程的资源
3) 线程是处理器调度的基本单位, 但进程不是
4) 二者均可并发执行
5) 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
2、协程多与线程进行比较
1) 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。
2) 线程进程都是同步机制,而协程则是异步
3) 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
# 这里举例简单的三种方式生成协程
#第一种yield:
import time
def work1():
for i in range(5):
print('work1',i)
yield
time.sleep(1)
def work2():
for i in range(5):
print('work2',i)
yield
time.sleep(1)
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
#第二种:
'''greenlet包'''
from greenlet import greenlet
import time
def work1():
for i in range(5):
print('work1',i)
time.sleep(1)
g2.switch() #遇到耗时操作,手动切换其他操作,(现在切换了g2协程)
def work2():
for i in range(5):
print('work2', i)
time.sleep(1)
g1.switch()
# 创建多协程
g1 = greenlet(work1)
g2 = greenlet(work2)
#启动协程
g1.switch()
g2.switch()
#第三种:
'''gevent包'''
import gevent
from gevent import monkey #猴子补丁
monkey.patch_all() #给所有的耗时操作打上补丁,协程自动切换
import time
def work1():
for i in range(5):
print('work1',i)
time.sleep(1)
def work2():
for i in range(5):
print('work2', i)
time.sleep(1)
#创建多协程与 创建多进程\多线程 的过程差不多
g1 = gevent.spawn(work1) #创建协程1
g2 = gevent.spawn(work2) #创建协程2
g1.join() #等待协程操作完成
g2.join()
4.io多路复用
同步:提交一个任务之后要等待这个任务执行完毕
异步:只管提交任务,不等待这个任务执行完毕就可以做其它的事情
阻塞:例如:在socket中的这些recvfrom,recv,accept都会产生阻塞。
非阻塞:除去阻塞中的都是一般都是非阻塞。
基本分类:阻塞IO,非阻塞IO,IO多路复用,异步IO。
# IO多路复用:通过一种机制,可以监视多个描述符(socket),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
I/O多路复用是用于提升效率,单个进程可以同时监听多个网络连接IO
I/O是指Input/Output
I/O多路复用,通过一种机制,可以监视多个文件描述符,一旦描述符就绪(读就绪和写就绪),能通知程序进行相应的读写操作。
I/O多路复用避免阻塞在io上,原本为多进程或多线程来接收多个连接的消息变为单进程或单线程保存多个socket的状态后轮询处理.
5.select poll epoll
select维护一个文件描述符数据结构,单个进程使用有上限,通常是1024,线性扫描这个数据结构。效率低。
pool和select的区别是内部数据结构使用链表,没有这个最大限制,但是依然是线性遍历才知道哪个设备就绪了
epool使用事件通知机制,使用回调机制提高效率。
select/pool还要从内核空间复制消息到用户空间,而epoll通过内核空间和用户空间共享一块内存来减少复制
Windows Python: 提供: select
Mac Python:提供: select
Linux Python: 提供: select、poll、epoll
select,poll,epoll都是IO多路复用的机制,I/O多路复用就是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知应用程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
# select
# select是通过系统调用来监视一组由多个文件描述符组成的数组,通过调用select()返回结果,数组中就绪的文件描述符会被内核标记出来,然后进程就可以获得这些文件描述符,然后进行相应的读写操作
# select的实际执行过程如下:
select需要提供要监控的数组,然后由用户态拷贝到内核态
内核态线性循环监控数组,每次都需要遍历整个数组
内核发现文件描述符状态符合操作结果,将其返回
# poll
poll本质上与select基本相同,只不过监控的最大连接数上相较于select没有了限制,因为poll使用的数据结构是链表,而select使用的是数组,数组是要初始化长度大小的,且不能改变
poll原理
将fd列表,由用户态拷贝到内核态
内核态遍历,发现fd状态变为就绪后,返回fd列表
# epoll
epoll相当于是linux内核支持的方法,而epoll主要是解决select,poll的一些缺点
1.数组长度限制
解决方案:fd上限是最大可以打开文件的数目,具体数目可以查看/proc/sys/fs/file-max。一般会和内存有关
2.需要每次轮询将数组全部拷贝到内核态
解决方案:每次注册事件的时候,会把fd拷贝到内核态,而不是每次poll的时候拷贝,这样就保证每个fd只需要拷贝一次。
3.每次遍历都需要列表线性遍历
解决方案:不再采用遍历的方案,给每个fd指定一个回调函数,fd就绪时,调用回调函数,这个回调函数会把fd加入到就绪的fd列表中,所以epoll只需要遍历就绪的list即可。
6.gil
GIL:又叫全局解释器锁,每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行,目的是解决多线程同时竞争程序中的全局变量而出现的线程安全问题。
多线程下每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行
即使在多核CPU中,多线程同一时刻也只有一个线程在运行,这样不仅不能利用多核CPU的优势,反而由于每个线程在多个CPU上是交替执行的,导致在不同CPU上切换时造成资源的浪费,反而会更慢。即原因是一个进程只存在一把gil锁,当在执行多个线程时,内部会争抢gil锁,这会造成当某一个线程没有抢到锁的时候会让cpu等待,进而不能合理利用多核cpu资源。
GIL面试题参考答案:
Python语言和GIL没有什么关系。仅仅是由于历史原因在Cpython虚拟机(解释器),难以移除GIL。
GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行代码。
线程释放GIL锁的情况: 在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100。
Python使用多进程是可以利用多核的CPU资源的。
多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁。
7.数据库acid
# 事务
定义:所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
# ACID,是指在可靠数据库管理系统(DBMS)中,事务(transaction)所应该具有的四个特性:
原子性(Atomicity)、
一致性(Consistency)、
隔离性(Isolation)、
持久性(Durability).这是可靠数据库所应具备的几个特性.下面针对这几个特性进行逐个讲解.
# 原子性:原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。
# 一致性:一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
# 隔离性:多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。
并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。
事务之间的相互影响有四种:脏读,不可重复读,幻读,丢失更新
1.脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
2.不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
3.幻读是事务非独立执行时发生的一种现象。指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
4.丢失更新是指两个事务同时读取同一条记录,A先修改记录,B也修改记录(B是不知道A修改过),B提交数据后B的修改结果覆盖了A的修改结果。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)
事务隔离性的设置语句:
Serializable(串行化):可避免脏读、不可重复读、虚读情况的发生。
Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。
Read committed(读已提交):可避免脏读情况发生。
Read uncommitted(读未提交):最低级别,以上情况均无法保证。
# 持久性:意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
事务的(ACID)特性是由关系数据库管理系统(RDBMS,数据库系统)来实现的。数据库管理系统采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。
数据库管理系统采用锁机制来实现事务的隔离性。当多个事务同时更新数据库中相同的数据时,只允许持有锁的事务能更新该数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。
8.事务
# 事务
定义:所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
# 开启事务(start transaction)
# 提交事务(commit)
# 回滚事务(rollback)
9.跨表语法
left join student on score.student_id=student.sid
10.mysql存储引擎 & B+Tree
# MyISAM引擎
- MyISAM引擎,非聚簇索引(数据 和 索引结构 分开存储)
使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址,MyISAM的索引文件仅仅保存数据记录的地址
# InnoDB引擎
- InnoDB引擎,聚簇索引(数据 和 主键索引结构存储在一起)
InnoDB的数据文件本身就是索引文件,表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引
InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。
myisam 表锁: 整个表给锁了
innoDB 行锁: 行内容(常用)
# B+Tree
1.非叶子节点只存储键值信息。
2.所有叶子节点之间都有一个链指针。
3.数据记录都存放在叶子节点中。
11.索引是什么
在数据库中索引最核心的作用是:**加速查找**
索引的底层是基于B+Tree的数据结构存储的
- myisam引擎,非聚簇索引(数据 和 索引结构 分开存储)
- innodb引擎,聚簇索引(数据 和 主键索引结构存储在一起)
常见索引:
- 主键索引:加速查找、不能为空、不能重复。 + 联合主键索引
主键和联合主键索引
alter table 表名 add primary key(列名);
alter table 表名 drop primary key;
- 唯一索引:加速查找、不能重复。 + 联合唯一索引(只能有一个空值)
唯一和联合唯一索引
create unique index 索引名 on 表名(列名);
drop unique index 索引名 on 表名;
- 普通索引:加速查找。 + 联合索引
索引和联合索引
create index 索引名 on 表名(列名);
drop index 索引名 on 表名;
12.数据库查询
1、数据库服务器:运行数据库管理软件的计算机
2、数据库管理软件:mysql,oracle,db2,slqserver
3、库:文件夹
4、表:文件
5、记录:事物一系列典型的特征:egon,male,18,oldgirl
6、数据:描述事物特征的符号
SQL语句:
操作文件夹(库)
增
create database db1 charset utf8;
查
show create database db1;
show databases;
改
alter database db1 charset gbk;
删
drop database db1;
操作文件(表)
切换文件夹:use db1;
查看当前所在文件夹:select database();
增
create table t1(id int,name char);
查
show create table t1;
show tables;
desc t1;
改
alter table t1 modify name char(6);
alter table t1 change name NAME char(7);
删
drop table t1;
操作文件内容(记录)
增
insert t1(id,name) values(1,'egon1'),(2,'egon2'),(3,'egon3');
查
select id,name from db1.t1;
select * from db1.t1;
改
update db1.t1 set name='SB';
update db1.t1 set name='ALEX' where id=2;
删
delete from t1;
delete from t1 where id=2;
# 单表查询
select distinct 字段1,字段2,字段3 from 库.表
where 条件
group by 分组条件
having 过滤
order by 排序字段
limit n;
# 连表操作
内连接:只取两张表的共同部分
select * from employee inner join department on employee.dep_id = department.id ;
左连接:在内连接的基础上保留左表的记录
select * from employee left join department on employee.dep_id = department.id ;
右连接:在内连接的基础上保留右表的记录
select * from employee right join department on employee.dep_id = department.id ;
全外连接:在内连接的基础上保留左右两表没有对应关系的记录
select * from employee full join department on employee.dep_id = department.id ;
select * from employee left join department on employee.dep_id = department.id
union
select * from employee right join department on employee.dep_id = department.id ;
13 mysql优化的套路
# mysql优化的套路
mysql是否优化过直接体现了一个程序员的功底,mysql前期的选型以及表的设计已经决定优化的上限
- 业务选型(引擎方面)
innodb or myisam 如何选目前所接触到的互联网基本都是innodb(主要是锁的力度比较细MyISAM和InnoDB.note)
编码格式 utf8 or utf8mb4 做过聊天 使用utf8无法储存表情的时候就哭了
- 架构优化
主从分离(防止发生死锁,独占锁和共享锁的容易造成死锁影响效率 数据库死锁出现原因.note)
高并发状态下写数据时同步机制过慢(多线程手动同步可以吗 no 多个表操作统一数据不行 如何解决)
- 表优化
分库分表 hash 取除模还是取余(消息的 loginc/di 分离策略 数据库分表 是取模还是做除法.note)
动静分离(高频修改表尽可能简短,更新时候反写数据比较快,不经常更新的放在一个表里,消息计数表和消息表)
不使用外键关系 (全部是逻辑类外键)
尽可能不使用存储过程、触发器、视图、event(表迁移你会绝望的)
NOT NULL 设立默认值 (在用django是就体会到了坑) 手机号存储为varchar(20) (+86!!!!)
设计三范式 最小单元设计
- 索引建立
索引建立太多影响写的性能
频繁更新的字段在建立索引后 会对索引树产生影响
只做简单查询,特别复杂的尝试使用es这样的组件来解决问题
最左前缀的问题abc = a + ab + abc
索引还是降不下数据查询量 那就是结构设计问题
- 语句优化
join 字段类型不一样 会全表扫描
都说select count(*)慢 其实看引擎myisam直接存了条数
select * 太耗io
插入数据 精确到字段插入 (否则后期调整模型的时候很难受)
or 换成in
模糊查询别用
- 缓存
redis 做防护 防止缓存击穿 缓存雪崩,布隆过滤器兜底 最新的布谷鸟过滤器
- 数据库选型
在数据量大的时候 关系型数据和非关系的选择就比较重要
redis
levedb
mysql
mongo
14 其他
四模块
进程线程
进程通讯方式
io多路复用
select poll epoll
gil
数据库acid
事务
跨表语法
mysql存储引擎
索引是什么
数据库查询
第四模块
进程线程协程之间的定义以及区别是什么?
协程:自己创建出来,内部做IO监测,资源复用,IO多路复用是协程的方式之一
知识体系
进程线程之间的通讯方式有哪些?
共享内存
线程之间:共享,
进程之间:相互独立
Python解释器和线程之间静态数据 联想到 GIL锁
io多路复用的实现原理是什么?
监测多个文件描述符FD
I/O: 输入输出
计算机本身就有自己的一套I/O多路复用体系
select poll epoll 分别是什么,区别是什么?
模型思想
gil 是什么,为了解决什么问题
数据库acid,acid的定义
隔离级别:脏读
总结笔记
什么是事务,事务为了解决什么问题,事物的应用场景有哪些
mysql存储引擎有哪些,分别又什么区别(为什么要有这么多存储引擎)
数据库类型:要知道类型,总结
引擎原理
myisam 表锁: 整个表给锁了
innoDB 行锁: 行内容(常用)
索引是什么(mysql的索引有哪些,为什么要有索引)
B+tree 怎么维护,什么结构,怎么达到快速查找.
面向对象 - 封装后续慢慢理解,能应用就行
浙公网安备 33010602011771号