面试的一些知识点总结

redis2.2新增BitMap
redis2.6 新增服务端支持Lua脚本,支持redis Sentinel
redis2.8 添加部分主从复制功能,redis Sentinel(哨兵模式)第二版,新增HyperLongLog
redis3.0 分布式实现Redis Cluster
redis3.2 新增GEO相关功能
redis4.0 提供RDB-AOF 混合持久化格式,充分利用AOF和RDB各自优势
redis5.0 新增Stream数据类型
redis6.0 新增多线程IO

redis常见五中数据结构String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合)

String key-value 结构,value最大长度512M
String底层数据结构是int和SDS(简单动态字符串),SDS可以报错文本数据和二进制数据,获取长度的时间复杂度是o(1)
redis的SDS的API是安全的,拼接字符串不会造成缓冲区溢出,字符串对象内部编码有三种:int,raw,embstr
缓存对象,常规计数,分布式锁,共享Session信息
List 简单的字符串列表, 从头部或者尾部向List添加元素 ,每个列表支持超40亿个元素
底层数据结构是双向链表或者压缩列表,元素个数小于512,在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了
应用于消息队列
Hash 一个键值对集合 ,适合存储对象 ,底层由压缩列表或哈希表实现,hash元素小于512个,所有值小于64字节,使用压缩列表,其他用哈希表,
redis7.0压缩列表数据结构已废弃,用listpack实现
可应用于购物车
Set 一个无序并唯一的键值集合,可以交集,并集,差集。底层由哈希表或者整数集合实现,元素个数小于512
使用整数集合作为底层数据结构,其他用哈希表
应用场景,点赞,共同关注,重讲活动
Zset 有序集合类型,比Set多一个排序书信score,底层数据结构是由压缩列表或者调表,元素个数小于128个,
redis使用压缩列表,其他使用调表redis7.0压缩列表数据结构已废弃,用listpack实现
应用场景:排行榜

持久化:AOF日志,RDB快照,混合持久化
AOF:只会记录写操作命令,读操作不会记录,默认不开启,日志文件是普通文本,redis先执行写操作命令后,
才将该命令记录到AOF日志里面,避免额外的检查开销,不会阻塞当前写操作命令的执行,为了避免AOF文件越
写越大,AOF重写机制,AOF文件大小超过设定阈值后,redis会启用AOF重写机制,压缩AOF文件。数据库某个键过期,
AOF文件生成的时候会保留此过期键,当过期键被删除后,AOF文件会被追加一条DEL命令。AOF重写阶段,会对键值进行
检查,已过期的键不会被保存到重写后的AOF文件中。
RDB:文件的内容是二进制数据,RDB快照是记录某一瞬间的内存数据,记录的是实际数据,RDB恢复数据的效率比AOF
高些,执行save命令,主线程会生成RDB文件,写入时间过长会阻塞主线程,执行bgsave会创建一个子进程来生成RDB
文件,这样可以避免主线程的阻塞。redis快照是全量快照,每次执行,会把内存中所有数据记录到磁盘中。RDB生成
文件时会对key进行过期检查,过期的键不会被保存到新的RDB文件中。RDB文件加载阶段如果是主服务器会对保存中的键进行
检测,过期的键不会被载入到数据库中。从服务器则是不论过期与否都会被载入到数据库中,主库在进行数据同步时会
删除。
混合持久化:在AOF重写日志时,子进程会把内存数据以RDB方式写入AOF文件,主线程处理的操作命令会被记录在重写
缓冲区,重写缓冲区以AOF方式增量写入AOF


缓存雪崩:
大量缓存数据在同一时间过期(失效)或者Redis故障宕机时,全部请求直接访问数据库,数据库压力骤增,造成数据宕机,
造成整个系统崩溃。
发生原因:大量数据同时过期,redis故障宕机
应对方法:均匀设置过期时间,互斥锁,双key策略,后台更新缓存。
缓存击穿:
某个热点数据过期了,大量请求访问了该热点数据,无法从缓存中读取,直接访问数据库,数据库容易被高并发请求冲垮,
这就是缓存击穿的问题。
应对方法:互斥锁方案,不给热点数据设置过期时间。
缓存穿透:当用户访问数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,
发现缓存缺失,再去访问数据库时,发现数据库中也没有访问的数据,没办法构建缓存数据。有大量这样的请求
到来时,数据库压力骤增。
发生原因:业务误操作,缓存和数据库中数据都被误删除,黑客恶意攻击。
应对方法:非法请求限制,缓存控制或者默认值,使用布隆过滤器快速判断数据是否存在。
Redis主从复制 :
第一次主从同步,可以使用replicaof(Redis5.0之前使用slaveof)命令形成主服务器的关系,
第一阶段:建立链接,协商同步,执行replicaof命令后,从服务器向主服务器发送psync命令,表示要近些数据同步。
第二阶段:主服务器同步数据库给服务器,主服务器执行bgsave命令来生成RDB文件,然后把文件发送给从无服务器 ,
bgsave会fork一个子进程来生成RDB文件,是异步,redis依然可以正常处理命令,主服务器会将同步期间的写操作命令,
写入到replication buffer缓冲区里
第三阶段:主服务器发送新写操作命令给从服务器,收到redis载入成功的回复之后,主服务器会将replication buffer
缓冲区里所记录的写操作命令发送给从服务器,从服务器执行来自主服务器replication buffer缓冲区里的发来的命令,
这时从服务器的数据就一致了
主从服务器完成第一次同步化,双方会维护一个TCP连接,后续主服务器降写操作命令传播给从服务器,这个连接是长连接。
Redis2.8之前,主从服务器命令同时出现网络断开又恢复的情况,从服务器会和主服务器重新进行一次全量复制,
Redis2.8开始,网络断开又恢复,从主服务器会采用增量复制的方式继续同步,只会把网络断开期间主服务器接收到的写操
作命令,同步给从服务器。repl_backlog_buffer缓冲区会保存增量同步的写命令。
主从数据不一致:
主从节点间的命令复制是异步进行,无法实现强一致性保证。
应对:尽量保证主从节点间的网络连接状况良好,使用外部程序来监控主从节点间的复制进度。

