备忘录

本文深入探讨了Go语言的并发原理,重点介绍了协程与线程的区别,以及Go如何通过调度器实现高效并发。同时,文章讲解了Redis的持久化策略,包括RDB和AOF,以及它们的优缺点。此外,还分析了MySQL的索引、事务和优化策略,并提到了秒杀场景下的系统优化方法。最后,讨论了HTTP请求的生命周期,以及PHP7的新特性。
摘要由CSDN通过智能技术生成

进程之间互不影响,开销大,所有的进程都是由内核管理的,所有的进程也都是相互隔离的
线程间可以共享数据,但是开销也比较大,比如一万个并发就需要一万个线程
协程是可以看做轻量级的线程,但是需要语言的支持,如果不支持就需要自己去实现相应的调度器

一般来说php执行会从上到下依次执行,中间卡住了那就一直就卡住了,要执行完之后才能进行下一步,
使用多线程的话可以不经过中间那步就直接进入下一步,但是这里的问题是也不知道上一步什么时候会执行完成,很可能会出现脏数据
线程共享内存叫做共享内存系统

go最大的优势是轻量级,可以轻松创建数百万个协程

go并发编程的原理:

go语言会通过go来启动多个协程,,然后在不同的协程中完成不同的子任务,这些协程的本质是用户级线程,在运行的时候会启动底层的调度器将用户级线程交给操作系统的系统级线程去操纵
如果用户级线程因为某个IO阻塞,调度器会把用户级线程和系统级线程分离,让系统级线程去操作其他的用户级线程,如果该用户级线程恢复了,调度器会调度空闲的系统级线程来处理这个用户级线程
从而达到处理多个并发协程的目的
调度器还会在系统级线程不够的情况下创建多个系统级线程,同样系统级线程多了的话还会去销毁系统级线程

缓冲通道

双向通道
ch := make(chan int) 无缓冲
ch := make(chan int ,20) 有缓冲
单向通道
ch := make(chan<- int) 只写通道
ch := make(<-chan int) 只读通道
通过select等待通道就绪
select的写法和swith一样,case…default,这里要注意的是case并不是像swith一样,从上到下按顺序一个一个去执行

秒杀:

浏览器-站点-服务-数据库
最终的目的都是尽量让访问落在系统上游,而不是数据库

在浏览器可以做限制每多少秒内只能限制一次访问
在站点端通过计数限制用户请求频率,增加缓存或者页面缓存
在服务层限制请求个数,使用队列,每次只能传过去有限的人,秒杀就如果就几件传过去几百人也没用,这里也可以做缓存
在数据库层面基本就没什么压力了

最难的还是站点,减压的方法有两个,一个是服务器扩容一个是直接抛弃部分请求

秒杀的优化方向主要是两点:

一个是尽量将请求拦截在系统上游
一个是读多写少用缓存

redis

redis的持久化就是会把内存中的数据写到磁盘中,防止数据丢失

持久化的方式有两种

一种是rdb,比如1秒内更新数据100次,生成一个rdb文件,进行全量的数据恢复,优点是需要数据恢复的时候,恢复的快,缺点是因为他是每隔一段时间生成一次,如果在间隔时间内服务挂了,也会丢数据
一种是aof,每个更新数据的时候都会把redis命令写到aof文件中,优点是数据基本不会丢,缺点是如果需要恢复数据,数据恢复的很慢,如果文件足够大的话,还可能无法恢复

innodb主键

如果设置了主键,那么innodb会选择主键作为聚集索引,如果没有定义显示主键,那么innodb会选择第一个不含有null的唯一索引作为主键,如果也不存在,那么innodb会选择内置6字节长的rowid作为
隐含的聚集索引,rowid会随着记录的增长而自增
如果表使用了主键,那么每次插入新的纪录,记录会按照顺序添加到当前索引节点的后面,主键的顺序会按照记录的插入顺序排列,如果一页满了,会自动开辟新的一页
如果表使用了非自增主键,那么表每次新增记录,都会被查到现有索引页的某个位置,此时mysql不得不为了插入新的数据,而把其他的数据挪动位置,甚至目标页面可能已经被写入磁盘而被清理掉
此时又要从磁盘中读回来,这来来回回就会增加很多开销,这些频繁的移动,分页会造成大量的数据碎片,得到了不够紧凑的索引结构,后续不得不使用optimize table来重新建表

