2022PHP面试题分享及归档(持续更新)

前言:
过完年之后打算寻找新的工作机会,发现之前自己对于很多基础的面试理解和学习不够深刻,为了鼓励自己持续前进所以最近开始在论坛和搜索引擎上开始学习和总结相关知识,其中有一些题目时论坛里面一些前辈分享过的题目或者答案,还有一部分时自己最近面试遇到的问题,基于自己的理解和前辈们的分享归档了一部分,所以分享出来,希望对其他的小伙伴们也有帮助,同时也希望能收到大佬们对于理解有误的地方的指导,最近一段时间会持续更新

本论坛的文章参考的比较多,优先列出来大家参考,如下:

分享一下最近的面试题,都是大厂(BAT TMD
PHP面试问答
[最近面试整理] PHP 面试详解之技术篇

问题目录

  1. php 数组底层实现原理
  2. 冒泡排序
  3. 网络七层协议
  4. tdp 和udp
  5. 三次握手四次挥手
  6. http状态码
  7. http 和https
  8. redis 单线程 为什么快
  9. redis 分布式锁
  10. redis数据结构及使用场景
  11. redis持久化
  12. 秒杀流程
  13. 防sql注入
  14. 事务隔离级别
  15. 索引原理
  16. 分库分表
  17. select 和update流程
  18. binlog 作用及格式
  19. 主从同步原理
  20. 死锁
  21. 优化大分页查询
  22. redis 和mysql 数据一致性
  23. connet和pconnet
  24. zset 为什么使用skiplist
  25. redis的过期删除和淘汰机制
  26. redis常见问题
  27. php-fpm 生命周期
  28. nginx 和php 通信机制
  29. web漏洞及解决
  30. 魔术方法原理及场景
  31. 乐观锁和悲观锁
  32. mysql一次查询能使用几个索引
  1. php数组底层实现原理

     1. 底层实现是通过散列表(hash table) + 双向链表(解决hash冲突)
         hashtable:将不同的关键字(key)通过映射函数计算得到散列值(Bucket->h) 从而直接索引到对应的Bucket
         hash表保存当前循环的指针,所以foreach 比for更快
         Bucket:保存数组元素的key和value,以及散列值h
     2. 如何保证有序性
         1. 散列函数和元素数组(Bucket)中间添加一层大小和存储元素数组相同的映射表。
         2. 用于存储元素在实际存储数组中的下标
         3. 元素按照映射表的先后顺序插入实际存储数组中
         4. 映射表只是原理上的思路,实际上并不会有实际的映射表,而是初始化的时候分配Bucket内存的同时,还会分配相同数量的 uint32_t 大小的空间,然后将 arData 偏移到存储元素数组的位置。        
     3. 解决hash重复(php使用的链表法)1. 链表法:不同关键字指向同一个单元时,使用链表保存关键字(遍历链表匹配key) 
         2. 开放寻址法:当关键字指向已经存在数据的单元的时候,继续寻找其他单元,直到找到可用单元(占用其他单元位置,更容易出现hash冲突,性能下降)
     4.基础知识
         链表:队列、栈、双向链表、
         链表    :元素 + 指向下一元素的指针
         双向链表:指向上一元素的指针 + 元素 + 指向下一元素的指针

    参考:
    算法的时间与空间复杂度(一看就懂) https://zhuanlan.zhihu.com/p/50479555
    LeetCode0:学习算法必备知识:时间复杂度与空间复杂度的计算 https://cloud.tencent.com/developer/article/1769988

  2. 冒泡排序的时间复杂度和空间复杂度

     1. 代码实现
             $arr = [2, 4, 1, 5, 3, 6];
             for ($i = 0; $i < (count($arr)); $i++) {
                 for ($j = $i + 1; $j < (count($arr)); $j++) {
                     if ($arr[$i] <= $arr[$j]) {
                         $temp = $arr[$i];
                         $arr[$i] = $arr[$j];
                         $arr[$j] = $temp;
                     }
                 }
             }
         result : [6,5,4,3,2,1]
    
         评论区有其他同学对于上面的写法有不用意见(第一种写法虽然能达到排序的目的,但是他觉得按照冒泡排序的明确定义来说不是冒泡排序),所以另一种写法我也列举出来,有更专业的大佬们可以指正一下,代码实现如下  
     $list = [2, 4, 1, 7, 9, 3];
     $len = count($list);
    
     for ($i = $len - 1; $i > 0; $i--) {
         for ($j = 0; $j < $i; $j++) {
             if ($list[$j] > $list[$j + 1]) {
                 $tmp = $list[$j];
                 $list[$j] = $list[$j + 1];
                 $list[$j + 1] = $tmp;
             }
         }
     }
     var_dump($list);
    
     2. 计算原理
         第一轮:将数组的第一个元素和其他所有的元素进行比较,哪个元素更大,就换顺序,从而冒泡出第一大(最大)的元素
         第一轮:将数组的第二个元素和其他所有的元素进行比较(第一大已经筛选出来不用继续比较了),哪个元素更大,就换顺序,从而冒泡出第二大的元素
         ... 依次类推,冒泡从大到小排序的数组
    
         平均时间复杂度:O(n^2)  :
         最优时间复杂度:O(n) :需要加判断,第一次循环如果一次都没有交换就直接跳出循环
    
         空间复杂度:O(1),交换元素的时候的临时变量占用的空间
         最优空间复杂度:O(1),排好序,不需要交换位置
     3. 时间复杂度和空间复杂度
         时间复杂度:全程为渐进时间复杂度,估算对处理器的使用效率(描述算法的效率趋势,并不是指算法具体使用的时间,因为不同机器的性能不一致,只是一种效率计算的通用方法)
         表示方法:大O符号表示法 
         复杂度量级:
             常数阶O(1)
             线性阶O(n)
             平方阶O()
             立方阶O()
             K次方阶O(n^k)
             指数阶(2^n)
             对数阶O(logN)
             线性对数阶O(nlogN)
         时间复制类型:
             最好时间复杂度
             最坏时间复杂度
             平均时间复杂度
             均摊时间复杂度
    
         空间复杂度:全程渐进空间复杂度,估算对计算机内存的使用程度(描述算法占用的存储空间的趋势,不是实际占用空间,同上)

参考:
算法的时间与空间复杂度(一看就懂) https://zhuanlan.zhihu.com/p/50479555
LeetCode0:学习算法必备知识:时间复杂度与空间复杂度的计算 https://cloud.tencent.com/developer/article/1769988

  1. 网络七层协议及 TCP 和 UDP

     应用层、表示层、会话层、传输层、网络层、(数据)链路层、物理层
     记忆套路:
     首字:应表会传(物链网)
         第一个字:应用层(出现次数多,易忆)
         前四个正向:应表 - 会传
         后三个反向:物联网谐音比网链物更好记
  2. TCP 和 UDP 的特点和区别

     1. 都是属于传输层协议
     2. TCP
         1. 面向连接,所以只能一对一
         2. 面向字节流传输
         3. 数据可靠,不丢失
         4. 全双工通信
     3. UDP(根据TCP特点反记)
         1. 无连接,支持一对一,一对多,多对多
         2. 面向报文传输
         3. 首部开销小,数据不一定可靠但是速度更快

参考:
github:colinlet/PHP-Interview-QA =>PHP 面试问答 网络篇

  1. TCP的三次握手和四次挥手

     1. 三次握手:
          1. 第一次:客户端发送SYN = 1,seq = client_isn
          作用:
             客户端:无
             服务端:确认自己的接收功能和客户端的发送功能
          2. 第二次:服务端发送SYN = 1,seq = server_isn,ACK =client_isn +1    
           作用:
             客户端:确认自己发送和接收都正常,确认服务端的接收和发送正常
             服务端:确认自己的接收正常,确认服务端的发送正常(这时候服务端还不能确认客户端接收是否正常)
         3. 第三次:客户端发送SYN = 0,  ACK =  server_isn+1,seq =client_isn+1 
         作用:双方确认互相的接收和发送正常,建立连接
    
     2. 四次挥手
         1. 第一次:客户端发送FIN
             作用:告诉服务端我没有数据发送了(但是还能接收数据)
         2. 第二次:服务端发送ACK
             作用:告诉客户端收到请求了,可能服务端可能还有数据需要发送,所以客户端收到进入FIN_WAIT状态,等服务端数据传输完之后发送FIN
         3. 第三次:服务端发送FIN
             作用:服务端告诉客户端我发送完了,可以关闭连接了。
         4. 第四次:客户端发送ACK  
             作用:客户端收到FIN之后,担心服务端不知道要关闭,所以发送一个ACK,进入TIME_WAIT,等待2MSL之后如果没有收到回复,证明服务端已经关闭了,这时候客户端也关闭连接。
         注意:
         1. 当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据
         2. 最后需要等待2MSL是因为网络是不可靠的,如果服务端没有收到最后一次ACK,服务端会重新放FIN包然后等客户端再次发送ACK包然后关闭(所以客户端最后发送ACK之后不能立即关闭连接)

参考:
github:colinlet/PHP-Interview-QA =>PHP 面试问答 网络篇
TCP协议中的三次握手和四次挥手(图解) https://blog.csdn.net/whuslei/article/details/6667471
“三次握手,四次挥手”你真的懂吗? https://zhuanlan.zhihu.com/p/53374516
TCP 的特性 https://hit-alibaba.github.io/interview/basic/network/TCP.html

  1. HTTP状态码

     1.  状态码分类  
             - 1xx:信息,服务器收到请求,需要请求者继续操作
             - 2xx:成功
             - 3xx:重定向
             - 4xx:客户端错误
             - 5xx:服务端错误
    
     2. 常用状态码
    
         200:请求成功
         301:永久重定向
         302:临时移动
         400 bad request:客户端请求语法错误
         401 unauthorized:客户端没有权限
         403 forbidden:服务器拒绝客户端请求
         404 not found:客户端请求资源不存在
         500 Internal Server Eerro:服务器内部错误
         502 bad gateway:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应
         503 Service Unavailable 超载或系统维护
         504 Gateway timeout:网关超时
    
     3. 502 的原因及解决方法
         原因:nginx将请求提交给网关(php-fpm)处理异常导致
             1. fastcgi 缓冲区设置过小
                 fastcgi_buffers 8 16k;
                  fastcgi_buffer_size 32k;
             2. php-cgi的进程数设置过少
                 查看FastCgi进程数:netstat -anpo | grep "php-cgi"| wc -l
                 调整参数最大子进程数:max_children
                     一般按照单个进程20M计算需要需要设置的子进程数 
             3. max_requests(内存溢出或频繁重启)
                 参数指明每个children最多能处理的请求数量,到达最大值之后会重启children。
                 设置过小可能导致频繁重启children:
                     php将请求轮询给每个children,在大流量的场景下,每一个children 到达最大值的时间差不多,如果设置过小可能多个children 在同一时间关闭,nginx无法将请求转发给php-fpm,cpu降低,负载变高。
                 设置过大可能导致内存泄露
             4. php执行时间超过nginx等待时间
                     fastcgi_connect_timeout
                     fastcgi_send_timeout
                     fastcgi_read_timeout
             5. fastcgi执行时间
                 max_execution_time

参考:
php+php-fom+nginx配置参数调优详解
nginx报错502

  1. http和HTTPS的区别

     1. 端口:http 80; https :443
     2. http无状态,https是有http + ssl构建的可进行加密传输的协议
     3. http明文传输,https加密传输
     4. http更快,三次握手三个包,https 需要12个包(3个tcp包+9个ssl握手包)
  2. redis分布式锁及问题

     1. 实现:
         加锁:setnx
         解锁:del
         锁超时:expire
     2. 可能出现的问题
         1. setnx 和expire非原子性问题(加锁之后还没来得及设置超时就挂了)
             解决方案:
                 Redis 2.6.12以上版本为set指令增加了可选参数,伪代码如下:set(key,130NX,这样就可以取代setnx指令
         2. 超时误删其他进程锁。(A进程执行超时,导致锁释放,这时候B进程获取锁开始处理请求,这时候A进程处理完成,会误删B进程的锁)
             解决方案:只能删除自己进程的锁 (lua脚本防止B进程获取过期锁之后误删A进程的锁) 
         3. 并发场景,A进程执行超时导致锁释放,这时候B进程获取到锁。
             解决方案:开启守护进程,给当前进程要过期的锁延时。
         4. 单点实例安全问题
             单机宕机之后导致所有客户端无法获取锁
             解决:
                 主从复制,因为是异步完成的所以无法完全实现解决

参考:
使用Redis作为分布式锁的一些注意点
面试官:你真的了解Redis分布式锁吗?

  1. redis为什么是单线程?为什么快?

  2. redis的数据类型及应用场景

    1. string :
        普通的key/value存储
    2. hash:
        hashmap:键值队集合,存储对象信息
    3. list:
        双向链表:消息队列
    4. set:
        value永远为null的hashMap:无序集合且不重复:计算交集、并集、差集、去重值
    5. zset:
        有序集合且不重复:hashMap(去重) + skiplist跳跃表(保证有序):排行榜

    参考:
    Redis五种数据类型及应用场景

  3. redis实现持久化的方式及原理、特点

    1. RDB持久化(快照):指定时间间隔内的内存数据集快照写入磁盘        
            1. fork一个子进程,将快照内容写入临时RDB文件中(dump.rdb),当子进程写完快照内容之后新的文件替换老的文件
            2. 整个redis数据库只包含一个备份文件
            3. 性能最大化,只需要fork子进程完成持久化工作,减少磁盘IO
            4. 持久化之前宕机可能会导致数据丢失        
    2. AOF持久化 :以日志的形式记录服务器的所有的写、删除操作
            1. 每接收到一个写的命令用write函数追加到文件appendonly.aof
            2. 持久化的文件会越来越大,存在大量多余的日志(0 自增100次到100,会产生100条日志记录)
            3. 可以设置不同的fsync策略
                1. appendfsync everysec :1s一次,最多丢失1s的数据(默认)
                2. appendfsync always    :每次变动都会执行一次
                3. appendfsync no          :不处理
            4. AOF文件太大之后会进行重写:压缩AOF文件大小
                1. fork一个子进程,将redis内地数据对象的最新状态写入AOF临时文件(类似rdb快照)
                2. 主进程收到的变动会先写入内存中,然后同步到老的AOF文件中(重写失败之后也能保证数据完整性)
                3. 子进程完成重写之后会将内存中的新变动同步追加到AOF的临时文件中
                4. 父进程将临时AOF文件替换成新的AOF文件,并重命名。之后收到的新命令写入到新的文件中

参考:
Redis专题:万字长文详解持久化原理
Redis持久化
RDB 和 AOF 持久化的原理是什么?我应该用哪一个?它们的优缺点?

  1. 秒杀设计流程及难点

    1. 静态缓存
    2. nginx 负载均衡  
        三种方式:DNS轮询、IP负债均衡、CDN
    3. 限流机制
        方式:ip限流、接口令牌限流、用户限流、header动态token(前端加密,后端解密)
    4. 分布式锁
        方式:
            1. setnx + expire (非原子性,redis2.6 之后set保证原子性)
            2. 释放锁超时 (开启守护进程自动续时间)
            3. 过期锁误删其他线程(requestId验证或者lua脚本保证查 + 删的原子性)
    5. 缓存数据
        方式:
            1. 缓存击穿:缓存数据预热 + 布隆过滤器/空缓存 
            2. 缓存雪崩:缓存设置随机过期时间,防止同一时间过期
    6. 库存及订单
        1. 扣库存
            1. redis 自减库存,并发场景下可能导致负数,影响库存回仓:使用lua脚本保证原子性
            2. redis预扣库存之后,然后使用异步消息创建订单并更新库存变动 
            3. 数据库更新库存使用乐观锁:where stock_num - sell_num > 0
            4. 添加消息发送记录表及重试机制,防止异步消息丢失
        2. 创建订单
            1. 前端建立websocket连接或者轮询监听订单状态
            2. 消费验证记录状态,防止重复消费
        3. 回仓
            1. 创建订单之后发送延时消息,验证订单支付状态及库存是否需要回仓 
  2. 防sql注入

    1. 过滤特殊字符
    2. 过滤数据库关键字
    3. 验证数据类型及格式
    4. 使用预编译模式,绑定变量
  3. 事务隔离级别(更新时间2022-03-02 15:56:14,之前的有误)

    1. 标准的sql隔离级别实现原理
        1. 未提交读:其他事务可以直接读到没有提交的:脏读
            1. 事务对当前被读取的数据不加锁
            2. 在更新的瞬间加行级共享锁到事务结束释放
        2. 提交读:事务开始和结束之间读取的数据可能不一致,事务中其他事务修改了数据:不可重复度
            1. 事务对当前读取的数据(被读到的时候)行级共享锁,读完释放
            2. 在更新的瞬间加行级排他锁到事务结束释放
        3. 可重复读:事务开始和结束之前读取的数据保持一致,事务中其他事务不能修改数据:可重复读
            1. 事务对当前读到的数据从事务一开始就加一个行级共享锁
            2. 在更新的瞬间加行级排他锁到事务结束释放
            3. 其他事务再事务过程中可能会新增数据导致幻读 
        4. 串行化
            1. 事务读取数据时加表级共享锁
            2. 事务更新数据时加表级排他锁
    2. innodb的事务隔离级别及实现原理(!!和上面的不一样,区分理解一个是隔离级别 一个是!!事务!!隔离级别)
        1. 基本概念
            1. mvcc:多版本并发控制:依赖于undo log 和read view
                1. 让数据都读不会对数据加锁,提高数据库并发处理能力
                2. 写操作才会加锁
                3. 一条数据有多个版本,每次事务更新数据的时候会生成一个新的数据版本,旧的数据保留在undo log
                4. 一个事务启动的时候只能看到所有已经提交的事务结果
            1. 当前读:读取的是最新版本
            2. 快照读:读取的是历史版本
            4. 间隙锁:间隙锁会锁住一个范围内的索引
                1. update id between 10 and 20 
                2. 无论是否范围内是否存在数据,都会锁住整个范围:insert id = 15,将被防止
                3. 只有可重复读隔离级别才有间隙锁
            5. next-key lock:
                1. 索引记录上的记录锁+ 间隙锁(索引值到前一个索引值之间的间隙锁)
                2. 前开后闭
                3. 阻止幻读
        事务隔离级别
            1. 未提交读
                1. 事务对当前读取的数据不加锁,都是当前读
                2. 在更新的瞬间加行级共享锁到事务结束释放
            2. 提交读
                1. 事务对当前读取的数据不加锁,都是快照读
                2. 在更新的瞬间加行级排他锁到事务结束释放
            3. 可重复读
                1. 事务对当前读取的数据不加锁,都是快照读
                2. 事务再更新某数据的瞬间,必须加行级排他锁(Record 记录锁、GAP间隙锁、next-key 锁),事务结束释放
                3. 间隙锁解决的是幻读问题
                        1. 主从复制的情况下 ,如果没有间隙锁,master库的AB进程 
                        2. A进程 delete id < 6 ;然后还没有commit
                        3. B进程insert id = 3,commit
                        4. A进程提交commit
                        5. 该场景下,主库会存在一条id =3 的记录,但是binlog里面是先删除再新增就会导致从库没有数据,导致主从的数据不一致
                4. MVCC的快照解决的是不可重复读问题
            4. 串行化
                1. 事务读取数据时加表级,当前读
                2. 事务更新数据时加表级排他锁

    参考:
    深入理解MySQL中事务隔离级别的实现原理
    小胖问我:MySQL 事务与 MVCC 原理?
    快照在MVCC中是怎么工作的?
    MVCC我知道,但是为什么要设计间隙锁?
    间隙锁和next-key lock

  4. 索引原理

    索引就是帮助数据库高效查找数据的存储结构,存储再磁盘中,需要消耗磁盘IO
    1. 存储引擎
        1. myisam 支持表锁,索引和数据分开存储适合跨服务器迁移
        2. innodb 支持行锁,索引和数据存储再一个文件
    2. 索引类型
        1. hash索引
            1. 适合精确查询且效率高
            2. 无法排序、不适合范围查询
            3. hash冲突的情况下需要遍历链表(php数组的实现原理、redis zset 的实现原理类似)
        2. b-tree、b+tree
            1. b-tree 和b+tree的去区别
                1. b+tree 的数据全部存储在叶子节点,内部节点只存key,一次磁盘IO能获取到更多的节点
                2. b-tree 的内部节点和叶子节点都存储key和数据,查找数据不需要找到叶子节点,内部节点可以直接返回数据
                3. b+tree 增加了叶子节点到相邻节点的指针,方便返回查询遍历
        3. 聚簇索引和非聚簇索引
            1. 概念
                1. 聚簇索引  :索引和数据存储在一个节点
                2. 非聚簇索引:索引和数据分开存储,通过索引找到数据实际存储的地址
            2. 详解:
                1. innodb 使用的聚簇索引,且默认主键索引为聚簇索引(没有主键索引的时候,选择一个非空索引,还没有则隐式的主键索引),辅助索引指向聚簇索引位置,然后在找到实际存储地址
                2. myisam 使用非聚簇索引,所有的索引都只需要查询一次就能找到数据
                3. 聚簇索引的优势和略势
                    1. 索引和数据在一起,同一页的数据会被缓存到(buffer)内存中,所以查看同一页数据的时候只需要从内存中取出,
                    2. 数据更新之后之只需要维护主键索引即可,辅助索引不受影响
                    3. 辅助索引存的是主键索引的值,占用更多的物理空间。所以会受到影响
                    4. 使用随机的UUID,数据分布不均匀,导致聚簇索引可能扫全表,降低效率,所以尽量使用自增主键id 
    
    # 索引原理补充(2022-03-111. 索引原理
        1. 磁盘IO1. 寻道时间:指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下
            2. 旋转延迟:指的是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms
            3. 传输时间:指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。
            4. 磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分。访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右
        2. 预读
            1. 考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)
            2. 具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。
        3. B+tree
            1. 一次IO将一页的数据加载到内存中
            2. 为什么索引字段尽量小并且数据只存在叶子节点:字节越少,每次IO请求扫描的节点就越多
                1. 假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低
            3. 最左前缀特性:b+数是按照从左到右的顺序来建立搜索树的
    2. 索引创建原则
        1. 最左前缀原则:mysql会一直向右匹配直到遇到范围查询(><、between、like)就停止匹配。联合索引(A,B,C= (A) + (A,B) +(A,B,C) (更新于2022-03-02:之前错误写法: (A) + (B) + (C) + (A,B) +(A,B,C)2. =in可以乱序,mysql查询优化器会自动识别成需要使用索引的类型
        3. 尽量选择区分度高的列作为索引
        4. 索引列不能参与计算
        5. 尽量的扩展索引,不要新建索引,入过已经存在A索引 需要加一个新的索引,建议修改原来的索引为(A,B)联合索引    
    4. OR 是否能走索引及原理
    5. GROUP BY 是否能走索引及原理
    6. ORDER BY 能否走索引及原理

    参考:
    聚簇索引与非聚簇索引(也叫二级索引)–最清楚的一篇讲解
    MySQL索引原理及慢查询优化

  5. 分表(分库)的策略

    1. 流程
        评估容量和分表数量-> 根据业务选定分表key->分表规则(hash、取余、range)->执行->考虑扩容问题
    2. 水平拆分
        1. 根据字段水平拆分为多个表
        2. 每个表的结构相同
        3. 所有分表的合集是全量数量
    3. 垂直拆分
        1. 根据字段垂直拆分
        2. 表结构不一样,分表的同一个关联行是一条完整的数据
        3. 扩展表,热点字段和非热点字段的拆分(列表和详情的拆分)
        4. 获取数据时,尽量避免使用join,而是两次查询结果组合
    4. 问题
        1. 跨库join问题
            1. 全局表:需要关联部分系统表的场景
            2. 冗余法:常用字段进行冗余
            3. 组装法:多次查询的结果进行组装
        2. 跨节点的分页、排序、函数问题
        3. 事务一致性
        4. 全局主键id
            1. 使用uuid -> 会降低聚簇索引效率
            2. 使用分布式自增id
        5. 扩容问题
            1. 升级从库
                1. 从库升级为主库,数据一致,只需要删除冗余数据即可
                2. 成倍扩容:需要在加一倍从库
            2. 双写迁移:
                1. 新数据进行双写,同时写进新老数据库
                2. 旧数据复制到新数据库
                3. 以老数据库为准,验证数据一致性之后删除冗余数据

参考:
常用分库分表方案

  1. select 和update的执行流程

    1. mysql 构成
        1. server层:连接器->缓存器->分析器(预处理器)->优化器->执行器
        2. 引擎层  : 查询和存储数据
    2. select 执行过程
        1. 客户端发送请求,建立连接
        2. server层查找缓存,命中直接返回,否则继续
        3. 分析七分析sql语句以及预处理(验证字段合法性及类型等)
        4. 优化器生成执行计划
        5. 执行器调用引擎API查询结果
        6. 返回查询结果
    3. update执行过程
        1. 基础概念
            1. buffer pool(缓存池),在内存中,下次读取同一页的数据的时候可以直接从buffer pool中返回(innodb的聚簇索引)
            2. 更新数据的时候先更新buffer pool,然后在更新磁盘
            3. 脏页:内存中的缓存池更新了,但是没有更新磁盘
            4. 刷脏:inndb 中有一个专门的进程将buffer pool的数据写入磁盘,每隔一段时间将多个修改一次性写入磁盘
            5. redo log 和 binlog
                1. redo log(重做日志),innodb特有的日志,物理日志,记录修改
                2. redo log是重复写,空间固定且会用完,会覆盖老日志
                3. binlog 是server层共有的日志,逻辑日志,记录语句的原始逻辑
                4. binlog 是追加写到一定大小切换到下一个,不会覆盖以前的日志
                5. redo log主要是用来恢复崩溃,bin log是用来记录归档的二进制日志
                6. redo log只能恢复短时间内的数据,binlog可以通过设置恢复更大的数据
            6. WAL(write-ahead-logging)先写日志方案
                1. 记录日志是顺序IO
                2. 直接写入磁盘(刷盘)是随机IO,因为数据是随机的,可能分布在不同的扇区
                3. 顺序IO的效率更高,先写入修改日志,可以延迟刷盘时机,提高吞吐量
            7. redo log 刷盘机制,check point
                1. redo log大小固定,循环写入
                2. redo log 就像一个圆圈,前面是check point (到这个point就开始覆盖老的日志),后面是write point (当前写到的位置)
                2. write point 和check point 重叠的时候就证明redo log 满了,需要开始同步redo log 到磁盘中了
        2. 执行步骤(两阶段提交 - 分布式事务,保证两个日志的一致性)
            1. 分析更新条件,查找需要更新的数据(会用到缓存)
            2. server 调用引擎层的API,Innodb 更新数据到内存中,然后写入redo log,然后进入prepare
            3. 引擎通知server层开始提交数据
            4. server层写入binlog 日志,并且调用innodb 的接口发出commit请求
            5. 引擎层收到请求之后提交commit
        3. 宕机后数据崩溃恢复规则
            1. 如果redo log 状态为commit ,则直接提交
            2. 如果redo log 状态为prepare,则判断binlog 中的事务是否commit,是则提交,否则回滚
        4. 如果不使用两次提交的错误案例(update table_x set value = 10 where value = 91. 先redo log 再写入binlog
                1. redo log 写完之后,binlog没写完,这时候宕机。
                2. 重启之后redo log 完整,所以恢复数据 value = 10 
                3. bin log日志中没有记录,如果需要恢复数据的时候 value = 9
            2. 先写binlog 再写redo log
                1. binlog 写入完成,redo log 未完成
                2. 重启之后没有redo log ,所以value 还是9
                3. 当需要恢复数据的时候binlog 日志完整,value 更新成10
        5. undo log 
            1. 在更新写入buffer pool之前记录
            2. 如果更新过程中出错,直接回滚到undo log 的状态 

    参考:

    [mysql中select和update语句执行过程](https://blog.csdn.net/zuocheng217/article/details/108985973) 
    [基于Redo Log和Undo Log的MySQL崩溃恢复流程](https://segmentfault.com/a/1190000039180234)
  2. binlog 的作用和三种格式

    作用:
        1. 数据恢复
        2. 主从复制
    格式(二进制文件):
        1. statement 
            1. 记录每次sql语句的原文
            2. 删除一个表只需要记录一条sql语句,不需要记录每一行的变化,节约IO,提高性能,减少日志量
            3. 可能出现主从不一致(存储过程、函数等)
            4. RC隔离级别(读提交),因为binlog 记录顺序是按照事务commit 顺序记录的,所以可能导致主从复制不一致。通过可重复读级别的间隙锁的引入,可以解决。
        2. row          
            1. 记录每条记录的修改情况,不需要记录sql语句的上下文记录
            2. 导致binlog日志量很大
            3. 删除一个表:记录每条记录都被删除的状况 
        3. mixed
            1. 前两个格式的混合版
            2. 根据语句自动选择使用哪一种:
                1. 一般的sql语句修改使用statement
                2. 修改表结构、函数、存储过程等操作选择row
                3. update 和delete 还是会记录全部记录的变化

    参考:
    Mysql Binlog三种格式详细介绍
    Mysql的binlog是做什么的?

  3. 主从同步(主从复制)的原理和问题及读写分离

    1. 解决的问题
        1. 数据分布
        2. 负载均衡
        3. 数据备份,高可用,避免单点失败
        4. 实现读写分离,缓解数据库压力
        5. 升级测试(使用高版本mysql当从库)
    2. 支持的复制类型(binlog 的三种格式)
        1. 基于sql语句的复制
        2. 基于行的复制
        3. 混合型复制
    3. 原理
        1. 基础概念
            1. 从库生成两个线程
                1. I/O线程
                2. SQL线程
            2. 主库生成线程
                1. log dumo 线程
        2. 流程(主节点必须开启bin log功能,)
            1. 从节点开启start slave 命令之后,创建一个IO进程连接到主节点
            2. 连接成功之后,主节点创建一个 log dump线程(主节点会为每一个从节点创一个log dump线程)
            3. 当binlog发生变化时,主节点的dump log线程会读取bin-log内容并发送给从节点
            4. 主节点dump log 线程读取bin-log 的内容时会对主节点的bin-log加锁,读取完成在发送给从节点之前释放锁
            5. 从节点的IO线程接收主节点发送的binlog内容,并将其写入本地relay log 文件中
            6. 主从节点通过binlog文件+position偏移量定位主从同步的位置,从节点会保存接收到的position偏移量,如果从节点发生宕机重启,自动从postion位置发起同步
            7. 从节点的SQL线程复制读取本地relay log的内容,解析成具体的操作并执行,保证主从数据一致性
        4. 主从复制的模式
            1. 异步模式(默认方式)
                1. 可能导致主从不一致(主从延时)
                2. 主节点接收到客户端提交的事务之后直接提交事务并返回给客户端
                3. 如果主节点事务提交之后,log dump还没来得及写入就宕机就会导致主从数据不一致
                4. 不用关心主从的同步操作,性能最好            
            2. 全同步模式
                1. 可靠更高,但是会影响主库相应时间
                2. 主节点接收到客户端提交的事务之后,必须等待binlog 发送给从库,并且所有从库全部执行完事务之后才返回给客户端
            3.  半同步模式
                1.  增加一部分可靠性,增加主库一部分相应时间
                2.  主节点接收到客户端提交的事务之后,等待binlog发送给至少一个从库并且成功保存到本地relay log中,此时主库提交事务并返回给客户端
            4. server-id的配置和server-uuid
                1. server-id用于标识数据库实例,防止在链式主从、多主多从拓扑中导致SQL语句的无限循环
                2. server-id默认值为0,对于主机来说依然会记录二进制日志,但是会拒绝所有的从机连接。
                2. server-id = 0 对于从机来说会拒绝连接其他实例
                3. server-id是一个全局变量,修改之hi偶必须重启服务
                4. 主库和从库的server-id重复时
                    1. 默认replicate-same-server-id = 0,从库会跳过所有主从同步的数据,导致主从数据不一致
                    2. replicate-same-server-id = 1,可能导致无线循环执行sql
                5. 两个从库(BC)server-id重复会导致主从连接异常,时断时连
                    1. 主库(A)发现相同的server-id会断开之前的连接,重新注册新的连接
                    2. BC从库的连接会周而复始的重连
                6. MySQL服务会自动创建并生成server-uuid配置
                    1. 当主从同步时如果主从实例的server-uuid相同会报错退出,不过我们可以通过设置replicate-same-server-id=1来避免报错(不推荐)
    
         5. 读写分离 
             1. 基于代码实现,减少硬件开支
             2. 基于中间代理实现
             3. 主从延时
                 1. 从库性能比主库差
                 2. 大量查询导致从库压力大,消耗大量CPU资源,影响同步速度:一主多从
                 3. 大事务执行:事务执行完之后才会写入binlog,从库读取延时
                 4. 主库ddl(alter、drop、create)

    参考:
    MySQL 主从同步(1) - 概念和原理介绍 以及 主从/主主模式 部署记录
    MySQL主从同步机制和同步延时问题追查
    MYSQL 主从同步详解
    最近面试整理PHP 面试详解之技术篇
    MYSQL 主从同步详解
    MySQL主从同步中的server-id
    关于主从延迟,一篇文章给你讲明白了!

  4. 死锁

    1. 产生的四个必要条件
        1. 互斥条件
        2. 请求与保持条件:一次性分配全部资源,否则一个都不分配
        3. 非剥夺条件:当进程获得一部分资源等待其他资源的时候释放占有的资源
        4. 循环等待条件:
        理解:一个资源只能被一个进程占用,进程获取资源资源还能申请新的资源,并且已经获得的资源不能被剥夺,同时多个进程相互等待其他进程被占用的资源
    2. 解除死锁
        1. 终止进程(全部干掉)
        2. 逐个种植(杀一个看一下有没有解除)

    参考:

    [死锁的原因,和一般的解决方案](https://cloud.tencent.com/developer/article/1483989)
  5. Mysql优化大分页查询 limit 100000(offset),10(page_sie)

    1. 原因
        mysql查询分页数据时不是直接跳过offset(100000),而是取offset + page_size  = 100000 + 10 = 100010条数据,然后放弃其掉前面的100000条数据,所以效率地下
    2. 优化方案
        1. 延时关联:使用覆盖索引
        2. 主键阈值法:主键是自增的情况下,通过条件推算出符合条件的主键最大值&最小值(使用覆盖索引)
        3. 记录上一页的结果位置,避免使用 OFFSET

参考:
MySQL分页查询越来越慢?是时候该优化了!
Mysql优化大分页查询

  1. redis缓存和mysql 数据一致性

    方式:
        1. 先更新redis 再更新数据库
            场景:update set value = 10 where value = 9
            1. redis更新成功:redis value = 10
            2. 数据库更新失败:mysql value = 9 
            3. 数据不一致
        2. 先更新数据库,再更新redis
            场景: A进程update set value = 10 where value = 9B进程 update set value = 11 where value = 9;
            1. A 进程先更新数据库,还未写入缓存:mysql value = 10 ;redis value = 9
            2. B 进程更新数据库并且提交事务,写入缓存:mysql value = 11;redis value = 113. A 进程处理完请求提交事务,写入缓存:redis value = 104. 最终 mysql value = 11; redis value = 10
        3. 先删除缓存再更新数据库
            场景:A进程update set value = 10 where value = 9B进程查询value;
            1. A 进程先删除缓存 还没来得及修改数据或者事务未提交
            2. B 进程开始查询,没有命中缓存,所以查库并写入缓存 redis value = 9 
            3. A 进程更新数据库完成 mysql value = 10
            4. 最终 mysql value = 10;redis value = 9 
    解决方案:
        1. 延时双删除
            场景:A进程update set value = 10 where value = 9B进程查询value;
            1. A 进程先删除缓存 还没来得及修改数据或者事务未提交
            2. B 进程开始查询,没有命中缓存,所以查库并写入缓存 redis value = 9 
            3. A 进程更新数据库完成 mysql value = 10
            4. A 进程估算延时时间,sleep之后再次删除缓存
            5. 最终mysql value = 10;redis value 为空(下次查询直接查库)
            6. 延时的原因时防止B进程在A进程更新完之后B进程还没来得及写入缓存 
        2. 请求串行化
            1. 创建两个队列 :更新队列和查询队列
            2. 当缓存不存在需要查库的时候将key存入更新队列
            3. 如果查询未完成之前有新的请求进来,并且发现更新队列中还存在key则将key放入查询队列,则等待;不存在则重复第二步
            4. 如果查询的数据发现查询队列已经存在则不需要再次写入队列
            4. 数据更新完成之后rpop更新队列,同时rpop查询队列,释放查询请求
            5. 查询请求可以使用while + sleep  查询缓存并且设置最大延迟时间,还没有完成则返回空

参考:
面试官:如何保证缓存一致性?一篇文章带你分析到位!
为什么说延时双删很扯淡

  1. redis中的connect 和pconnect

    1. connect             :脚本结束之后释放连接
        1. close :释放连接
    2. pconnect(长连接) :脚本结束连接不释放,连接保持在php-fpm进程中,生命周期随着php-fpm进程的生命周期
        1. close不释放连接
            1. 只是当前php-cgi进程中不能再次请求redis
            2. 当前php-cgi中的后续连接仍然可以复用,直到php-fpm结束生命周期
        2. 减少建立redis连接的消耗
        3. 减少一个php-fpm多次建立连接
        4. 消耗更多的内存,并且连接数持续增加
        5. 同一个php-fpm的woker子进程(php-cgi)的上一个请求可能会影响到下一个请求
    3. pconnect 的连接复用问题
        1. 变量A select db 1 ;变量B select db 2;会影响到变量A的db
        2. 解决:每一个db创建一个连接实例

参考:
深入php redis pconnect
Redis中connect与pconnect区别?
PHP使用Redis长连接的方法详解

  1. redis zset有序集合使用skiplist的原理

    1. 基本概念
         1. skiplist是一个随机的数据,以有序的方式在层次化的链表中保存元素(只能用于元素有序的情况)
         2. skiplist实在有序链表和多层链表的基础上演变的
         3. 允许重复值,所以对比检查除了要对比key 还要对比value
         4. 每个节点都带有一个高度为1的后退指针,用于表头方向到表尾方向的迭代
         5. 时间复杂度O(logn)、空间复杂度O(n)
     2. 跳跃表和平衡树的对比
         1. 范围查询效率
             1. 跳跃表范围查询效率更高,因为找到最小值之后只需要对第一层的链表进行遍历直到小于最大值即可
             2. 平衡树范围查询找到最小值之后还要进行中序遍历找到其他不超过最大值的节点
         2. 内存占用
             1. skiplist 每个节点的指针数量为1/(1-p)
             2. 平衡树的每个节点指针数都为2
         3. 插入和删除操作
             1. skiplist只需要修改相邻节点的指针
             2. 平衡树变更会引起子树的调整

参考:
Redis 为什么用跳表而不用平衡树?
深入理解Redis跳跃表的基本实现和特性
跳跃表

  1. redis的过期删除和淘汰机制

    1. 常规过期删除策略
        1. 定时删除
            1. 通过定时器在过期的时候立即删除
            2. 内存释放及时但是消耗更多的CPU,大并发的时候需要消耗CPU资源影响处理请求的速度    
            3. 内存友好,CPU不友好
        2. 惰性删除
            1. 放任键过期不管,到下次需要去取出的时候检查是否过期并删除
            2. 可能存在大量过期键,且不会使用,导致内存溢出
            2. 内存不友好,CPU友好
        3. 定期删除
            1. 每隔一段时间检查,删除过期的键
            2. 删除多少和检查多少有算法决定
    2. redis采用的 惰性删除 + 定期删除
        1. 周期性随机测试一些设置了过期时间的键进行检查,到期则删除
        2. 每次清理的时间不超过CPU25%,达到时间则退出检查
        3. 定期没有删除到的键,且以后不会使用的键还是会存在内存中,所以需要配合淘汰策略
    3. 淘汰策略(内存不足以写入新数据的时候执行)
        1. volatile-lru     :设置了过期时间且最近使用越少越优先淘汰
        2. volatile-ttl     :设置了过期时间且过期时间越早越优先淘汰
        3. volatile-random    :设置了过期时间中随机删除
        4. allkeys-lru        :所有键中过期时间越早越优先淘汰
        5. allkeys-random    :所有键中过期随机淘汰
        6. no-enviction        :不允许淘汰,内存不足报错

参考:
Redis专题:万字长文详解持久化原理
Redis持久化
RDB 和 AOF 持久化的原理是什么?我应该用哪一个?它们的优缺点?

  1. redis常见问题及解决方案

    1. 缓存雪崩:同一时间大量缓存失效,导致请求直接查询数据库,数据库内存和CPU压力增加甚至宕机
        解决:
            1. 热点数据永不过期或者分布到不同实例,降低单机故障问题
            2. 缓存时间添加随机数,防止大量缓存同时失效                
            4. 做二级缓存或者双缓存,A为原始缓存 短时效,B为备份缓存 ,长期有效。更新时候双写缓存
    2. 缓存穿透:缓存和数据库都没有数据,大量请求下,所有请求直接击穿到数据库,导致宕机。
        解决:
            1. 布隆过滤器:长度为m的位向量或者位列表组成(仅包含01位值的列表)
                1. 使用多个不用的hash函数,产生多个索引值,填充对应多个位置的值为1
                2. 布隆过滤器可以检查值是 “可能在集合中” 还是 “绝对不在集合中”
                2. 可能误判但是基础过滤效率高
                3. 极端情况,当布隆过滤器没有空闲位置的时候每次查询返回true
            2. 空缓存(短时效)
            3. 业务层参数过滤
    3. 缓存击穿:数据库中有数据,但是缓存突然失效之后发生大量请求导致数据库压力增加甚至打垮宕机
        解决:
            1. 热点数据永不过期
            2. 互斥锁:获取锁之后不管成功还是失败都要释放锁

参考:
【实战问题】– 缓存穿透,缓存击穿和缓存雪崩的区别以及解决方案
5 分钟搞懂布隆过滤器,亿级数据过滤算法你值得拥有!

  1. php-fpm详解及生命周期

    1. 基础知识
        1. CGI协议
            1. 动态语言的代码文件需要通过对应的解析器才能被服务器识别
            2. CGI协议就是用来使服务器和解释器相互通信的
            3. 服务器解析PHP文件需要PHP解释器加上对应的CGI协议        
        2. CGI程序 = php-cgi
            1. php-cgi就是一个遵守CGI协议的CGI程序
            2. 同时也就是PHP解释器
            3. 标准的CGI每个请求都会解析php.ini,初始化执行环境等,降低性能
            4. 每次修改配置之后需要重新php-cgi才能让php.ini生效
            5. 不能动态worker调度,只能一开始指定数量的worker
        2. FastCGI协议 
            1.CGI一样也是一个协议/规范,不过是再CGI的基础上优化,效率更高
            2. 用来提高CGI程序性能的
            3. 实现了CGI进程的管理
    
        3. FastCGI程序  = php-fpm
            1. php-fpm就是一个遵守FastCGI协议的FastCGI程序        
            2. FastCGI程序对CGI程序的管理模式
                1. 启动一个master进程,解析配置文件,初始化环境
                2. 启动多个worker子进程
                3. 接受到请求之后,传递给woker进程去执行
            3. 解决修改php.ini之后平滑重启问题
                1. process_control_timeout:子进程接受主进程复用信号的超时时间(在规定时间内处理完请求,完成不了就不管了)
                2. 设定php-fpm留给fastcgi进程响应重启信号的时间
                3. process_control_timeout = 0,也就是不生效,无法保证平滑重启
                4. process_control_timeout设置过大可能导致系统请求堵塞 
                5. process_control_timeout =10的情况下,如果代码逻辑需要11s,重启旧可能导致代码执行部分退出
                6. 建议值:request_terminate_timeout
            4. 重启类型
                1. 优雅重启
                2. 强制重启
        2. php-fpm生命周期:待更新
            PHP-FPM 生命周期:https://www.abelzhou.com/php/php-fpm-lifespan/#

参考:
PHP-FPM 与 Nginx 的通信机制总结
搞不清FastCgi与PHP-fpm之间是个什么样的关系
FastCgi 与 PHP-fpm 之间的关系
详解nginx及FPM平滑重启
平滑重启之php-fpm
PHP-FPM】重启过程源码详解
【PHP】配置文件中的超时时间解析

  1. Nginx 和php之间的通信

    1. 通信方式:fastcgi_pass
        1. tcp socket
            1. 跨服务器,nginx和php不在一个机器时,只能用这个方式
            2. 面向连接的协议,更好的保证通信的正确性和完整性
        2. unix socket
            1. 不需要网络协议栈、打包拆包等
            2. 减少tcp 开销,效率比tcp socket 更高
            3. 高并发时候不稳定,连接数暴增产生大量的长时缓存,大数据包可能直接返回异常

    参考:
    PHP-FPM 与 Nginx 的通信机制总结
    Nginx与php-fpm之间的通信机制(一)

  2. web漏洞及问题

    1. sql注入
    2. XSS攻击
        推荐阅读(很详细的案例来分析XSS攻击的不同类型及解决方案)[前端安全系列(一):如何防止XSS攻击?](https://tech.meituan.com/2018/09/27/fe-security.html)
    3. CSRF攻击:
        推荐阅读 :[前端安全系列(二):如何防止CSRF攻击?](https://tech.meituan.com/2018/10/11/fe-security-csrf.html)
    4. 文件上传漏洞
        推荐阅读 :[浅析文件上传漏洞](https://xz.aliyun.com/t/7365)
    5. 跨域问题:
        推荐阅读 :
            1. [前端常见跨域解决方案(全)](https://segmentfault.com/a/1190000011145364)
            2. [JSONP原理详解——弄懂JSONP及其实现方法](https://juejin.cn/post/6844904024924225544)
        1. jsonp
        2. cors
        3. nginx代理

    更新时间:2022-03-14

  3. 魔术方法原理及使用场景

    1. __construct() 构造方法
        详解:
            1. 对象被实例化的时候首先调用
            2. 初始化数据、初始化属性
    2. __destruct() 析构方法
        详解:
            1. 对象销毁前调用该方法
            2. 释放资源防止内存溢出和单个进程占用过多内存
    3. __get()、__set()
        详解:
            1. 调用或者设置对象未定义的属性是触发
            2. 属性的一些格式化或者处理
    4. __isset()
        详解:
            1. 对不可访问属性调用isset()或者empty()时调用
    5. __unset()
        详解:
            1. 对不可访问属性调用unset()时调用
    6. __call() 、__callStatic()
        详解:
            1. 调用不存在的方法或者调用不存在的静态方法时执行
            2. 调用不存在的方法php会抛出致命错误,该方法可以进行错误日志或者重定向、抛出异常等处理
    7.  __sleep () 
        详解:
            1. 执行serialize()时调用
    8.  __wakeup ()
        详解:
            1. 执行unserialize()时调用
    9. __clone()
        详解:
            1. 对象复制完成时调用
            2. 克隆示例或者单例模式 防止克隆
    10. __autoload()
        详解:
            1. 尝试加载未定义的类时候调用
            2. 惰性加载/懒加载/lazy loading
    11. __toString()
    12. __invoke()
    13. __set_state

    参考:
    说说 PHP 的魔术方法及其应用
    PHP解析之十六个魔术方法详解

  4. 乐观锁和悲观锁

    1. 乐观锁:执行更新的时候判断其他进程是否修改了数据,如果修改了就放弃修改
        1. 实现机制:
            1. 版本号机制:数据表加一个version版本号字段,每次修改+12. CAS算法
        2. 场景:读取频繁场景
    2. 悲观锁:先取锁再操作-操作数据的时候把数据锁住,操作完成之后才会释放,释放锁之前其他进程不能修改数据
        1. 实现机制
            1. select * from table where id = 1 for update
        2. 场景:写入操作频繁的场景

参考:
【BAT面试题系列】面试官:你了解乐观锁和悲观锁吗?
MySQL中的乐观锁和悲观锁具体怎么实现的?

  1. mysql一次查询索引使用

    1. 一次查询只能使用1个,所以要合理的使用组合索引,而不是单列索引。

参考:
数据库中查询记录时是否每次只能使用一个索引?

本帖已被设为精华帖!
本帖由系统于 2个月前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 54

mark

2个月前 评论

关于 TCP 的三次握手、四次挥手,一定要区分 标志位 ACK 和 确认序号 ack 。

2个月前 评论

另外,推荐了两个同样的地址,可以删掉一个

 https://learnku.com/articles/63520
2个月前 评论
snakelis (楼主) 2个月前

很不错,比较贴合真实面试,点个赞

2个月前 评论

mysql那段 写的真不错 给原地址一键三连了!

2个月前 评论

第14点,隔离级别之前理解不透彻有误,已经重新更新了,更新时间:2022-03-02 15:58:21

2个月前 评论
$arr = [2, 4, 1, 5, 3, 6];
         for ($i = 0; $i < (count($arr)); $i++) {
             for ($j = $i + 1; $j < (count($arr)); $j++) {
                 if ($arr[$i] <= $arr[$j]) {
                     $temp = $arr[$i];
                     $arr[$i] = $arr[$j];
                     $arr[$j] = $temp;
                 }
             }
         }
     result : [6,5,4,3,2,1]

确定是冒泡的写法?

正确的冒泡不应该是下面这样

$list = [2, 4, 1, 7, 9, 3];
$len = count($list);

for ($i = $len - 1; $i > 0; $i--) {
    for ($j = 0; $j < $i; $j++) {
        if ($list[$j] > $list[$j + 1]) {
            $tmp = $list[$j];
            $list[$j] = $list[$j + 1];
            $list[$j + 1] = $tmp;
        }
    }
}
var_dump($list);
2个月前 评论
snakelis (楼主) 2个月前
arvin-hermit (作者) 2个月前
arvin-hermit (作者) 2个月前
snakelis (楼主) 2个月前
arvin-hermit (作者) 2个月前

想知道,这些都会了,一线城市能拿多少

2个月前 评论
wanghan 2个月前
snakelis (楼主) 2个月前
wuchenge

有计划换工作,正好看到,发现这些问题,多年的经验竟然答不上来 :joy:

2个月前 评论
snakelis (楼主) 2个月前

作者写的通俗易懂,点个赞 :+1: :+1: :+1: :+1: :+1: :+1: :+1:

2个月前 评论
snakelis (楼主) 2个月前

关于redis 缓存和 mysql 数据一致性
延时双删,个人感觉不如更新数据后再延时删

更新数据后删缓存,只有一个极端的情况会出现缓存不一致的问题。数据更新时无缓存,数据库更新完成,删除缓存前,有进程获取数据,且在删除缓存前没把查到的老数据更新到缓存。这个可以通过设置延时删除缓存来解决。只要延时删除的时间大于缓存时间即可。

更新数据后再延时删可以再优化下。在更新数据前做setnx操作,设置一个特殊值,数据更新完删除缓存。在缓存使用时,对缓存内容进行识别,如果是特殊值,直接访问数据库,并且不更新缓存。

2个月前 评论
snakelis (楼主) 2个月前
snakelis (楼主) 2个月前
IDLIFE (作者) 2个月前
IDLIFE (作者) 2个月前

打印出来背一背,

2个月前 评论
snakelis (楼主) 2个月前

卷王实锤

2个月前 评论

谢谢楼主分享,温故知新

2个月前 评论

redis单线程为什么快?

通过非阻塞 IO 和 IO 多路复用机制,Redis 仅以区区单线程之躯就可以同时应付这么多并发请求的处理。

1个月前 评论
berrx 1个月前

算法不一定会考你这么简单的 :joy:

1个月前 评论

有没有用,先收藏再说

1个月前 评论
wuchenge

今天面试字节,被考了两道中级算法,没有做出来

1个月前 评论
snakelis (楼主) 1个月前

:+1: :+1: :+1: :+1: :+1:

3周前 评论

第30个:魔术方法原理及使用场景 1. __cocnstruct() 构造方法,拼写错误,应为 __construct()

2周前 评论
snakelis (楼主) 2周前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!