网编并发数据库进阶篇
网编并发数据库进阶篇
第一部分 必答题
-
简述 OSI 7层模型及其作用?(2分)
OSI的7层从上到下分别是:7应用层;6表示层;5会话层;4传输层;3网络层;2数据链路层;1物理层. 7应用层 应用层确定进程之间通信的性质以满足用户需要以及提供网络与用户应用 6表示层 数据的压缩和解压缩,加密和解密等工作都由表示层负责 5会话层 它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制 4传输层 提供建立、维护和取消传输连接的功能,负责可靠地传输数据 3网络层 选择合适的网间路由和交换结点,确保数据及时传送。网络层将数据链路层提供的帧组成数据包,包中封装有网 络层包头,其中含有逻辑地址信息--源站点和目的站点地址的网络地址。 2数据链路层 负责在两个相邻结点间的线路上,无差错的传送以帧为单位的数据 要负责建立、维持和释放数据链路的连接 1物理层 物理层的任务就是为它的上一层提供一个物理连接 底层为1-4层,是面向通信的,高层为5-7层,是面向信息处理的
-
简述 TCP三次握手、四次回收的流程。(3分)
三次握手: 第一次握手:客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认; 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。 四次挥手 与建立连接的“三次握手”类似,断开一个TCP连接则需要“四次握手”。 第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。 第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。 第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。 第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
最简单的理解 一:建立TCP连接:三次握手协议 客户端:我要对你讲话,你能听到吗; 服务端:我能听到;而且我也要对你讲话,你能听到吗; 客户端:我也能听到。 ……. 互相开始通话 …….. 二:关闭TCP连接:四次握手协议 客户端:我说完了,我要闭嘴了; 服务端:我收到请求,我要闭耳朵了; (客户端收到这个确认,于是安心地闭嘴了。) ……. 服务端还没倾诉完自己的故事,于是继续唠唠叨叨向客户端说了半天,直到说完为止 ……. 服务端:我说完了,我也要闭嘴了; 客户端:我收到请求,我要闭耳朵了;(事实上,客户端为了保证这个确认包成功送达,等待了两个最大报文生命周期后,才闭上耳朵。) (服务端收到这个确认,于是安心地闭嘴了。)
-
TCP和UDP的区别?(3分)
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接 2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付 Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。 3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。 4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信 5、TCP对系统资源要求较多,UDP对系统资源要求较少。
-
什么是黏包?(2分)
1.由于你设置的接收大小小于你收到的消息的大小,那么剩余的消息的部分会和下一次接收一次被接收到 2.快速连续发送两个很小的消息,两个小消息会被合到一起被一次接收拿到。本质上就是因为接收端不知道发送端发送消息的大小导致的。 解决办法:在发送消息之前,先将消息大小发送过去,接收端按照消息大小来接收
-
什么 B/S 和 C/S 架构?(2分)
1、B/S架构是针对C/S架构缺点进行改进后提出的网络结构模式。 B/S结构属于C/S结构,是一种特殊的C/S,因为浏览器只是特殊的客户端。 2、C/S可以使用任何通信协议,而B/S架构规定必须实现HTTP协议。
-
请实现一个简单的socket编程(客户端和服务端可以进行收发消息)(3分)
rom socket import * #获取socket构造及常量 myHost = 'localhost' #服务器 myPort = 50007 #端口 sockobj = socket(socket.AF_INET,socket.SOCK_STREAM) #设置一个TCP socket对象 sockobj.bind((myHost,myPort)) #绑定它至端口号 sockobj.listen(5) #监听,允许5个连结 while True: #直到进程结束时才结束循环 connection,address = sockobj.accept() #等待下一个客户端连结 print 'server connected by',address #连结是一个新的socket while True: data = connection.recv(1024) #读取客户端套接字的下一行 if not data: #如果没有数量的话,那么跳出循环 break connection.send('Echo=>'+data) #发送一个回复至客户端 connection.close() # try: # connection.settimeout(5) # buf = connection.recv(1024) # # if buf == '1': # connection.send('welcome to server!') # else: # connection.send('please go out!') # except socket.timeout: # print 'time out!' # connection.close() client代码: import socket # import time import sys serverHost = 'localhost' serverPort = 50007 message = ['hello network world'] if len(sys.argv) > 1: serverHost = sys.argv[1] #如果参数大于1的话,连结的服务端为第一个参数 if len(sys.argv) > 2: message = sys.argv[2:] #如果参数大于2的话,连结的文字为第二个参数 sockobj = socket(socket.AF_INET,socket.SOCK_STREAM) sockobj.connect((serverHost,serverPort)) # time.sleep(2) # sock.send('1') # print sock.recv(1024) for line in message: sockobj.send(line) #经过套按字发送line至服务端 data = sockobj.recv(1024) #从服务端接收到的数据,上限为1k print 'Client received:',repr(data) #确认他是引用的,是'x' sockobj.close() #关闭套接字
-
简述进程、线程、协程的区别?(3分)
1.进程是计算器最小资源分配单位 进程切换需要的资源很最大,效率很低 2.线程是CPU调度的最小单位 . 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下) . 3.协程切换任务资源很小,效率高(协程本身并不存在,是程序员通过控制IO操作完成) .
-
什么是GIL锁?(2分)
为了线程安全,也就是为了解决多线程之间的数据完整性和状态同步而加的锁
-
进程之间如何进行通信?(2分)
信号、信号量、消息队列、共享内存
-
Python如何使用线程池、进程池?(2分)
进程池与线程池的作用 保证在硬件允许的范围内创建(进程/线程)的数量 from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor import time # ProcessPoolExecutor(5) # 5代表只能开启5个进程 # ProcessPoolExecutor() # 默认以CPU的个数限制进程数 pool = ThreadPoolExecutor(5) # 5代表只能开启5个线程 -5 +1 -1 +1 -1 # ThreadPoolExecutor() # 默认以CPU个数 * 5 限制线程数 # t = Tread() # 异步提交 # t.start(0) # pool.submit('传函数地址') # 异步提交任务 # def task(): # print('线程任务开始了...') # time.sleep(1) # print('线程任务结束了...') # # # for line in range(5): # pool.submit(task) # 异步提交任务 # pool.submit('传函数地址').add_done_callback('回调函数地址') def task(res): # res == 1 print('线程任务开始了...') time.sleep(1) print('线程任务结束了...') return 123 # 回调函数 def call_back(res): print(type(res)) # 注意: 赋值操作不要与接收的res同名 res2 = res.result() print(res2) for line in range(5): pool.submit(task, 1).add_done_callback(call_back) # 会让所有线程池的任务结束后,才往下执行代码 # pool.shutdown() print('hello')
-
请通过yield关键字实现一个协程? (2分)
def n1k(): i = 0 while i < 1000: yield i i += 1 ################################################### # 调用函数,得到生成器 gen = n1k() print gen # <generator object n1k at 0x00000000033344C0> # 调用生成器 for i in gen: print i # 1,2,3,4,......,999
-
什么是异步非阻塞? (2分)
同步和异步 同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO 操作并等待或者轮询的去查看IO 操作是否就绪,而异步是指用户进程触发IO 操作以后便开始做自己的事情,而当IO 操作已经完成的时候会得到IO 完成的通知。 阻塞和非阻塞 阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作方法的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入方法会立即返回一个状态值。
-
什么是死锁?如何避免?(2分)
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。 当然死锁的产生是必须要满足一些特定条件的: 1.互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放 2.请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。 3.不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用 4.循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。
所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。 虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件。 1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。 2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。 3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。 4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。 在系统中已经出现死锁后,应该及时检测到死锁的发生,并采取适当的措施来解除死锁。目前处理死锁的方法可归结为以下四种: 1) 预防死锁。 这是一种较简单和直观的事先预防的方法。方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。预防死锁是一种较易实现的方法,已被广泛使用。但是由于所施加的限制条件往往太严格,可能会导致系统资源利用率和系统吞吐量降低。 2) 避免死锁。 该方法同样是属于事先预防的策略,但它并不须事先采取各种限制措施去破坏产生死锁的的四个必要条件,而是在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。 3)检测死锁。 这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,此方法允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源,然后采取适当措施,从系统中将已发生的死锁清除掉。 4)解除死锁。 这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。死锁的检测和解除措施,有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。 列举说明linux系统的各类异步机制
-
程序从flag a执行到falg b的时间大致是多少秒?(2分)
import threading import time def _wait(): time.sleep(60) # flag a t = threading.Thread(target=_wait) t.setDeamon(False) t.start() # flag b
60
-
程序从flag a执行到falg b的时间大致是多少秒?(2分)
import threading import time def _wait(): time.sleep(60) # flag a t = threading.Thread(target=_wait) t.setDeamon(True) t.start() # flag b
0
-
程序从flag a执行到falg b的时间大致是多少秒?(2分)
import threading import time def _wait(): time.sleep(60) # flag a t = threading.Thread(target=_wait) t.start() t.join() # flag b
60
-
读程序,请确认执行到最后number是否一定为0(2分)
import threading loop = int(1E7) def _add(loop:int = 1): global number for _ in range(loop): number += 1 def _sub(loop:int = 1): global number for _ in range(loop): number -= 1 number = 0 ta = threading.Thread(target=_add,args=(loop,)) ts = threading.Thread(target=_sub,args=(loop,)) ta.start() ta.join() ts.start() ts.join()
0
-
读程序,请确认执行到最后number是否一定为0(2分)
import threading loop = int(1E7) def _add(loop:int = 1): global number for _ in range(loop): number += 1 def _sub(loop:int = 1): global number for _ in range(loop): number -= 1 number = 0 ta = threading.Thread(target=_add,args=(loop,)) ts = threading.Thread(target=_sub,args=(loop,)) ta.start() ts.start() ta.join() ts.join()
0
-
MySQL常见数据库引擎及区别?(3分)
MySQL常见的三种存储引擎为InnoDB、MyISAM和MEMORY。其区别体现在事务安全、存储限制、空间使用、内存使用、插入数据的速度和对外键的支持
-
简述事务及其特性? (3分)
事务(Transaction)是并发控制单位,是用户定义的一个操作序列,这些操作要么都做,要么都不做,是一个不可分割的工作单位。 事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。
-
事务的隔离级别?(2分)
1、事务隔离级别为读提交时,写数据只会锁住相应的行 2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。 3、事务隔离级别为串行化时,读写数据都会锁住整张表 4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大
-
char和varchar的区别?(2分)
数据库中char和varchar的区别为:长度不同、效率不同、存储不同。 一、长度不同 1、char类型:char类型的长度是固定的。 2、varchar类型:varchar类型的长度是可变的。 二、效率不同 1、char类型:char类型每次修改的数据长度相同,效率更高。 2、varchar类型:varchar类型每次修改的数据长度不同,效率更低。 三、存储不同 1、char类型:char类型存储的时候是初始预计字符串再加上一个记录字符串长度的字节,占用空间较大。 2、varchar类型:varchar类型存储的时候是实际字符串再加上一个记录字符串长度的字节,占用空间较小。
-
mysql中varchar与char的区别以及varchar(50)中的50代表的含义。(2分)
varchar(50)中50的涵义最多存放50个字符,varchar(50)和(200)存储hello所占空间一样,但后者在排序时会消耗更多内存, 因为order by col采用fixed_length计算col长度(memory引擎也一样)
-
MySQL中delete和truncate的区别?(2分)
一、灵活性:delete可以条件删除数据,而truncate只能删除表的所有数据; delete from table_test where ... truncate table table_test 二、效率:delete效率低于truncate,delete是一行一行地删除,truncate会重建表结构, 三、事务:truncate是DDL语句,需要drop权限,因此会隐式提交,不能够rollback;delete是DML语句,可以使用rollback回滚。 四、触发器:truncate 不能触发任何Delete触发器;而delete可以触发delete触发器。
-
where子句中有a,b,c三个查询条件, 创建一个组合索引abc(a,b,c),以下哪种会命中索引(3分)
(a) (b) (c) (a,b) (b,c) (a,c) (a,b,c)
(a,b,c)
-
组合索引遵循什么原则才能命中索引?(2分)
最左优先
-
列举MySQL常见的函数? (3分)
hex() bin() count() sum() max() min() day()
-
MySQL数据库 导入、导出命令有哪些? (2分)
1、导出完整数据:表结构+数据 2、只导出表结构
-
什么是SQL注入?(2分)
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息
-
简述left join和inner join的区别?(2分)
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录 inner join(等值连接) 只返回两个表中联结字段相等的行
-
SQL语句中having的作用?(2分)
对由sum或其它集合函数运算结果的输出进行限制
-
MySQL数据库中varchar和text最多能存储多少个字符?(2分)
50
-
MySQL的索引方式有几种?(3分)
1.普通索引 2.唯一索引 3.主键索引 4.组合索引 5.全文索引
-
什么时候索引会失效?(有索引但无法命中索引)(3分)
列与列对比 存在NULL值条件 NOT条件 LIKE通配符 条件上包括函数 复合索引前导列区分大 数据类型的转换
-
数据库优化方案?(3分)
1.数据分区 对于海量的数据查询优化,一种重要方式是如何有效的存储并降低需要处理的数据规模,所以我们呢可以对海量数据进行分区.例如,针对年份存储的数据,我们可以按照年进行分区,不同数据库有不同的分区方式,但处理机制却大体相同.例如SQLserver的数据分区将不同的数据存于不同的文件组中,而不同的文件存在不同的磁盘分区下,这样吧数据分区,减少磁盘IO和系统负荷. 2.索引 索引一般可以加速数据的检索数据,加速表之间的连接,对表建索引包括在主键上建立聚簇索引,将聚合索引建立在日期列上, 索引的优点很多,但是对于索引的建立,还需要考虑实际情况,而不能对每个列都建索引.如果表结构很大,你要考虑到建立索引和维护索引的开销,索引本身也占用物理空间,动态修改表也要动态维护索引,如果这些开销大过索引带来的速度优化,那就得不偿失. 3.缓存机制 当数据量增加时,一般的处理工具都考虑缓存问题,缓存大小的设置也关系到数据处理的表现.列如, 处理2亿条数据聚合操作室,缓存设置为100000条/buffer合理 4.加大虚存 由于系统资源有限,而处理的数据量非常大,当内存不足时,适量增加虚存来解决 5.分批处理 由于处理信息量巨大,可以对海量的数据进行分批(类似云计算MapReduce),然后再对处理后的数据进行合并操作,分而治之,这样有利于处理小数据. 6.使用临时表和中间表 数据量增加时,处理中要考虑提前汇总,这样做的目的是化整为零,大表变小表,分块处理完之后再利用一定的规则进行合并,处理过程中的临时表的使用和中间结果的保存都非常重要.如果对海量的数据,大表处理不了,只能拆分为多个小表.如果处理过程中需要多步汇总操作,则按汇总步骤一步一步来. 7.优化查询语句 查询语句的性能对查询效率的影响非常大,尽量早的缩小查询范围 8.使用视图 视图是表中的逻辑表现,不占用物理地址,对于海量数据,可以按一定的规则分散到各个基本表中,查询过程基于视图进行. 9.使用存储过程 在存储过程中尽量使用SQL自带的返回参数,而非自定义的返回参数,减少不必要的参数,避免数据冗余 10.用排序来取代非顺序存储 磁盘上的机械手臂的来回移动使得非顺序磁盘存取变成了最慢的操作,但是在SQL语句中这个现象被隐藏了,这样就使得查询中进行了大量的非顺序页查询,降低了查询速度. 11.使用采样数据进行数据挖掘 基于海量数据的数据挖掘方兴未艾,面对超海量数据,一般的挖掘算法往往采用数据抽样的方式进行处理,这样误差不会很大,大大的提高处理效率和处理的成功率.一般采样时应注意数据的完整性,防止过大的偏差.
-
什么是MySQL慢日志?(2分)
1.mysql的慢查询日志是msyql提供的一种日志记录,它用来记录在msyql中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的sql,则会被记录到慢查询日志中。 2.默认情况下,mysql数据没有开启慢查询日志,需要我们手动来设置这个参数,当然如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响,慢查询日志支持将日志记录写入文件。 3.如果要永久生效,就必须修改配置文件my.cnf,修改my.cnf文件,[mysqld]下增加或修改参数。 4.设置新值之后需要重新连接或新开一个会话才能看到修改值。
-
设计表,关系如下: 教师, 班级, 学生, 科室。(4分)
科室与教师为一对多关系, 教师与班级为多对多关系, 班级与学生为一对多关系, 科室中需体现层级关系。1. 写出各张表的逻辑字段 2. 根据上述关系表 a.查询教师id=1的学生数 b.查询科室id=3的下级部门数 c.查询所带学生最多的教师的id
-
有staff表,字段为主键Sid,姓名Sname,性别Sex(值为"男"或"女"),课程表Course,字段为主键Cid,课程名称Cname,关系表SC_Relation,字段为Student表主键Sid和Course表主键Cid,组成联合主键,请用SQL查询语句写出查询所有选"计算机"课程的男士的姓名。(3分)
-
根据表关系写SQL语句(10分)
- 查询所有同学的学号、姓名、选课数、总成绩;
- 查询姓“李”的老师的个数;
- 查询平均成绩大于60分的同学的学号和平均成绩;
- 查询有课程成绩小于60分的同学的学号、姓名
- 删除学习“叶平”老师课的score表记录;
- 查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分;
- 查询每门课程被选修的学生数;
- 查询出只选修了一门课程的全部学生的学号和姓名;
- 查询选修“杨艳”老师所授课程的学生中,成绩最高的学生姓名及其成绩;
- 查询两门以上不及格课程的同学的学号及其平均成绩;
第二部分 补充题
-
什么是IO多路复用?
其实就是整个函数对外表现为阻塞式的,也就是我们调用这个函数,如果条件达不到一定 会被阻塞;但是其实内部并不是阻塞的,而是以一种非阻塞的方式工作的,内部能够实现 自动轮询,如果有任何一个IO设备达到条件即可返回到应用层
-
async/await关键字的作用?
一、async/await的优点 1)方便级联调用:即调用依次发生的场景; 2)同步代码编写方式: Promise使用then函数进行链式调用,一直点点点,是一种从左向右的横向写法;async/await从上到下,顺序执行,就像写同步代码一样,更符合代码编写习惯; 3)多个参数传递: Promise的then函数只能传递一个参数,虽然可以通过包装成对象来传递多个参数,但是会导致传递冗余信息,频繁的解析又重新组合参数,比较麻烦;async/await没有这个限制,可以当做普通的局部变量来处理,用let或者const定义的块级变量想怎么用就怎么用,想定义几个就定义几个,完全没有限制,也没有冗余工作; 4)同步代码和异步代码可以一起编写: 使用Promise的时候最好将同步代码和异步代码放在不同的then节点中,这样结构更加清晰;async/await整个书写习惯都是同步的,不需要纠结同步和异步的区别,当然,异步过程需要包装成一个Promise对象放在await关键字后面; 5)基于协程: Promise是根据函数式编程的范式,对异步过程进行了一层封装,async/await基于协程的机制,是真正的“保存上下文,控制权切换……控制权恢复,取回上下文”这种机制,是对异步过程更精确的一种描述; 6)async/await是对Promise的优化: async/await是基于Promise的,是进一步的一种优化,不过在写代码时,Promise本身的API出现得很少,很接近同步代码的写法
-
MySQL的执行计划的作用?
1.查看mysql执行计划 2.执行计划包含的信息
-
简述MySQL触发器、函数、视图、存储过程?
触发器 (1)触发器是一个特殊的存储过程,它是MySQL在insert、update、delete的时候自动执行的代码块。 (2)触发器必须定义在特定的表上。 (3)自动执行,不能直接调用, 函数 它跟php或js中的函数几乎一样:需要先定义,然后调用(使用)。 只是规定,这个函数,必须要返回数据——要有返回值 视图 视图只是一种逻辑对象,是一种虚拟表,它并不是物理对象,因为视图不占物理存储空间,在视图中被查询的表称为视图的基表,大多数的select语句都可以用在创建视图中(说白了,视图就是一种虚拟表,就像是一张电子照片) 优点:集中用户使用的数据,掩码数据的复杂性,简化权限管理以及为向其他应用程序输出而重新组织数据等 存储过程 存储过程(procedure),概念类似于函数,就是把一段代码封装起来,当要执行这一段代码的时候,可以通过调用该存储过程来实现。在封装的语句体里面,可以同if/else ,case,while等控制结构,可以进行sql编程,查看现有的存储过程。
-
数据库中有表:t_tade_date
id tade_date 1 2018-1-2 2 2018-1-26 3 2018-2-8 4 2018-5-6 ... 输出每个月最后一天的ID