哨兵机制:Redis在2.8版本以后提供哨兵机制,实现主从节点故障转移,它会检测主节点是否存活,如果发现主节点挂了,
它就会选举一个从节点切换为主节点。并把新主节点的相关信息通知给从节点和客户端。
监控:哨兵会每隔1s给所有主节点发送PING命令,当主从节点收到PING命令后,会发送一个响应命令给哨兵,这样就
可以判断是否在正常运行。
哪个哨兵节点判断主节点为客观下线,这个哨兵节点就是候选者,所谓候选者就是相当Leader的哨兵。
候选者会向其他哨兵发送命令,表明希望成为Leader来执行主从切换,并让所有其他哨兵对它进行投票。拿到半数以上的
赞成票,拿到的票数同时还需要大于等于哨兵配置文件的quorum值,才能成为Leader。
主从故障转移:
1、在已下线主节点属下的所有从节点里面,挑选一个从节点,并将其转换为主节点。
2、让已下线主节点属下所有从节点修改复制目标,修改为复制新主节点。
3、降薪主节点IP地址和信息,通过发布者/订阅者机制通知给客户端
4、继续监控旧主节点,当这个旧主节点重新上线,将它设置为新主节点的从节点。
切片集群模式:
当Redis缓存数据量大到一台服务器无法缓存时,就需要使用Redis切片集群(Redis Cluster)方案,它把数据分布在
不同的服务器上,以此来降低以此来降低系统对单主节点的依赖,从而提高Redis服务的读写性能。Redis Cluster 方案
采用哈希槽,处理数据和节点的映射关系,一个切片集群共有16384个哈希槽,哈希槽映射到具体的Redis节点的两种
方案,平均分权,使用cluster create 命令创建redis集群时,redis会自动把所有哈希槽平均分布到集群节点上。
手动分配,可以使用cluster meet命令手动建立节点间的连接,组成集群,再使用cluster addslots命令,指定每个
节点上的哈希槽个数。
由于网络问题,集群节点之间失去联系,主从数据不同步,导致客户端写入的数据丢失。 当主节点发现从节点下线或者
通信超时的总数量小于阈值时,就禁止主节点进行写数据,把错误返回给客户端
redis过期删除策略:
惰性删除策略:不主动删除过期键,每次重数据库访问key时,都检测key是否过期,如果过期则删除该key。优点:对cpu
最友好,缺点如果过期key一直没有被访问,它所占用的内存就不会释放,造成内存空间浪费。,对内存不友好
定期删除策略:每个一段时间随机从书籍中取出一定数量的key进行检测,并删除其中的过期key。
redis内存淘汰策略:
1、不进行数据淘汰的策略,redis3.0之后默认的内存淘汰策略。当运行内存超过最大设置内存时,不淘汰任何数据,而是不
再提供服务,直接返回错误。
2、进行数据淘汰:
volatile-random:随机淘汰设置了过期时间的任意键值
volatile-ttl 优先淘汰更早过期的键值
volatile-lru(redis3.0之前,默认的内存淘汰策略)淘汰所有设置了过期时间的键值中,最久未使用的键值。
volatile-lfu(Redis4.0后新增的内存淘汰策略):淘汰设置了过期时间的最少使用的键值。
allkeys-random:随机淘汰任意键值
allkeys-lru:淘汰整个键值中最久未使用的键值
allkeys-lfu(redis4.0后新增的内存淘汰策略);淘汰整个键值中最少使用的键值)。

redis延迟队列:使用有序集合的方式来实现延迟消息队列,ZSet有一个Score属下可以用来存储延迟执行的时间。使用zadd score1 value1
命令就可以一直往内存中生产消息。再利用zrangebysocre 查询符合条件的所有待处理的任务,通过循环执行队列任务即可。
redis的大可以处理:大key并不是指key的值很大,而是key对应的value很大。一般而言,下面两种情况被称为大key:String类型的值大于10KB,
Hash List,Set,ZSet 类型的元素个数超过5000个。大key会导致:客户端超时阻塞,引发网络阻塞,阻塞工作线程,内存分布不均。
使用redis-cli --bigkeys 查找大key,使用SCAN命令查找大key,使用第三方工具查找大key。
分批次删除大key:对于删除大Hash,使用hscan命令,每次获取100个字段,再用hdel命令删除,每次删除1个字段。
异步删除(redis4.0版本以上):用unlink命令代替del来删除,讲key放入一个异步线程中进行删除,这样不会阻塞主线程。
Redis管道技术:是客户端提供的一种批处理技术,用于一次处理多个Redis命令,从而提高整个交互的性能。
Redis没有提供回滚机制,使用discard放弃事物执行,把暂存命令队列清空,redis并不一定保证原子性(事务中的命令要不
全部成功,要不全部失败)