事务的四大特性

原子性,一致性,隔离性,持久性
原子性:一个事务要么成功要么失败
一致性:代表的数据底层储存的完整性,需要开发和事务系统一起来保证,例如一次转账过程,某一账户扣的钱和另一个账户得到的钱必须一致
隔离型:事务必须保证不影响其他事务和进程的前提下独立进行
持久性:表示某个事务执行过程中,对数据所做的所有改动都必须在事务成功前保存在某个物理设备中

隔离性的隔离级别

事务的隔离级别:读未提交,读提交,可重复读(Mysql),可串行化
读未提交:所有的事务都可以看到其他未提交事务的执行结果
读提交:一个事务只能看到已经提交了事务所做的改变
可重复读:同一个事务在并发请求的时候,会看到同样行数的数据
可串行化:强制事务排序,使事务间不可能相互冲突;简单来说就是给事务加上共享锁,但是这种情况下会出现大量的超时现象和锁竞争

事务隔离级别实际要解决的主要是脏读,可重复读,不可重复读,幻读
脏读:指的是读到了其他事务还没有提交的数据,这些数据还可能会回滚;简单来说就是读到了并不一定最终提交的数据
可重复读:指的是在一个事务中,一开始读取到的数据和最终读取的数据是一样的,通常事务中存在执行update语句
不可重复读:指在一个事务中,不同时间读取到的数据可能会不同,原因是其他事务对这个数据有更改
幻读:在一个事务中两次查询的数据不一致,例如一个事务查询的查询了几行数据,另一个事务则插入了几行数据,前面那个事务在后面的查询中会发现有几列数据是上次查询没有的,这就是幻读

读未提交可能会引起脏读,不可重复读,幻读
读提交可能会引起不可重复读,幻读
不可重复读可能会引起幻读
串行化是相对而言最完善的级别

Mysql缓存

mysql缓存机制就是把sql语句和缓存结果以kv形式存到数据库内存中,如果运行相同的sql,服务器直接去缓存中获取结果

Mysql查询缓存

mysql查询缓存是mysql比较特殊的一个缓存区域,用来存放缓存特定query的结果信息,并共享给所有的客户端
为了提高完全相同的query响应语句,mysql server会把query结果进行hash计算后,把得到的hash值和query计算的结果集对应存放到query cache中
当mysql server打开query cache时,mysql server会把每一个select语句进行特定的hash算法计算其的hash值在query cache中进行匹配,如果匹配到了就直接返回给客户端
如果匹配不到,将这个hash值存放到一个hash链表中,将query的结果集存到query cache中,存放hash链表的每个hash节点都存放了query对应的结果集在chche中的位置,以及该query涉及到的一些table信息
目前mysql query cache只缓存select语句,对于其他的show,use语句不做缓存

Mysql缓存机制

mysql缓存机制简单来说就是缓存sql文本及查询结果,如果客户端在运行相同的sql,服务端会直接从缓存中拿,而不需要再去解析和执行sql

缓存的规则

1,开启了缓存以后,mysql server会自动将sql和查询结果返回到内存,下次直接从内存中拿
2,缓存的结果是共享的
3,mysql query cache的缓存为select的查询内容作为结果集,cache使用完整的sql做key,两条sql必须完全一致,才可以获取缓存
4,where条件中如果包含任意一个不确定函数就不会被缓存,例如current_date,now
5,太大的result_set也不会被缓存
6,缓存在分库分表下也是不起作用的
7,sql有触发器,自定义函数也不起作用

缓存失效

表的结果或者数据发生更改的时候,就会导致缓存失效

缓存机制中的内存管理

