并发编程重点:
并发编程: 线程, 进程, 队列, IO 多路模型
操作系统工作原理介绍, 线程, 进程演化史, 特点, 区别, 互斥锁, 信号,
事件, join,GIL, 进程间通信, 管道, 队列.
生产者消息者模型, 异步模型, IO 多路复用模型, select\poll\epoll 高性
能 IO 模型源码实例解析, 高并发 FTP server 开发
1, 简述计算机操作系统的中断的作用
中断是指在计算机执行期间, 系统内发生任何非寻常的或非预期的急需处理事件,
使得 cpu 暂时中断当前正在执行的程序, 转去执行相应的事件处理程序. 待处理完毕
后又返回原来被中断处继续执行或调度新的进程执行的过程. 它使计算机可以更好更
快利用有限的系统资源解决系统响应速度和运行效率的一种控制技术. 实时响应, 系统调度.
2, 简述计算机内存的 "内核态" 和 "用户态"
内核态: cpu 可以访问内存的所有数据, 包括外围设备,
例如硬盘, 网卡, cpu 也可以将自己从一个程序切换到另一个程序.
用户态: 只能受限的访问内存, 且不允许访问外围设备, 占用 cpu 的能力被剥夺,
cpu 资源可以被其他程序获取
3, 为什么要有内核态和用户态?
由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据,
或者获取外围设备的数据, 并发送到网络
cpu 划分出两个权限等级: 用户态和内核态.
4, 什么是进程?
正在执行的一个程序或者说一个任务, 负责执行任务的是 cpu.
进程是用来把资源集中到一起的, 进程是资源单位, 或者说资源集合.
5, 什么是线程?
线程是 cpu 上的执行单位. 同一个进程内的多个线程共享该进程内的地址资源.
创建线程的开销要远小于创建进程的开销.
6, 什么是系统调用?
所有用户程序都是运行在用户态的, 但是有时候程序确实需要做一些内核态的事情,
例如从硬盘读取数据, 或者从键盘获取输入等,
而唯一能做这些事情的就是操作系统, 所以此时程序就需要向操作系统请求以程序
的名义来执行这些操作. 这时, 就需要一个机制: 用户态程序切换到内核态, 但是不能
控制在内核态中执行的指令. 这种机制就叫系统调用.
7,threading 模块 event 和 condition 的区别;
condition: 某些事件触发或达到特定的条件后才处理数据, 默认创建了一个 lock 对象.
- con = threading.Condition()
- con.acquire()
- con.notify()
- con.wait()
- con.release()
event: 其他线程需要通过判断某个线程的状态来确定自己的下一步操作, 就可以用 event.
- from threading import Event
- event = Event()
event.set(): 设置 event 的状态值为 True, 所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
event.is_set(): 返回 event 的状态值;
event.wait(): 如果 event.is_set()==False 将阻塞线程;
event.clear(): 恢复 event 的状态值为 False.
8, 进程间通信方式有哪些?
消息队列 管道 信号量 信号 共享内存 套接字
9, 简述对管道, 队列的理解
队列 = 管道 + 锁
- from multiprocessing import Queue,Process
- queue = Queue()
- queue.put(url)
- url = queue.get()
- from multiprocessing import Pipe,Process
- pipe = Pipe()
- pipe.send(url)
- pipe.recv()
10, 请简述你对 join,daemon 方法的理解, 举出它们在生产环境中的使用场景;
join: 等待一个任务执行完毕; 可以将并发变成串行.
daemon:
守护进程 (守护线程) 会等待主进程 (主线程) 运行完毕后被销毁.
运行完毕:
1. 对主进程来说, 运行完毕指的是主进程代码运行完毕.
2. 对主线程来说, 运行完毕指的是主线程所在的进程内所有非守
护线程统统运行完毕, 主线程才算运行完毕.
11, 简述 IO 多路复用模型的工作原理
IO 多路复用实际上就是用 select,poll,epoll 监听多个 io 对象, 当 io 对象有
变化 (有数据) 的时候就通知用户进程. 好处就是单个进程可以处理多个 socket.
1. 当用户进程调用了 select, 那么整个进程会被 block;
2. 而同时, kernel 会 "监视" 所有 select 负责的 socket;
3. 当任何一个 socket 中的数据准备好了, select 就会返回;
4. 这个时候用户进程再调用 read 操作, 将数据从 kernel 拷贝到用户进程.
总结:
1.I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,
而这些文件描述符 (套接字描述符) 其中的任意一个进入读就绪状态,
select()函数就可以返回.
2.IO 多路复用: 需要两个系统调用, system call (select 和 recvfrom),
而 blocking IO 只调用了一个 system call (recvfrom). 但是, 用 select 的优势
在于它可以同时处理多个 connection.
3. 如果处理的连接数不是很高的话, 使用 select/epoll 的 web server 不一定
比使用多线程 + 阻塞 IO 的 web server 性能更好, 可能延迟还更大.
4.select/epoll 的优势并不是对于单个连接能处理得更快, 而是在于能处理更多的连接.
12,threading 中 Lock 和 RLock 的相同点和不同点
Lock(): 互斥锁, 只能被 acquire 一次, 可能会发生死锁情况.
RLock(): 递归锁, 可以连续 acquire 多次.
RLock = Lock + counter
counter: 记录了 acquire 的次数, 直到一个线程所有的 acquire 都被 release, 其他线程才能获得资源.
13, 什么是 select, 请简述它的工作原理, 简述它的优缺点;
python 中的 select 模块专注于 I/O 多路复用, 提供了 select poll epoll 三个方法; 后两个在 linux 中可用,
windows 仅支持 select.
fd: 文件描述符
fd_r_list,fd_w_list,fd_e_list = select.select(rlist,wlist,xlist,[timeout])
参数: 可接受四个参数(前三个必须)
rlist: 等到准备好阅读
wlist: 等到准备写作
xlist: 等待 "异常情况"
超时: 超时时间
返回值: 三个列表
select 监听 fd 变化的过程分析:
用户进程创建 socket 对象, 拷贝监听的 fd 到内核空间, 每一个 fd 会对应一张系统文件表,
内核空间的 fd 响应到数据后,
就会发送信号给用户进程数据已到;
用户进程再发送系统调用, 比如 (accept) 将内核空间的数据 copy 到用户空间, 同时作
为接受数据端内核空间的数据清除,
这样重新监听时 fd 再有新的数据又可以响应到了(发送端因为基于 TCP 协议所以需要收到
应答后才会清除).
该模型的优点:
相比其他模型, 使用 select() 的事件驱动模型只用单线程 (进程) 执行, 占用资源少,
不消耗太多 CPU, 同时能够为多客户端提供服务.
如果试图建立一个简单的事件驱动的服务器程序, 这个模型有一定的参考价值.
该模型的缺点:
首先 select()接口并不是实现 "事件驱动" 的最好选择. 因为当需要探测的句柄值较大时,
select()接口本身需要消耗大量时间去轮询各个句柄.
很多操作系统提供了更为高效的接口, 如 linux 提供了 epoll,BSD 提供了 kqueue,
Solaris 提供了 / dev/poll,....
如果需要实现更高效的服务器程序, 类似 epoll 这样的接口更被推荐. 遗憾的是不同
的操作系统特供的 epoll 接口有很大差异,
所以使用类似于 epoll 的接口实现具有较好跨平台能力的服务器会比较困难.
其次, 该模型将事件探测和事件响应夹杂在一起, 一旦事件响应的执行体庞大, 则
对整个模型是灾难性的.
14, 什么是 epoll, 请简述它的工作原理, 简述它的优缺点;
epoll: 性能最好的多路复用 I/O 就绪通知方法. 相比于 select,epoll 最大的
好处在于它不会随着监听 fd 数目的增长而降低效率.
因为在内核中的 select 实现中, 它是采用轮询来处理的, 轮询的 fd 数目越多, 自然耗时越多.
epoll: 同样只告知那些就绪的文件描述符, 而且当我们调用 epoll_wait()获得就绪文件描述符时,
返回的不是实际的描述符, 而是一个代表就绪描述符数量的值,
你只需要去 epoll 指定的一个数组中依次取得相应数量的文件描述符即可, 这里也使用了内存映射 (mmap) 技术, 这样便彻底省掉了这些文件描述符在系统调用时复制的开销.
另一个本质的改进在于 epoll 采用基于事件的就绪通知方式. 在 select/poll 中, 进程只有在调用
一定的方法后, 内核才对所有监视的文件描述符进行扫描,
而 epoll 事先通过 epoll_ctl()来注册一个文件描述符, 一旦基于某个文件描述符就绪时, 内核会
采用类似 callback 的回调机制, 迅速激活这个文件描述符,
当进程调用 epoll_wait()时便得到通知. 从以上可知, epoll 是对 select,poll 模型的改进,
提高了网络编程的性能, 广泛应用于大规模并发请求的 C/S 架构中.
python 中的 epoll:
只适用于 unix/linux 操作系统
15, 简述 select 和 epoll 的区别;
select: 调用 select()时
1, 上下文切换转换为内核态
2, 将 fd 从用户空间复制到内核空间
3, 内核遍历所有 fd, 查看其对应事件是否发生
4, 如果没发生, 将进程阻塞, 当设备驱动产生中断或者 timeout 时间后, 将进程唤醒, 再次进行遍历
5, 返回遍历后的 fd
6, 将 fd 从内核空间复制到用户空间
select: 缺点
1, 当文件描述符过多时, 文件描述符在用户空间与内核空间进行 copy 会很费时
2, 当文件描述符过多时, 内核对文件描述符的遍历也很浪费时间
3,select 最大仅仅支持 1024 个文件描述符
epoll 很好的改进了 select:
1,epoll 的解决方案在 epoll_ctl 函数中. 每次注册新的事件到 epoll 句柄中时,
会把所有的 fd 拷贝进内核, 而不是在 epoll_wait 的时候重复拷贝. epoll 保证了每
个 fd 在整个过程中只会拷贝一次.
2,epoll 会在 epoll_ctl 时把指定的 fd 遍历一遍 (这一遍必不可少) 并为每个 fd
指定一个回调函数, 当设备就绪, 唤醒等待队列上的等待者时, 就会调用这个回调
函数, 而这个回调函数会把就绪的 fd 加入一个就绪链表.
epoll_wait 的工作实际上就是在这个就绪链表中查看有没有就绪的 fd.
3,epoll 对文件描述符没有额外限制.
16, 简述多线程和多进程的使用场景;
多进程用于计算密集型, 如金融分析; 利用多核实现并发.
多线程用于 IO 密集型, 如 socket, 爬虫, web.
17, 请分别简述 threading.Condition,threading.event,threading.semaphore 的使用场景;
condition: 某些事件触发或达到特定的条件后才处理数据.
event: 用来通知线程有一些事情已发生, 从而启动后继任务的开始.
semaphore: 为控制一个具有有限数量用户资源而设计.
18, 假设有一个名为 threading_test.py 的程序里有一个 li = [1, 2, 3, 4]的列表, 另有 a,b 两个函数分别往该列表中增加元素, a 函数需要修改 li 之前需要获得 threading.Lock 对象, b 函数不需要, 请问当线程 t1 执行 a 函数获取到 Lock 对象之后并没有 release 该对象的情况下, 线程 t2 执行 b 函是否可以修改 li, 为什么?
可以, 线程的数据是共享的, a 函数虽然上了锁, 没有释放. 由于 b 函数不需要上锁, 就可以访问资源.
19, 简述你对 Python GIL 的理解;
GIL(global interpreter lock)全局解释器锁
GIL 是 CPython 的一个概念, 本质是一把互斥锁, 将并发运行变成串行.
解释器的代码是所有线程共享的, 所以垃圾回收线程也有可能访问到解释器的代码去执行.
因此需要有 GIL 锁, 保证 python 解释器同一时间只能执行一个任务的代码.
GIL: 解释器级别的锁(保护的是解释器级别的数据, 比如垃圾回收的数据)
Lock: 应用程序的锁(保护用户自己开发的应用程序的数据)
20, 什么是同步 I/O, 什么是异步 I/O?
synchronous io: 做 "IO operation" 的时候会将 process 阻塞;"IO operation" 是指真实的 IO 操作
blocking IO,non-blocking IO,IO multiplexing 都属于 synchronous IO 这一类.
asynchronous io: 当进程发起 IO 操作之后, 就直接返回再也不理睬了, 直到 kernel 发送一个信号,
告诉进程说 IO 完成. 在这整个过程中, 进程完全没有被 block.
异步 io 的实现会负责把数据从内核拷贝到用户空间.
21, 什么是管道, 如果两个进程尝试从管道的同一端读写数据, 会出现什么情况?
管道: 是两个进程间进行单向通信的机制. 由于管道传递数据的单向性.
管道又称为半双工管道.
管道传递数据是单向性的, 读数据时, 写入管道应关闭. 写数据时, 读取管道应关闭.
22, 为什么要使用线程池 / 进程池?
对服务端开启的进程数或线程数加以控制, 让机器在一个自己可以承受的
范围内运行, 这就是进程池或线程池的用途.
23, 如果多个线程都在等待同一个锁被释放, 请问当该锁对象被释放的时候, 哪一个线程将会获得该锁对象?
这个由操作系统的调度决定.
24,import threading;s = threading.Semaphore(value=-1)会出现什么情况?
当 threading.Semaphore(1) 为 1 时, 表示只有一个线程能够拿到许可,
其他线程都处于阻塞状态, 直到该线程释放为止.
当然信号量不可能永久的阻塞在那里. 信号量也提供了超时处理机制.
如果传入了 -1, 则表示无限期的等待.
25, 请将二进制数 10001001 转化为十进制;
10001001 = 1*10^7 + 1*10^3 + 1* 10^0 = 10001001
26, 某进程在运行过程中需要等待从磁盘上读入数据, 此时该进程的状态将发生什么变化?
一个程序有三种状态: 运行态, 阻塞态, 就绪态;
遇到 IO 阻塞, 进程从运行态转到阻塞态, cpu 切走, 保存当前状态;
27, 请问 selectors 模块中 DefaultSelector 类的作用是什么;
IO 多路复用: select poll epoll
select: 列表循环, 效率低. windows 支持.
poll: 可接收的列表数据多, 效率也不高. linux 支持.
epoll: 效率最高 异步操作 + 回调函数. linux 支持.
selectors 模块:
sel=selectors.DefaultSelector()
自动根据操作系统选择 select/poll/epoll
28, 简述异步 I/O 的原理;
用户进程发起 read 操作之后, 立刻就可以开始去做其它的事.
而另一方面, 从 kernel 的角度, 当它受到一个 asynchronous read 之后,
首先它会立刻返回, 所以不会对用户进程产生任何 block.
然后, kernel 会等待数据准备完成, 然后将数据拷贝到用户内存,
当这一切都完成之后, kernel 会给用户进程发送一个 signal, 告诉它 read 操作完成了.
29, 请问 multiprocessing 模块中的 Value,Array 类的作用是什么? 举例说明它们的使用场景
python 多进程通信 Queue Pipe Value Array
queue 和 pipe 用来在进程间传递消息;
Value + Array 是 python 中共享内存映射文件的方法; 速度比较快.
30, 请问 multiprocessing 模块中的 Manager 类的作用是什么? 与 Value 和 Array 类相比, Manager 的优缺点是什么
Python multiprocessing.Manager(进程间共享数据)
Python 中进程间共享数据, 除了基本的 queue,pipe 和 value+array 外,
还提供了更高层次的封装. 使用 multiprocessing.Manager 可以简单地使用这些高级接口.
Manager 支持的类型有 list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,
Event,Queue,Value 和 Array.
31, 请说说你对 multiprocessing 模块中的 Queue().put(), Queue().put_nowait(), Queue().get(), Queue().get_nowait()的理解;
q = Queue(3) 队列 先进先出 进程间通信; 队列 = 管道 + 锁
- q.put()
- q.put_nowait() # 无阻塞, 当队列满时, 直接抛出异常 queue.Full
- q.get()
- q.get_nowait() # 无阻塞, 当队列为空时, 直接抛出异常 queue.Empty
32, 什么是协程? 使用协程与使用线程的区别是什么?
协程: 单线程下的并发. 协程是一种用户态的轻量级线程, 即协程是由用户程序自己控制调度的.
1.python 的线程是属于内核级别的, 即由操作系统控制调度(如单线程
遇到 io 或执行时间过长就会被迫交出 cpu 执行权限, 切换其他的线程运行)
2. 单线程内开启协程, 一旦遇到 io, 就会从应用程序级别(而非操作系统)
控制切换, 以此来提升效率(!! 非 io 操作的切换与效率无关)
33,asyncio 的实现原理是什么?
https://www.cnblogs.com/earendil/p/7411115.html
Python 异步编程: asyncio 库和 async/await 语法
asyncio 是 Python 3.4 试验性引入的异步 I/O 框架, 提供了基于协程做异
步 I/O 编写单线程并发代码的基础设施.
其核心组件有事件循环(Event Loop), 协程(Coroutine), 任务(Task),
未来对象 (Future) 以及其他一些扩充和辅助性质的模块.
synchronous io: 做 "IO operation" 的时候会将 process 阻塞;"IO operation" 是指真实的 IO 操作
blocking IO,non-blocking IO,IO multiplexing 都属于 synchronous IO 这一类.
asynchronous io: 当进程发起 IO 操作之后, 就直接返回再也不理睬了, 直到 kernel 发送一个信号,
告诉进程说 IO 完成. 在这整个过程中, 进程完全没有被 block. 异步 io 的实现会负责把
数据从内核拷贝到用户空间
数据库重点:
, 数据库介绍, 类型, 特性
,MySQL 数据库安装, 连接, 启动, 停止
, 表字段类型介绍, 主键约束, 表创建语句
, 常用增删改查语句, 分组, 聚合
, 外键管理, unique 字段, 表结构修改语法
, 跨表查询, inner join,left join,right join,full join 语法
, 复杂 SQL 语句如 group by, 子查询, 函数的使用
, 索引原理及作用, 普通索引, 多列索引, 唯一索引, 全文索引等
, 基于 hash&b + 树索引的实现原理, 索引的优缺点剖析
, 事务原理, ACID 特性, 应用场景讲解
, 事务回滚
, 触发器的特性, 应用场景
, 触发器的增删改查方法
, 存储过程的作用及应用场景
, 创建存储过程, 参数传递, 流程控制语句 if\while\repeat\loop 等, 动态 SQL 的创建
, 视图的作用及使用场景, 视图的增删改查
, 数据库权限管理, 用户管理
, 数据库备份命令及工具讲解
, 基于不同业务的数据库表结构设计, 性能优化案例
,pymysql 模块介绍和使用
1, 说说你所知道的 MySQL 数据库存储引擎, InnoDB 存储引擎和 MyISM 存储引擎的区别?
主要有
MyISM:MyISAM 存储引擎: 不支持事务, 也不支持外键,
优势是访问速度快, 对事务完整性没有 要求或者以 select,insert 为主的
应用基本上可以用这个引擎来创建表
InnoDB: 支持事务
Memory:Memory 存储引擎使用存在于内存中的内容来创建表. 每个 memory 表
只实际对应一个磁盘文件, 格式是. frm.memory 类型的表访问非常的快, 因为它
的数据是放在内存中的, 并且默认使用 HASH 索引, 但是一旦服务关闭, 表中的数
据就会丢失掉.
Merge:Merge 存储引擎是一组 MyISAM 表的组合, 这些 MyISAM 表必须结构完全
相同, merge 表本身并没有数据, 对 merge 类型的表可以进行查询, 更新, 删除操作,
这些操作实际上是对内部的 MyISAM 表进行的.
BLACKHOLE: 黑洞存储引擎, 可以应用于主备复制中的分发主库.
MyISM 和 InnoDB 的区别
InnoDB 支持事务, 而 MyISM 不支持事务
InnoDB 支持行级锁, 而 MyISM 支持表级锁
InnoDB 支持外键, 而 MyISM 不支持
InnoDB 支持全文索引, 而 MyISM 不支持
InnoDB 是索引组织表, MyISM 是堆表 (堆表的数据是随机插入的, 索引组织表的数据是有序的)
2,MySQL 中 char 和 varchar 的区别, varchar(50)和 char(50)分别代表什么意思?
char(50): 定长, 字符的长度为 50, 浪费空间, 存取速度快, 数据不足时,
会往右填充空格来满足长度.
varchar(50): 变长, 字符的长度为 50, 节省空间, 存取速度慢, 存储数据
的真实内容, 不会填充空格, 且会在真实数据前加 1-2bytes, 表示真实数据的 bytes 字节数.
3,MySQL 中 int 类型存储多少个字节?
int 存储 4 字节 有符号:(-2147483648,2147483647)
无符号:(0,4294967295)
4, 主键具有什么特征?
不为空且唯一
5, 简述你对 inner join,left join,right join,full join 的理解;
多表连接查询:
inner join: 内连接, 只连接匹配的行, 找两张表共有的部分;
left join: 外连接之左连接, 优先显示左表全部记录, 在内连接的基础上增加左表有右表没有的结果;
right join: 外连接之右连接, 优先显示右表全部记录, 在内连接的基础上增加右表有左表没有的结果;
full join: = left join on union right join on ... mysql 不支持 full join 但是可以用 union ...
全外连接, 显示左右两个表全部记录, 在内连接的基础上增加左表有右表没有和右表有左表没有的结果;
6,concat, group_concat 函数的作用是什么?
定义显示格式:
concat() 用于连接字符串
eg: select concat('姓名:',name,'年薪:',salasy*12) as annual_salary from employee;
concat_ws() 第一个参数为分隔符
eg: select concat_ws(':',name,salary*12) as annual_salary from employee;
group by 与 group_concat() 函数一起使用
select post,group_concat(name) as emp_members from employee group by post;
7, 请介绍事务的实现原理;
事务: 用于将某些操作的多个 sql 作为原子性操作, 一旦有某一个出现错误,
即可回滚到原来的状态, 从而保证数据库数据的完整性.
原子性: 一堆 sql 语句, 要么同时执行成功, 要么同时失败!
8, 索引的本质是什么? 索引有什么优点, 缺点是什么?
索引是帮助 MySQL 高效获取数据的数据结构. 因此, 索引的本质是一种数据结构.
在数据之外, 数据库系统还可以维护满足特定查找算法的数据结构, 这些数据结构
以某种方式指向真实数据, 这样就可以在这些数据结构上实现高级查找算法, 这种
数据结构就是索引.
优点:
1, 提高数据检索效率, 降低数据库的 IO 成本;
2, 通过索引对数据进行排序, 降低了数据排序的成本, 降低了 CPU 的利用率;
缺点:
1, 索引实际上也是一张表, 索引会占用一定的存储空间;
2, 更新数据表的数据时, 需要同时维护索引表, 因此, 会降低 insert,update,delete 的速度;
9, 哪些情况下需要创建索引, 哪些情况下不需要创建索引?
, 主键自动创建唯一非空索引;
, 频繁作为查询条件的字段应该创建索引;
, 频繁更新的字段不适合简历索引, 因为每次更新不仅仅更新数据表同时还会更新索引表;
, 查询中经常排序的字段, 可以考虑创建索引;
, 如果某个字段的重复数据较多, 不适合创建普通索引;
10, 请分别介绍 ACID 代表的意思, 什么业务场景需要支持事务, 什么业务场景不需要支持事务?
ACID, 指数据库事务正确执行的四个基本要素的缩写.
包含: 原子性(Atomicity), 一致性(Consistency), 隔离性(Isolation), 持久性(Durability).
一个支持事务 (Transaction) 的数据库, 必须要具有这四种特性, 否则在事务过程 (Transaction processing) 当中无法保证数据的正确性.
使用场景:
银行的交易系统
- eg:
- start transaction;
- update user set balance = 900 where name = 'wsb'; #买支付 100 元
- update user set balance = 1010 where name = 'egon'; #中介拿走 10 元
- uppdate user set balance = 1090 where name = 'ysb'; #卖家拿到 90 元, 出现异常没有拿到
- rollback;
- commit;
11, 什么是触发器, 请简述触发器的使用场景?
使用触发器可以定制用户对表进行[增, 删, 改] 操作时前后的行为, 注意: 没有查询.
触发器无法由用户直接调用, 而知由于对表的[增 / 删 / 改] 操作被动引发的.
- eg:
- create trigger tri_before_insert_tb1 before insert on tb1 for each row
- begin
- ...
- end
12, 什么是存储过程, 存储过程的作用是什么?
存储过程包含了一系列可执行的 sql 语句, 存储过程存放于 MySQL 中,
通过调用它的名字可以执行其内部的一堆 sql.
优点:
1. 用于替代程序写的 SQL 语句, 实现程序与 sql 解耦
2. 基于网络传输, 传别名的数据量小, 而直接传 sql 数据量大
缺点:
1. 程序员扩展功能不方便
- eg:
- delimiter //
- create procedure p1()
- begin
- select * from blog;
- insert into blog(name,sub_time) values('xxx',now());
- end //
- delimiter ;
13, 什么是视图, 简单介绍视图的作用和使用场景?
视图是一个虚拟表(非真实存在), 其本质是[根据 SQL 语句获取动态的数据集, 并为其命名] ,
用户使用时只需使用[名称] 即可获取结果集, 可以将该结果集当做表来使用.
视图取代复杂的 sql 语句, 方便用来查询.
- eg:
- create view teacher_view as select tid from teacher where tname='李平老师';
14, 如何查看 SQL 语句的执行计划?
http://blog.itpub.net/12679300/viewspace-1394985/
执行计划的查看是进行数据库的 sql 语句调优时依据的一个重要依据.
- eg:
- explain select * from class;
- +----+-------------+-------+------+---------------+------+---------+------+------+-------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+------+---------------+------+---------+------+------+-------+
- | 1 | SIMPLE | class | ALL | NULL | NULL | NULL | NULL | 12 | NULL |
- +----+-------------+-------+------+---------------+------+---------+------+------+-------+
Id: 包含一组数字, 表示查询中执行 select 子句或操作表的顺序;
执行顺序从大到小执行;
当 id 值一样的时候, 执行顺序由上往下;
Select_type: 表示查询中每个 select 子句的类型(简单 OR 复杂), 有以下几种:
SIMPLE: 查询中不包含子查询或者 UNION
PRIMARY: 查询中若包含任何复杂的子部分, 最外层查询则被标记为 PRIMARY
SUBQUERY: 在 SELECT 或 WHERE 列表中包含了子查询, 该子查询被标记为 SUBQUERY
DERIVED: 在 FROM 列表中包含的子查询被标记为 DERIVED(衍生)
若第二个 SELECT 出现在 UNION 之后, 则被标记为 UNION;
若 UNION 包含在 FROM 子句的子查询中, 外层 SELECT 将被标记为: DERIVED
从 UNION 表获取结果的 SELECT 被标记为: UNION RESULT
Type: 表示 MySQL 在表中找到所需行的方式, 又称 "访问类型", 常见有以下几种:
ALL:Full Table Scan, MySQL 将进行全表扫描;
index:Full Index Scan,index 与 ALL 区别为 index 类型只遍历索引树;
range:range Index Scan, 对索引的扫描开始于某一点, 返回匹配值域的行, 常见于 between,<,>等的查询;
ref: 非唯一性索引扫描, 返回匹配摸个单独值的所有行. 常见于使用非唯一索引或唯一索引的非唯一前缀进行的查找;
eq_ref: 唯一性索引扫描, 对于每个索引键, 表中只有一条记录与之匹配. 常见于主键或唯一索引扫描
const,system: 当 MySQL 对查询某部分进行优化, 并转换为一个常量时, 使用这些类型访问. 如将主键置于 where 列表中, MySQL 就能将该查询转换为一个常量
NULL:MySQL 在优化过程中分解语句, 执行时甚至不用访问表或索引
possible_keys: 指出 MySQL 能使用哪个索引在表中找到行, 查询涉及到的字段上若存在索引, 则该索引将被列出, 但不一定被查询使用;
key: 显示 MySQL 在查询中实际使用的索引, 若没有使用索引, 显示为 NULL. 当查询中若使用了覆盖索引, 则该索引仅出现在 key 列表中
key_len: 表示索引中使用的字节数, 可通过该列计算查询中使用的索引的长度
ref: 表示上述表的连接匹配条件, 即那些列或常量被用于查找索引列上的值;
rows: 表示 MySQL 根据表统计信息及索引选用情况, 估算的找到所需的记录所需要读取的行数;
Extra: 包含不适合在其他列中显示但十分重要的额外信息;
Using where: 表示 MySQL 服务器在存储引擎受到记录后进行 "后过滤"(Post-filter), 如果查询未能使用索引, Using where 的作用只是提醒我们 MySQL 将用 where 子句来过滤结果集
Using temporary: 表示 MySQL 需要使用临时表来存储结果集, 常见于排序和分组查询;
Using filesort:MySQL 中无法利用索引完成的排序操作称为 "文件排序";
15, 在你本地数据库中查看 select from student * 的执行计划, 并解释每个字段分别代表什么意思?
- mysql> explain select * from student;
- +----+-------------+---------+------+---------------+------+---------+------+------+-------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+---------+------+---------------+------+---------+------+------+-------+
- | 1 | SIMPLE | student | ALL | NULL | NULL | NULL | NULL | 16 | NULL |
- +----+-------------+---------+------+---------------+------+---------+------+------+-------+
id: 表示查询中执行 select 子句或操作表的顺序.
select_type: simple 表示查询中不包含子查询或者 union
table: student
type: all 表示 mysql 将进行全表扫描
possible_keys: 指出 MySQL 能使用哪个索引在表中找到行, 查询涉及到的字段上若存在索引, 则该索引将被列出, 但不一定被查询使用;
key: 显示 MySQL 在查询中实际使用的索引, 若没有使用索引, 显示为 NULL. 当查询中若使用了覆盖索引, 则该索引仅出现在 key 列表中;
ey_len: 表示索引中使用的字节数, 可通过该列计算查询中使用的索引的长度;
ref: 表示上述表的连接匹配条件, 即那些列或常量被用于查找索引列上的值;
rows: 表示 MySQL 根据表统计信息及索引选用情况, 估算的找到所需的记录所需要读取的行数;
Extra: 包含不适合在其他列中显示但十分重要的额外信息;
16, 数据备份分为哪几种类型? 增量备份和差异备份的区别是什么?
完整备份: 备份系统中的所有数据. 特点: 占用空间大, 备份速度慢, 但恢复时一次恢复到位, 恢复速度快.
增量备份: 只备份上次备份以后有变化的数据.
特点: 因每次仅备份自上一次备份 (注意是上一次, 不是第一次) 以来有变化的文件,
所以备份体积小, 备份速度快, 但是恢复的时候, 需要按备份时间顺序,
逐个备份版本进行恢复, 恢复时间长.
差异备份: 只备份上次完全备份以后有变化的数据.
特点: 占用空间比增量备份大, 比完整备份小, 恢复时仅需要恢复第一个完
整版本和最后一次的差异版本, 恢复速度介于完整备份和增量备份之间.
简单的讲, 完整备份就是不管三七二十一, 每次都把指定的备份目录完整的复制一遍,
不管目录下的文件有没有变化;
增量备份就是每次将之前 (第一次, 第二次, 直到前一次) 做过备份之后有变化的文件进行备份;
差异备份就是每次都将第一次完整备份以来有变化的文件进行备份.
17, 请介绍 select 语句的执行顺序;
- from
- where
- group by
- having
- select
- distinct
- order by
- limit
说明:
1. 找到表: from
2. 拿着 where 指定的约束条件, 去文件 / 表中取出一条条记录
3. 将取出的一条条记录进行分组 group by, 如果没有 group by, 则整体作为一组
4. 将分组的结果进行 having 过滤
5. 执行 select
6. 去重
7. 将结果按条件排序: order by
8. 限制结果的显示条数
18, 请问存储引擎 MyISM 和 InnoDB 的适合什么样的使用场景?
Innodb 与 Myisam 引擎的区别与应用场景:
1. 区别:
(1)事务处理:
MyISAM 是非事务安全型的, 而 InnoDB 是事务安全型的(支持事务处理等高级处理);
(2)锁机制不同:
MyISAM 是表级锁, 而 InnoDB 是行级锁;
(3)select ,update ,insert ,delete 操作:
MyISAM: 如果执行大量的 SELECT,MyISAM 是更好的选择
InnoDB: 如果你的数据执行大量的 INSERT 或 UPDATE, 出于性能方面的考虑, 应该使用 InnoDB 表
(4)查询表的行数不同:
MyISAM:select count(*) from table,MyISAM 只要简单的读出保存好的行数, 注意的是, 当 count(*)语句包含 where 条件时, 两种表的操作是一样的
InnoDB : InnoDB 中不保存表的具体行数, 也就是说, 执行 select count(*) from table 时, InnoDB 要扫描一遍整个表来计算有多少行
(5)外键支持:
mysiam 表不支持外键, 而 InnoDB 支持
2. 为什么 MyISAM 会比 Innodb 的查询速度快.
INNODB 在做 SELECT 的时候, 要维护的东西比 MYISAM 引擎多很多;
1)数据块, INNODB 要缓存, MYISAM 只缓存索引块, 这中间还有换进换出的减少;
2)innodb 寻址要映射到块, 再到行, MYISAM 记录的直接是文件的 OFFSET, 定位比 INNODB 要快
3)INNODB 还需要维护 MVCC 一致; 虽然你的场景没有, 但他还是需要去检查和维护
MVCC ( Multi-Version Concurrency Control )多版本并发控制
3. 应用场景
MyISAM 适合:(1)做很多 count 的计算;(2)插入不频繁, 查询非常频繁;(3)没有事务.
InnoDB 适合:(1)可靠性要求比较高, 或者要求事务;(2)表更新和查询都相当的频繁, 并且行锁定的机会比较大的情况.
19, 请举出 MySQL 中常用的几种数据类型;
mysql 常用数据类型:
1. 数值类型:
整数类型: tinyint smallint int bigint
浮点型: float double decimal
float : 在位数比较短的情况下不精准(一般 float 得精确度也够用了)
double : 在位数比较长的情况下不精准
0.000001230123123123
存成: 0.000001230000
decimal:(如果用小数, 则推荐使用 decimal)
精准 内部原理是以字符串形式去存
2. 日期类型:
最常用: datetime year date time datetime timestamp
3. 字符串类型:
char(6) varchar(6)
char(10): 简单粗暴, 浪费空间, 存取速度快, 定长;
root 存成 root000000
varchar: 精准, 节省空间, 存取速度慢, 变长;
sql 优化: 创建表时, 定长的类型往前放, 变长的往后放
比如性别 比如地址或描述信息
>255 个字符, 超了就把文件路径存放到数据库中.
比如图片, 视频等找一个文件服务器, 数据库中只存路径或 url.
4. 枚举类型与集合类型:
- enum('male','female')
- set('play','music','read','study')
20, 什么情况下会产生笛卡尔乘积, 如何避免?
交叉连接: 不适用任何匹配条件. 生成笛卡尔积;
select * from employee,department;
避免:
select
employee.id,employee.name,employee.age,employee.sex,department.name
- from
- employee,department
- where
- employee.dep_id=department.id;
21, 请列举 MySQL 中常用的函数;
聚合函数:
聚合函数聚合的是组的内容, 若是没有分组, 则默认一组
- count()
- max()
- min()
- avg()
- sum()
22, 请说明 group by 的使用场景;
什么是分组, 为什么要分组?
1, 首先明确一点: 分组发生在 where 之后, 即分组是基于 where 之后得到的记录而进行的
2, 分组指的是: 将所有记录按照某个相同字段进行归类, 比如针对员工信息表的职位分组,
或者按照性别进行分组等
3, 为何要分组呢?
取每个部门的最高工资
取每个部门的员工数
取男人数和女人数
小窍门:'每'这个字后面的字段, 就是我们分组的依据
4, 大前提:
可以按照任意字段分组, 但是分组完毕后, 比如 group by post, 只能查看 post 字段,
如果想查看组内信息, 需要借助于聚合函数
23, 请介绍 hash 索引和 B + 树索引的实现原理;
哈希索引基于哈希表实现, 只有精确匹配索引的所有列的查询才有效.
对于每一行数据, 存储引擎都会对所有的索引列计算一个哈希码, 哈希码
是一个较小的值, 并且不同键值的行计算出来的哈希码也不一样. 哈希索
引将所有的哈希码存储在索引中, 同时在哈希表中保存指向每个数据行的
指针. 也就是说, 由于哈希查找比起 B-Tree 索引, 其本身对于单行查询的
时间复杂度更低, 有了哈希索引后明显可加快单行查询速度.
但是哈希索引也有它自己的限制:
哈希索引只包含哈希值和行指针, 而不存储字段值, 所以不能使用索引中
的值来避免读取行. 不过, 访问内存中的行的速度很快, 所以大部分情况下
这一点对性能的影响并不明显.
哈希索引数据并不是按照索引值顺序存储的, 所以也就无法用于排序.
哈希索引也不支持部分索引列匹配查找, 因为哈希索引始终是使用索引列的
全部内容来计算哈希值的. 例如, 在数据列 (A, B) 上建立哈希索引, 如果查
询只有数据列 A, 则无法使用该索引.
哈希索引只支持等值比较查询, 包括 =,in(),<=>. 不支持任何范围查询,
例如 where price> 100.
访问哈希索引的数据非常快, 除非有很多哈希冲突. 如果哈希冲突很多的话,
一些索引维护操作的代价也很高.
B + 树索引是 B 树索引的变体, 本质上也是多路平衡查找树
来源: https://www.cnblogs.com/wj-1314/p/9152658.html