Redis实现分布式锁:
分布式锁:是用于分布式环境下并发控制的一种机制,用于控制某个资源在同一时刻只能被一个应用所使用的。
redis的set命令有个NX参数可以实现key不存在插入,实现分布式锁,SET lock_key unique_value NX PX 10000


网络:
键入网址到网页显示,期间发生了什么:1、输入网址,DNS解析,建立TCP/IP链接,发送HTTP请求,服务器处理请求,
服务器返回HTTP响应,浏览器渲染页面并展现,断开连接。
HTTP:超文本传输协议,分拆成三部分分别为:超文本,传输,协议。HTTP是一个用在计算机世界里的协议。它使用计算机
能够理解的语言确立了一种计算机之间交流通信的规范,HTTP是一个双向传输协议。超文本,它超越了普通文本的文本,
它是文字、图片、视频等的混合体,最关键有超链接,能从一个超文本跳转到另外一个超文本。HTTP是一个在计算机世界里
面专门在两点之间传输文字、图片、视频等超文本数据的约定和规范。
HTTP常见的状态码:
200 ok :最常见的成功状态码,表示一切正常。
204 No Content 常见成功状态码,响应头没有body数据
206 Partial Content 应用于HTTP分块下载或断点续传。也是服务器处理成功状态
301 :表示永久重定向,说明请求的资源已经不存在了,需要改用新的URL再次访问。
302:表示临时重定向,说明请求的资源还在,但是暂时需要用另外一个URL来访问。
304:不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件。
400:表示客户端请求的保温有错误
403:服务器禁止访问资源,并不是客户端的请求出错。
404:表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。
500:服务器发生了错误。
501:表示客户端请求的功能不支持
502:通常是服务器作为网关或代理时返回的错误码。表示服务器自身工作正常,访问后端服务器发生了错误。
503:表示服务器当前很忙,暂时无法响应客户端。
GET:从服务器获取指定资源。安全幂等的。
POST:根据请求负荷,对指定的资源做出处理,新增或者提交数据的操作,会修改服务器上的资源,不是幂等。
TCP三次握手:
一次握手:客户端发送带有SYN(SEQ=x)标志的数据包-》服务端,然后客户端进入SYN_SEND状态,等待服务器的确认。
二次握手:服务端发送带有SYN+ACK(SEQ-y,ACK=x+1)标志的数据包-》客户端,然后服务端进入SYN_RECV状态
三次握手:客户端发送带有ACK(ACK=y+1)标志的数据包-》服务端,然后客户端和服务器端都进入ESTABLISHED状态,完成
TCP三次握手。
断开一个TCP连接则需要四次挥手:
第一次挥手:客户端发送一个FIN(SEQ=x),标志的数据包-》服务端,用来关闭客户端到服务器的数据传送,然后客户端进入
FIN-WAIT-1状态
第二次挥手:服务器收到这个FIN(SEQ=X)标志的数据包,它发送一个ACK(SEQ=X+1)标志的数据包-》客户端。然后辞职服务端进入
CLOSE-WAIT状态,客户端进入FIN-WAIT-2状态。
第三次挥手:服务端关闭与客户端的连接并发送一个FIN(SEQ=Y)标志的数据包-》客户端请求关闭连接,服务端进入LAST-ACK 状态
第四次挥手:客户端发送ACK(SEQ=y+1)标志的数据包-》服务端并且进入TIME-WAIT状态,服务端手动ACK(SEQ=y+1)标志的数据包进入
CLOSE状态。


MYSQL知识点
mysql查询执行顺序:
1、加载from子句的前两个表计算笛卡尔积,生成虚拟表vt1
2、筛选关联表符合on表达式的数据,保留主表,生成虚拟表vt2
3、如果使用外连接,执行on的时候,会将主表中不符合on条件的数据也加载进来,作为外部行。
4、如果from 子句中表数量大于2,则重复第一到第三步,直至所有表加载完毕,更新vt3
5、执行where表达式,筛选不符合条件的数据生成vt4.
6、执行group by子句,生成vt5
7、执行聚合函数,生成vt6
8、执行having,筛选v6数据,生成vt7
9、执行select从vt7中筛选列,生成vt8
10、执行distinct,对vt8去重,生成vt9.
11、执行orderby对vt9排序,返回游标
12、执行limit语句,结果返回客户端。