mysql query cache使用内存池的技术,自己管理内存释放和分配,而不是靠系统管理
当服务器启动的时候,会初始化缓存需要的内存,是一个完整的空闲块
当查询结果需要缓存的时候,先从空闲快中申请一个数据库块为存储空间,这个数据库块的大小是固定的,是实现配置好的,即使要缓存的数据很小,也是这个,这是因为在查询结果返回之前就分配空间,这时候不知道数据的大小
分配内存块要先锁住空间块,这个耗时比较久,mysql会避免这个操作,选择尽可能小的内存快,如果内存快不够的话会继续申请,如果存储完时有多余的则会释放多余的那部分

不建议使用mysql缓存

因为任何对于表数据的更改都会引起缓存失效,可能还没等第二次使用就失效了

Mysql索引底层数据结构选型 https://zhuanlan.zhihu.com/p/113917726

hash

hash算法也叫散列算法,就是把要任意的key通过hash函数转换为固定长度的key地址,通过这个key地址查找具体数据的数据结构,时间复杂度O(1)
hash算法会出现碰撞,比如hash(7)和hash(199)的结果是一样的,解决碰撞问题的一个常见方法是链地址法,
即用链表把碰撞的数据链接起来,如果该hash值出现碰撞数据链表,则会一直循环链表,直到找到真正的key对应的数据
hash不适合进行范围查询,范围查询的话会把所有的数据都查出来,然后在这里面查找符合条件的数据
总结:hash算法可以做到快速检索数据,不适合范围查找

二叉树查找

二叉树查找的时间复杂度是O(lgn)
二叉树的叶子节点都是按序排列的,从左到右依次升序排列
例如:假如要取出id > 5的数据,我们只要取出节点为6的以及他的右侧节点就可以了
但是二叉树有个致命的缺陷,例如在有主键自增的时候,二叉树会演化为线性链表,检索速度大大降低

AVL和红黑树

红黑树:
是一个会自动调整树形态的树结构,当二叉树处于不平衡的时候,会自动左旋右旋节点以及变色,调整树的形态,使其保持基本的平衡状态
但是也有问题,假如主键自增的情况,原来是线性链表,会变为右旋链表,并没有完全解决问题
AVL:
是个绝对二叉平衡树,解决了红黑树的问题,不会出现线性链表
他的问题主要在于磁盘IO,每个节点只存了一个数据,我们要找一个节点,可能要经过很多节点,比如id=7,top是4->6->7,要经过三次IO

要注意了,磁盘IO有个特点,读取1B的数据和1KB的数据IO次数是一样的,所以根据这个思路,一次IO就多存些数据到内存中,这也就是B树,B+树的由来

B树

B树的一个节点可以存多个key,时间复杂度是O(h*logn),h为树高,n为每个节点关键词的个数,并且尽可能的减少IO,还支持范围查找

B+树

B树和B+树的区别

1,B树的每个节点里存的是数据,B+树的节点存的是索引,每个节点存不了多少数据,但是可以存很多索引
2,B+树的子节点是用了一个链表串起来的,方法范围查找,这个链表本身是有序的,在范围查找时更具备效率

innodb和Myisam引擎的区别

Myisam数据查找性能较好,但是不支持事务;innodb支持事务,而且支持行级锁
使用innodb建表后会产生frm文件(创建表的语句)和idb文件(表里面的数据和索引文件),索引innodb底层实现是聚集索引方式
使用myisam建表后会产生三个文件,frm文件(创建表的语句),myd(表里面的数据文件),myi(表里面的索引文件),所以myisam底层实现是非聚集索引方式
myisam的查询速度快的原因是他定位到节点的位置就可以直接得到数据,innodb是要定位到索引后,再用索引去查一遍主键索引树,所以innodb比myisam多了一步

为什么加了索引后查询速度就快了

建立索引的过程会选择索引类型,比如说选了btree(二叉树),就会快速检索,减少IO次数

mysql索引优化【https://zhuanlan.zhihu.com/p/343312997】