mysql存储行为由存储引擎实现,表空间由段(segment)、区(extent)、页(page)、行(row)组成,
用B+树来组织数据。B+树中每一层都是通过双向链表链接起来,
行:数据库表中的记录都是按行进行存放,每行记录根据不同的行格式,有不同的存储结构。
页:默认每个也大小是16KB,InnoDb数据按页为单位读取,当读取一条记录时,以页为单位,将其整体读入内存。
表中的记录存储在数据页。
区:在表中数据量大的时候,为某个索引分配空间的时候就不再按照页为单位分配,而是按照区位单位分配,每个区的
大小为1MB,对于16KB的页来说,连续的64个页回本划为一个区,这样使得链表中相邻的页的物理位置也相邻,
就能使用顺序I/O了。
段:表空间是由各个段组成的,段是由多个区组成,段一般分为数据段、索引段、回滚段。
索引段:存放B+树的非叶子节点的区的集合。
数据段:存放B+树的非叶子节点的区的集合。
回滚段:存放的是回滚数据的区的集合。
InnoDb提供了四种格式:Redundant(mysql5.0版本之前格式),Compact(mysql5.1版本之后)是一种紧凑格式,
设计的初衷是为了让一个数据页多存放记录。Dynamic和Compressed都是基于Compact改进了一点东西,从MySQL5.7版本之后
默认格式。

mysql中NULL值如何存放:mysql中的Compact行格式会用NULL值列表来标记值为NULL的列,NULL值并不会存储在行格式的真实
数据部分。NULL值列表会占用1字节空间,表中所有字段都定义成NOT NULL行格式中就不会存在NULL值列表。这样可以节省1字
节空间。
mysql中varchar(n)实际占用数据的大小:mysql的Compact行格式中会用变长字段长度列表,存储变长字段实际占用的数据
的大小。
行溢出mysql处理:Compact行格式针对行溢出的处理是,在记录的真实数据处只会保存该列的一部分数据,把剩余数据放到
溢出页中,然后真实数据处用20字节存储指向溢出页的地址,从而可以找到剩余数据所处的页。

索引:帮助存储引擎快速获取数据的一种数据结构,形象的说就是索引是数据的目录。说白了就是如何存储数据、
如何为存储的数据建立和如何更新、查询数据等技术实现方法。InnoDB是在MySQL5.5之后成为默认的存储引擎。
在创建表时,如果有主键默认会使用主键作为聚簇索引的索引键(key),如果没有主键,会选择第一个不包含NULL值的唯一列作为
聚簇索引的索引键(key)。都没有的时候,InnoDB将自动生成一个隐式自增id作为聚簇索引的索引键(key),其他索引都属于辅助
索引,也称为二级索引或非聚集索引。创建的主键索引和二级索引默认使用B+Tree索引。

B+Tree 是一种多叉树,叶子节点存放数据,非叶子节点存放索引,每个节点李的数据是按照主键顺序存放,每一层父节点的索引值都会出现在下层子节点的索引值
中,因此在叶子节点中,包含了所有的索引值信息,并且每一个叶子节点都有两个指针,分别指向下一个叶子节点和上一个叶子节点,形成一个
双向链表。B+Tree对比于B树和二叉树来说,最大的优势在于查询效率很高,因为即使在数据量很大的情况,查询一个数据的磁盘I/O依然维持在3-4次。

主键索引和二级索引的B+Tree区别:
主键索引的B+Tree的叶子节点存放的是实际数据,所有完整的用户记录都存在在主键索引的B+Tree的叶子节点里。
二级索引的B+Tree的叶子节点存放的是主键值,而不是实际数据。

回表:先检索二级索引中B+Tree的索引值,找到对应的叶子节点,然后获取主键值,探后通过主键索引中的B+Tree树查询对应的叶子节点,然后获取整行数据。
覆盖索引:在二级索引的B+Tree就能查到结果的过程就叫做覆盖索引。
B+树比B树:B+Tree只在叶子节点存储数据,而B树的非叶子节点也要存储数据,所以B+Tree的单个节点的数据量更新,在相同的磁盘I/O下,能查询更多的节点。
B+Tree使用的双链表链接,适合MySql中常见的基于范围的顺序查找,B树无法做到。
B+Tree对比二叉树:B+树的搜索复杂度为o(logdn),一次查询操作只需要3-4次磁盘I/O操作,二叉树每个父节点的儿子个数为2个,搜索复杂度为o(logN),比B+树中每一层都是通过双向链表链接起来,
高出不算,经历的磁盘I/O次数更多。
B+Tree对Hash:等值查询的时候hash复杂度o(1),hash表不适合做范围查询。
联合所以:将多个字段组合成一个索引,该索引就被称为联合索引。使用联合索引时,存在最左匹配原则,按照最左优先的方式进行所以的匹配。
在范围查询的字段可以用到联合索引,但是在范围查询字段的后面的字段无法用到联合索引。

索引区分度:实际开发中简历联合索引时,把区分度大的字段排在前面,这样区分度大的字段越有可能被更多的SQL使用到,区分度就是某个字段column不同值的个数除以表的总行数
联合索引进行排序

什么时候适用索引:
1、字段有唯一性限制的,比如商品编码
2、经常用于WHERE查询条件的字段,如果查询条件不是一个字段,可以建立联合索引。
3、经常用于GROUP BY和ORDER BY的字段,这样在查询的时候不需要再去做一次排序。
索引失效:
1、使用左或者左右模糊匹配的时候,也就是like %xx 或者 like %xx%
2、在查询条件中对索引列做了计算、函数、类型转换操作
3、联合索引要正确使用需要遵循最左匹配原则
4、在where子句中,如果or前的条件列是索引列,而在or后的条件列不是索引列
MYSQL EXPLANIN详解:
1、id:查询序号,sql语句执行顺序
2、select_type:查询类型,常见的值,SIMPLE PRIMARY,DERIVED和UNION
simple:简单查询(没有union和子查询)
derived:子查询(在from列表中包含的子查询)
subquery:映射为子查询
union:联合查询
3、table输出的行所用的表
4、type连接类型,性能type》system/const》eq_ref》ref》ref_or_null》index_merge》range》index——all
type=NULL 在优化过程中就已经得到结果,不用再访问表或者索引 。
type=const/system 常量 在整个查询过程中,这个表最多只会有一条匹配行。一定是用到primary key或者unique情况下才会是const。
type=eq_ref使用有唯一性索引查询(主键或者唯一性索引)用再一个索引的所有部分被联接使用并且是UNIQUE或者PRIMARY key
type=ref 非唯一性索引访问,返回所有匹配某个单个值的行。
type=ref_or_null 该联接类型如果ref类型,结果包含空行
type=range 所有范围扫描,常见于 <,<=,>,>=between,in
type=index 该联节类型与ALL相同都是扫描表,index只对索引树扫描,all是对数据表文件的扫描,Extra列表中看到“Using index”,说明mysql正在使用覆盖索引,只扫描索引的数据。
type=ALL对于每个来自于先前的的表的行组合,进行完整的表扫描。
5、possible_keys:表示mysql可以从中选择查找表中的行的索引,如果此列是NULL,则没有相关的索引,如果这个列出现大量可能被使用的索引(例如多余3个),意味着索引数量太多,
也可能提示存在无效的索引。
6、key 该key列支出mysql优化器决定选择使用那个索引来优化对该表的访问。
7、key_len :定义了mysql在索引里使用的字节数,在不损失精度性的情况下,长度越短越好。
8、ref列显示使用那个列或者常数与key一起从表中选择数据行。指出对key列所选择的索引的查找方式,常见值有const,func,null,具体字段名,
当key列为NULL,即不使用索引时。
9、row:这一列是mysql评估为了找到所需的行而要读取的行数。这个数字是内嵌循环关联计划里的循环数目。
10、Extra:
列出包含MYSQL查询的详细信息。
Not exists:不存在信息
range checked for each record:没有找到合适的索引
Useing index condition:出现这个说明mysql使用了覆盖索引,避免访问了表的数据行,效率不错。
using temporary:mysql对查询结果进行排序的时候使用了一张临时表。
using filesort:mysql对数据不是按照表内的索引顺序进行读取,而是使用了其他资源重新排序。
using where:表示mysql服务器从存储引擎收到查询数据,再进行“后过滤”。

explain解析执行计划:首先看type,如果类型是ALL时,预计会进行全表扫描,建议适当创建索引,避免全表扫描。再看Extra列的结果,
如果出现Using temporary或者Using filesort:mysql对数据不是按照表内的索引顺序进行读取,而是使用了其他资源重新排序。
Using temporary:表示要创建临时表以满足需求,通常是因为group by的列没有索引,或者group by 或者order by的列不一样,也需要创建
临时表,建议添加适当索引
Using filesort:表示无法利用索引完成排序,也有可能是因为表连接太多,排序字段不是驱动表中的字段,无法利用索引完成排序。
Using where 通常是因为全表扫描或者全索引扫描(type显示ALL或者index),又加上了Where条件。

事务特性:
原子性:一个事务的所有操作要么全部完成,要么全部不完成。undo log (重做日志)来保证。
一致性:事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。通过持久性+原子性+隔离性来保证。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性防止多个事务并发执行由于交叉执行而导致数据不一致。通过MVCC(多版本并发控制)或锁机制来保证。
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。通过redo log(重做日志)来保证。
在同时处理多个事务的时候,可能出现脏读,不可重复读,幻读等问题。
脏读:如果一个事务读到另外一个未提交事务修改过的数据就意味着发生了脏读现象。
不可重复读:在一个事务内部多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了不可重复读现象。
幻读:在一个事务内多次查询某个符合查询条件的记录数量,如果出现前后两次查询到的记录数量不一样的情况,意味着发生了幻读现象。
事务隔离级别:
读未提交:指一个事务还没有提交时,它做的变更就被其他事务看到了。
读已提交:指一个事务提交之后,它的变更才被其他事务看到。
可重复读:指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,mysql InnoDb引擎的默认隔离级别。
串行化:会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等到前一个事务执行完成,才能继续执行。