1,limit m,n查询,m越大越慢
sql:select * from huox_caasdata.cd_channel where platform_id=998 and is_kol=1 order by id desc limit 150000,10;
sql:select * from huox_caasdata.cd_channel a ,(select id from huox_caasdata.cd_channel where platform_id=998 and is_kol=1 order by id desc limit 150000,10) as b where a.id = b.id;
可以看到第一条sql的查询速度limit的大小而变化,limit越大,查询越耗时,这是因为定位limit的位置所需要的IO开销较大
优化思路:这里可以利用辅助索引的的覆盖扫描来进行优化先获取id,这一步不需要回表,然后id跟原表相关联
2,需要更新大量的数据
优化思路分批次更新

mysql索引设计

1,前缀索引
2,复合索引

redis订阅发送

subscribe订阅 publish发送 unsubscribe退订

TCP/IP三次握手,四次挥手

三次握手:
1,A:我要发送请求了,可以吗?
2,B:可以,我准备好了
3,A:好的,那我也准备好了,那我要开始了
为什么不是两次握手:客户端不知道服务端是否已经准备好接收数据,所以会形成服务端超时,客户端始终忽略服务端发来的分组信号,形成两端死锁
四次挥手:
1,A:我要断开连接了
2,B:好,我知道了
3,B:数据我全都发过去了
4,A:好,我知道了,那我断开了

三次握手:
1,A发送(SYN=1,ACK=0)的请求报头,且不携带数据,seq=x表示该报文所用序号为x,A进入同步已发送状态【SYN=1,ACK=0,seq=x】
2,B接收到A的报文,发送SYN=1,ACK=1的同意建立连接的报文,ack=x+1期望收到A的在一个数据序号是x+1,seq=y表示该报文所有序号是y,B进入同步收到状态【SYN=1,ACK=1,ack=x+1,seq=y】
3,A收到B的同意连接的报文,向B发送ACK=1的确认报文,ack=y+1期望收到B的下一个数据序号为y+1,seq=x+1表示该报文的序号,A进入已建立连接状态【SYN=1,ACK=1,ack=y+1,seq=x+1】
3,B收到A的确认报文,进入链接状态

四次挥手:
1,A处于初始已连接状态
2,A的数据已经发送完毕,向B发出连接释放报文段(FIN=1 第一次挥手),seq=u,A进入终止等待1状态,等待B的确认【FIN=1,seq=u】
3,B收到A的释放报文段后,发出确认报文(ACK=1 第二次挥手),ack=u+1期望收到A下一个报文序号为u+1,seq=v表示该报文的序号是v,B进入关闭等待状态【ACK=1,ack=u+1,seq=v】
4,此时A已经没有数据要发给B了,B还有数据要发给A,所以A还要继续接收数据
5,A收到B的确认报文后,进入终止等待2的状态,等待B的连接释放报文
6,等待B发送确认报文后,又向A发送了一些数据,数据发送完毕,这时候要释放链接,B向A发送释放连接报文(FIN=1,ACK=1 第三次挥手),seq=w表示该报文的序号是w,ack=u+1因为上次A发送的连接释放报文(FIN=1)序号为u,B进入最后确认状态【FIN=1,ACK=1,seq=w,ack=u+1】
7,A收到B的释放报文后,发出确认报文(ACK=1),seq=u+1,ack=w+1进入时间等待,经过2MSL(两个最长报文寿命)后断开连接【ACK=1,seq=u+1,ack=w+1】
8,B收到A的确认报文后,断开连接

为什么连接的时候是三次握手,关闭的时候是四次?

在连接的时候,server收到客户端的报文的时候,可以直接发送SYN和ACK,其中ACK是用来报文应答的,SYN是用来同步的;
但是在关闭的时候,server在收到客户端FIN的时候,很可能不会立刻关闭socket,数据可能没有传输完毕,这时候只会回一个ACK,在数据传输完成之后才会输入一个FIN告诉客户端可以关闭了

为什么不能在连接的时候两次握手?