日志:
undo log(回滚日志):是Innodb存储引擎层生成的日志,实现了事务中的原子性,只要用于事务回滚和MVCC,undo log 是一种用于撤销回退的日子,在事务没提交之前,
mysql会先记录更新前的数据到undo log日志文件里面,当事务回滚时,可以利用undo log来进行回滚。一条记录每一次更新操作产生的undo log格式都有一个
roll_pointer指针和一个trx_id事务id,通过trx_id可以知道该记录那个事务被修改,roll_pointer指针可以将这些undo log串成一个链表。mvcc是通过ReadView+undo log
实现,undo log 为每条记录保存多份历史数据,mysql在指向快照读(普通select语句)的时候,会根据事务的Read View里的信息,顺着undo log的版本链找到满足其可见性的记录。
redo log(重做日志):是innodb 存储引擎层生成的日志,实现事务中的持久性,主要用于掉电等故障恢复。redo log 是物理日志,记录某个数据页做了什么修改,每当执行一个
事务就会产生这样的一条或者多条物理日志。在事务提交时,只要先将redo log持久化到磁盘即可,可以不需要等到降缓存在Buffer Pool里的脏页数据持久化到磁盘。当系统崩溃
时,虽然脏页数据没有持久化,但是redo log已经持久化,接着mysql重启后,可以根据redo log的内容,将所有数据恢复到最新的状况。redo log保证了事务四大特性中的持久性。
写入redo log 的方式使用了追加操作,,所以磁盘操作是顺序写,而写入数据需要先找到写入位置,然后才写到磁盘,所以磁盘操作是随机写。磁盘的顺序写比随机写高效的多,
因此redo log写入磁盘的开销更小。
binglog(归档日志):是Server层生成的日志,主要用于数据备份和主从复制。mysql在完成一条更新操作后,server层还会生成一条binglog,等之后事务提交的时候,会将该事务
执行过程中产生的所有binglog统一写入binlog文件。binlog文件是记录了所有数据库表结构变更和表数据修改的日志,不会记录查询类的操作。
redo log 和undo log 区别:
redo log 记录了此次事务完成后的数据状态,记录的是更新之后的值。
undo log记录了此次事务开始钱的数据状态,记录的是更新之前的值。
事务提交之前发生了崩溃,重启后会通过undo log回滚事务,事务提交之后发生了崩溃,重启后会通过redo log恢复事务。
redo log 和 binlog区别:
1、 binlog是mysql的server层实现的日志,所有存储引擎都可以使用。redo log 是innodb存储引擎实现的日志。
2、binlog有3种格式,分别为statement(默认格式)row,mixed
statement:每一条修改的sql都会被记录到binlog中(相当于记录了逻辑操作,所以针对这种格式,binlog可以称为逻辑日志),主从复制中slave端再根据SQL语句重现。但
statement有动态函数问题,比如uuid或者now这些函数,在主库上执行的结果并不是从库上执行的结果,这种随时在变的函数会导致复制的数据不一致。
row:记录行数据最终被修改成什么样,不会出现statement下动态函数的问题,row的缺点是每行数据的变化结果都会被记录,比如执行批量update语句,更新多少行数据就会
产生多少条记录,使binlog文件过大,而在statement格式下只会记录一个udpate语句而已。
mixed:包含了statement和row模式,它会根据不同的情况自动使用row模式和statement
redo log是物理日志,记录的是某个数据页做了什么修改
3、写入方式不同,binlog是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日子,保存的是全量的日志。redo log是循环写,日志空间大小是固定,
全部写满就从头开始,保存未被刷入磁盘的脏页日志。
4、binlog 用于备份恢复,主从复制,redo log 用于掉电等故障恢复。
mysql读取数据:
如果数据存在于Buffer Pool中,客户端就会直接读取Buffer Pool中的数据,否则去磁盘中读取。当修改数据时,如果数据存在于Buffer Pool中,那就直接修改Buffer Pool中
数据所在的页,然后将其设置为脏页(该页的内存数据和磁盘上的数据已经不一致),为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入
磁盘。
在mysql启动的时候,InnoDB会为Buffer Pool申请一片连续的内存空间,然后按照默认的16KB的大小换分出一个个的页,Buffer Pool中的页叫做缓存页。Buffer Pool除了缓存
索引页和数据页,还包括了Undo页,插入缓存,自适应哈希索引,锁信息等等。
mysql主从复制实现:
mysql的主从复制依赖于binlog,也就是记录mysql上所有变化并以二进制形式保存在磁盘上,复制过程就是将binlog中的数据从主库传输到从库上。
mysql集群的主从复制过程梳理成3个阶段:
写入binlog:主库写入binlog日志,提交事务,并更新本地存储数据。mysql主库在收到客户端提交事务请求之后,会先写入binlog,再提交事务,更新存储引擎中的数据,
事务提交完成后,返回给客户端“操作成功”的响应。
同步binlog:把binlog复制到所有从库上,每个从库把binlog写到暂存日志中。从库会创建一个专门的I/O线程,连接主库的log dump线程,来接受主库的binlog日志,再把
binlog信息写入relay log的中继日志李,再返回给主库“复制成功"的响应。
回放binlog:回放binlog,并更新存储引擎中的数据。从库会创建一个用户回放binlog的线程,去读relay中继日志,然后回放binlog更新存储引擎中的数据,最终实现主从
的数据一致性。
mysql主从复制模型:
同步复制:mysql主库提交事务的线程要等待所有从库的复制成功响应,才返回客户端结果。
异步复制(默认模型):mysql主库提交事务的线程并不会等待binlog同步到各从库,就返回客户端结果,这种模式一旦主机宕机,数据就会发生丢失。
半同步复制(mysql 5.7之后)事务线程不用等待所有的从库复制响应成功,只要一部分复制成功响应回来就行。