三次握手有两个重要的功能,一个是确认双方都已经准备好了,一个是双方就初始序列号进行协商
假如只有两次握手的话,举个例子,A向B发送一个连接请求分组,B收到后告诉A可以了,他准备好了,按照两次握手的情况,这时候A就开始发送分组了
但是这时候如果出现特殊情况,A在B的应答分组出现传输丢失的情况,将不知道B是否已经准备好,初始序列号是多少
在这种情况下,A认为连接还没建立,拒绝B发来的任何分组数据,只等待连接确认分组应答,而B在发送分组超时后会一直发送应答分组,就会陷入死锁状态

如果已经建立了连接,但是客户端突然出现故障怎么办?

TCP还设有一个保活计时器,每次接收到新的请求后都会重新复位计时器,在相隔固定的时候后没收到请求,服务端就会认为客户端出现故障,就会直接关闭连接

面向对象

面向对象是模型化的,只需要抽出来一个类,这是个封闭的盒子,在这里你拥有数据也拥有解决问题的方法,不必一步一步去实现

面向过程

面向过程是具体化的,要解决一个问题,要一步一步去实现

面向对象和面向过程的区别

面向过程的优点:性能较好,因为面向对象调用时需要实例化整个类,消耗资源
面向过程的缺点:不易维护,不易复用,不易扩展
面向对象的优点:易维护,易复用,易扩展
面向对象的缺点:性能相对而言较差

一次完整的http请求从客户端到服务端所经过的各个环节

1,浏览器会去请求DNS服务器,获取域名对应的IP地址
2,三次握手,建立tcp连接,建立session会话
3,浏览器发送request包,服务器接收后对其进行解析,,如果请求资源包含动态语言的内容,服务器将会调用动态语言的解释引擎进行解释
4,请求被转发给一个预定义的handler(处理器),并可能从数据库里取数据
5,处理器根据取出的数据对模版进行渲染
6,返回reponse请求
7,浏览器会渲染reponse中的主体,而且直到接收完reponse信息,客户端和服务端会一直保持连接

redis单线程为什么快

1,单线程避免了线程之间的竞争,避免了出现死锁的情况
2,纯内存操作,避免了大量IO操作
3,采用了非阻塞IO多路复用机制(采用多路IO复用技术,可以让单线程高效的处理多个连接请求,因为redis是单线程的,所有的命令都是按顺序执行的,很容易就会造成堵塞)

laravel

IOC容器(服务容器)

提供了整个系统容器功能及服务的配置,主要承担两个作用绑定和解析
简单理解就是当我们的服务开始运行的时候,我们把我们需要的一些服务放到或注册到(bind)到容器里面,在我们需要的时候,可以取(make)出来

工厂模式

工厂模式就是一个类所依赖的外部事物的实例都可以被一个或多个工厂创建的这样一种开发模式

依赖注入

只要不是由内部生产(比如初始化,构造方法中通过工厂模式或者自动new的),而是由外部以参数及其他形式注入的,都属于依赖注入

控制反转

由外部负责,外部创造的的能力符合类中的某一接口,这个接口是固定的模式,只要符合这个模式都可以被植入使用,可以增加该类的能力,这种由外部负责其依赖需求的行为就叫做控制反转

php7

1,增加表量类型和返回值类型声明
2,null的合并运算符 (??)
3,太空船运算符
4,常量数组
5,匿名类
6,异常
7,use合一
8,把闭包函数绑定到类上
9,新增函数 intdiv

消息队列

场景:异步,消峰,解偶
异步:比如电商购买流程,可能中间还有使用优惠券,积分,发短信的流程,这要是按顺序执行,那可能在高并发的时候就太慢了,可以把该订单放到消息队列中,等待后续执行
消峰:比如电商购买流程,会把请求放到队列中,但是没必要把所有的请求都请求,考虑自己服务器的情况,每秒执行多少请求自己决定
解偶:不把所有的鸡蛋放到一个篮子里,下单就管下单的事,积分,发短信每个功能并不非要一起执行

问题:
系统复杂性:重复消费,消息丢失,顺序消费
数据一致性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值