在持久化redo log和binlog这两份日志的时候,如果出现半成功的状态,就会造成主从环境的数据不一致性。redo log影响主库的数据,binlog影响从库的数据,
所以redo log和binlog必须保持一致才能保证主从一致性。 mysql为了避免出现两份日志之间的逻辑不一致的问题,使用了两阶段提交来解决,两阶段提交其实是
分布式事务一致性协议,它可以保证多个逻辑操作要不全部成功,要不全部失败,不会出现半成功状态。

redo log两阶段:
将redo log写入拆分成两个步骤,prepare和commit,中间再穿插写入binlog,具体如下:
prepare阶段:将XID(内部XA事务的ID)写入到redo log,同时将redo log对应的事务状态设置为prepare,然后降redo log持久化到磁盘即可,可以不需要等到降缓存在Buffer
commit阶段:把XID写入到binlog,然后将binlog持久化到磁盘,接着调用引擎的提交事务接口,降redo log设置为commit。


内核能力:
1、管理进程、线程、决定哪个进程、线程使用cpu,也就是进程调度的能力
2、管理内存,决定内存的分配和回收,也就是内存管理的能力
3、管理硬件设备,为进程与硬件设备之间提供通信能力,也就是硬件通信能力。
4、提供系统调用,如果应用程序要运行更高权限运行的服务,那么久需要系统调用,它是用户程序鱼操作系统之间的接口。
内存区域:
1、内核空间:这个内存空间只有内核程序可以访问
2、用户空间:这个内存空间专门给应用程序使用;
用户空间的代码只能访问一个局部的内存空间,而内核空间的代码可以访问所有内存空间。因此,当程序使用用户空间时,我们常说该程序在用户态执行,而当程序使用内核空间时,程序则在内核态执行。
应用程序如果需要进入内核空间,就需要通过系统调用。
虚拟地址:
操作系统会为每个进程分配独立的一套虚拟地址,把进程所用的地址隔离开来。操作系统会提供一种机制,将不同进程的虚拟地址和不同的内存的物理地址映射起来。
程序所使用的内存地址叫做虚拟内存地址,实际存在硬件里面的地址叫做物理内存地址。
malloc:
malloc()分配的是虚拟内存,如果分配后的虚拟内存没有被访问,虚拟内存不会映射物理内存,这样就不会占用物理内存。只有再访问已分配的虚拟地址空间的时候,操作系统通过查找页表,发现虚拟内存对应的页没有
在物理内存中,就会出发缺页中断,然后操作心痛会建立虚拟内存和物理内存之间的映射关系。
内存回收:
后台内存回收:在物理内存紧张的时候,会唤醒kswapd内核线程来回收内存,这个回收内存的过程异步,不会阻塞进程的执行。
直接内存回收:如果后台异步回收跟不上进程内存申请的速度,就会开始直接回收,这个回收内存的过程是同步的,会阻塞进程的执行。
OOM:
oom killer机制会根据算法选择一个占用物理内存较高的进程,然后将其杀死,以便释放内存资源。,如果物理内存依然不足,OOM killer 会继续杀死占用物理内存较高的进程,直到释放足够的内存。
进程通信方式:
管道:内核里面的一串缓存,从管道的一段写入数据,实际是缓存在内核中,另一端读取,也就是从内核中读取这段数据。管道只能一端写入,另一端读出。
消息队列:消息队列是保存在内核中的消息链表,发送数据时,会分成一个一个独立的数据单元,也就是消息体(数据块),消息体是用户自定义的数据类型,消息的发送方和接收方要约定好消息体的数据类型。
消息队列的不足两个地方,一是通信不及时,二是福建有大小限制。消息队列不适合比较大的数据传输,消息队列通信过程中存在用户态与内核态之间的数据拷贝开销。
共享内存:共享内存的机制,是拿出一块虚拟地址空间来映射到相同的物理内存中。这样这个进程写入的东西,另外一个进程马上就能看到了,都不需要拷贝来拷贝去,提高了进程间通信的速度。
信号量:是一个整型的计数器,用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据。信号量表示资源的数量,控制信号量的两种原子操作:一个是P操作,这个操作会把信号量减去1,相减后,如果信号量《0,则表示资源已被占用,进程需要阻塞等待。
另一个操作是V操作,这个会把信号量加上1,加后信号量《=0,表示当前有阻塞中的进程,会将进程唤醒运行。
Socket:
跨网络与不同主机上的进程之间同学,需要用到socket,也可以在同主机上进程间通信。int socket(int domain, int type, int protocal)
原子操作:要么全部执行,要么都不执行,不能出现执行到一半的中间状态。
自旋锁:当获取不到锁时,线程就会一直while循环,不做任何事情。
互斥条件:是指多个线程不能同时使用同一个资源
线程的上下文切换:当两个线程属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不懂,只需要切换线程的私有数据,寄存器等不共享的数据。
互斥锁:是一种独占锁,比如当线程 A 加锁成功后,此时互斥锁已经被线程 A 独占了,只要线程 A 没有释放手中的锁,线程 B 加锁就会失败,于是就会释放 CPU 让给其他线程,既然线程 B 释放掉了 CPU,自然线程
B 加锁的代码就会被阻塞
自旋锁:通过CPU提供的CAS函数,在用户态完成加锁和解锁操作,不会主动产生线程上下文切换,相比互斥锁来说,会快一些,开销也小一些。自旋锁是比较简单的一种锁,一直自旋,利用CPU周期,知道锁可用。
读写锁:
由读锁和写锁两部分构成,如果只读取共享资源用读锁加锁,如果要修改共享资源则用写锁加锁。
悲观锁:互斥锁,自旋锁,读写锁都属于悲观锁,悲观锁做事比较别管,它认为多线程同时修改共享资源的概率比较高,于是很容易冲突,所以访问共享资源前,先要上锁。
乐观锁:乐观锁做事比较乐观,假定冲突的概率比较低。
线程奔溃,进程不一定会崩溃:如果线程因为非法访问内存引起崩溃,进程肯定崩溃。在各个线程的地址空间是共享,某个线程对地址的非法访问会导致内存的不确定性,进而可能会影响其他线程。
进程调度算法:
先来先服务调度算法:最简单的一个调度算法,就是非抢占式的先来先服务算法。每次从就绪队列选择最先进入队列的进程,然后一直运行,知道进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。
最短作业优先:会优先选择运行时间最短的进程来运行。
高响应比优先调度算法:每次进行进程调度时,先计算响应优先级,然后把响应比优先级最高的进程投入运行。
时间片轮转调度算法:每个进程被分配一个时间段,成为时间片,即允许进程在该时间段中运行,如果时间片用完,进程还在运行,会将进程从CPU释放出来,并把CPU分配给另外一个进程。

IO多路复用:多个请求复用一个进程,这就是多路复用。进程可以通过一个系统调用函数从内核中获取多个事件。
select:实现多路复用的方式是,将已经链接的Socket都放到一个文件描述符集合,然后调用select函数将文件描述符集合拷贝到内核李,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符
集合的方式,当检查到有事件产生后,将此socket标记为可读或可写,接着再把整个文件描述符集合拷贝回用户态里。默认最大值为1024
poll:不再用BitsMap来存储所关注的文件描述符,取而代之用动态数组,以链表形势来组织,突破了select的文件描述符个数限制,当然还会受到系统文件描述符限制。
epoll:在内核里使用红黑树来跟踪进程所有待检测的文件描述字。使用事件驱动机制,内核里维护了一个链表来记录就绪事件,当某个socket有时间发生时,通过回调函数内核会将其加入到这个就绪事件列表中。
阻塞I/O:当用户执行read,线程会被阻塞,一直等到内核数据准备好,并把数据从内核缓冲区拷贝到应用程序的缓冲区中,当拷贝完成,read才会返回。阻塞等待的是内核数据准备好和数据从内核态拷贝到用户态。
非阻塞I/O:非阻塞的read请求在数据未准备好的情况下立即返回,可以继续往下执行,此时应用程序不断轮询内核,直到数据准备好,内核将数据拷贝到应用程序缓冲区,read调用才可以获取到结果。
Proactor采用异步I/O技术,被称为异步网络模型。
Reactor和Proactor的区别:
Reactor是非阻塞同步网络模式,感知的是就绪可读写事件。在每次感知到有事件发生(比如可读写就绪事件)后,就需要应用进程主动调用read方法来完成数据的读取,也就是要应用进程主动将soket接受缓存中的数据读到
应用进程内存中,这个过程是同步的,读取完数据后应用进程才能处理数据。
Proactor是异步网络模式,感知的是已完成的读写事件。在发起异步读写请求时,需要传入数据缓冲区的地址(用来存放结果数据)等信息。这样系统内核才可以自动帮我们把数据的读写工作写完,
这里的读写工作全程由操作系统来做,bin不需要像Reactor那样还需要应用进程主动发起read/write来读写数据,操作系统完成读写工作后,就会通知应用进程直接处理数据。
pv分析:PV全称Page View,用户访问一个页面就是一次PV,PV的数量并不代表真实的用户数量,只是个点击量,对于nginx的acess.log日志文件来说,就是有多少条日志记录就是有多少条PV
直接使用 wc -l 文件名 命令就可以查看整体的PV
PV分组:nginx的acess.log日志文件有访问时间的信息,可以根据访问时间进行分组,可以使用awk命令,awk命令默认是以空格为分隔符,由于访问时间再日志的第四列,
因此可以使用awk '{print$4}' access.log 命令把访问时间的信息过滤出来。awk '{print substr($4,2,11)}' access.log | sort | uniq -c,使用awk的substr函数,从第二个字符开始,截取11个字符
接着使用sort对日期进行排序,然后使用 uniq -c 进行统计,于是按天分组的PV就出来了。
UV分析:全称Uniq Visitor,它代表访问人数
awk '{print $1}' access.log | sort | uniq | wc -l awk '{print $1}' access.log,取日志的第 1 列内容,客户端的 IP 地址正是第 1 列,sort,对信息排序,uniq,去除重复的记录,wc -l,查看记录条数
awk '{print substr($4, 2, 11) " " $1}' access.log | sort | uniq 第一次 ack 是将第 4 列的日期和第 1 列的客户端 IP 地址过滤出来,并用空格拼接起来,然后 sort 对第一次 ack 输出的内容进行排序,接着用 uniq 去除重复的记录,也就说日期 +IP 相同的行就只保留一个

 

posted @ 2023-02-24 15:06  菜鸟的地盘  阅读(29)  评论(0编辑  收藏  举报