├── .DS_Store ├── LICENSE ├── README.md ├── es.md ├── fpm.md ├── git.md ├── go.md ├── images ├── .DS_Store ├── git │ ├── git.png │ └── git1.png ├── nginx │ ├── fastcgi.png │ └── 工作模式.png ├── php │ └── php.png ├── wechat │ └── wechat.jpeg ├── 收款码.jpeg ├── 网络协议 │ ├── tcp.jpeg │ ├── udp.jpeg │ ├── 三次握手.png │ ├── 下载.jpeg │ └── 四次挥手.png ├── 自己画的架构.png └── 项目 │ └── 权益售卖流程.png ├── kafka.md ├── linux.md ├── mysql.md ├── nginx.md ├── php.md ├── rabbitmq.md ├── rabbitmq ├── rabbitmq.png ├── rabbitmq1.png ├── rabbitmq2.png └── rabbitmq3.png ├── redis.md ├── 学习中.md ├── 操作系统.md ├── 数据结构.md ├── 杂项 └── 小米三面.md ├── 架构.md ├── 算法题.md ├── 计算机网络.md ├── 设计模式.md ├── 问题.md ├── 面试官问题.md ├── 项目介绍.md ├── 项目设计.md └── 高并发.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/.DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Zhaoyang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP-interview-myway 2 | 记录PHP面试包括计算机网路,操作系统,PHP,redis,数据库MySQL(从2019年12月开始)面试了好未来,跟谁学,头条,极客保险,百度文库,百度知道等等公司。持续更新中 3 | 4 | **微信号:86938904. 可以多交流联系.可以一起交流面经,学习成长**。也可以讨论一些公司情况和内推。 5 | 6 | 7 | 8 | # 计算机基础 9 | 10 | ##### [计算机网络](计算机网络.md) 11 | ##### [设计模式](设计模式.md) 12 | ##### [高并发](高并发.md) 13 | ##### [算法题](算法题.md) 14 | ##### [操作系统]() 15 | 16 | 17 | # 开发工具 18 | 19 | ##### [redis](redis.md) 20 | ##### [php](php.md) 21 | ##### [MySQL](mysql.md) 22 | ##### [ES](es.md) 23 | ##### [git](git.md) 24 | ##### [nginx](nginx.md) 25 | ##### [kafka](kafka.md) 26 | ##### **[rabbitmq](rabbitmq.md)** 27 | 28 | **[fpm](fpm.md)** 29 | 30 | **[go](go.md)** 31 | 32 | 33 | 34 | # 架构 35 | 36 | [架构](架构.md) 37 | 38 | # 项目 39 | 40 | ##### [问题](问题.md) 41 | ##### [项目介绍](项目介绍.md) 42 | ##### [项目设计](项目设计.md) 43 | 44 | -------------------------------------------------------------------------------- /es.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | ## 为什么要ES,本身你了解ES么?你还用一些其他的中间件么? 6 | 7 | 就是一种,搜索查询的工具吧。然后提供了强大的检索功能。 8 | 倒排索引,然后能获取到数据。 9 | 10 | 天然是分布式的,然后会获取到数据,然后组合。然后输出 11 | 12 | ## 项目的日志收集 13 | https://www.jianshu.com/p/0a5acf831409 14 | 15 | 通过filebeat读取日志,传送到kafka队列里,kafka再传给logstash过滤切割日志,logstash传到es里,最后kibana展示. 16 | 17 | # es是如何做存储排序的? 18 | 房源索引里只存储了房源基础数据,排序是根据查询筛选时根据某些字段排序或使用painless脚本计算。 19 | ES房源索引里存储每个房源每天的房态、价格、促销活动数据。房东改房态、改价、参加促销通过MQ同步到ES更新索引数据,另定时有全量房源更新。 20 | 21 | # 筛选多天时(每天价格可能一样可能不一样)价格如何展示? 22 | 价格展示:筛选日期内的每日价格和促销活动根据产品策略进行判断展示,这里不方便透漏。 23 | 24 | # 筛选多天&价格区间过滤实现 25 | 先筛选出多天房态满足的房源,然后根据产品策略动态计算每个房源多天的一个最优价格,然后用最优价格过滤是否在价格区间。最优价格计算主要使用到了ES painless脚本,会计算出房源是否可以展示,true或者false。最小价格是通过脚本计算出。最终展示出筛选出的房源。 -------------------------------------------------------------------------------- /fpm.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | # Fpm进程管理 6 | 7 | 介绍下三种不同的进程管理方式: 8 | 9 | 1. static: 这种方式比较简单,在启动时master按照pm.max_children配置fork出相应数量的worker进程,即worker进程数是固定不变的 10 | 2. dynamic: 动态进程管理,首先在fpm启动时按照pm.start_servers初始化一定数量的worker,运行期间如果master发现空闲worker数低于pm.min_spare_servers配置数(表示请求比较多,worker处理不过来了)则会fork worker进程,但总的worker数不能超过pm.max_children,如果master发现空闲worker数超过了pm.max_spare_servers(表示闲着的worker太多了)则会杀掉一些worker,避免占用过多资源,master通过这4个值来控制worker数 11 | 3. ondemand: 这种方式一般很少用,在启动时不分配worker进程,等到有请求了后再通知master进程fork worker进程,总的worker数不超过pm.max_children,处理完成后worker进程不会立即退出,当空闲时间超过pm.process_idle_timeout后再退出 12 | 13 | worker会将自己的状态更新到fpm_scoreboard_proc_s->request_stage,master就是通过这个字段来判断worker是否是空闲。 14 | 15 | **各自的优缺点** 16 | 17 | 1. 静态模式,不用动态扩容,提升性能。缺点:只需要考虑max_children数量,数量取决于cpu的个数和应用的响应时间,一次启动固定大小进程浪费系统资源 18 | 2. 动态模式,动态扩容, 不浪费资源;缺点:所有worker都在工作,新的请求到来需要等待创建worker进程,最长等待1s 19 | 3. 按需分配,会更节省资源。但是大流量下会使master变得很忙碌。 20 | 21 | # pm与NGINX的通信 22 | 23 | fpm的master通过【共享内存】与worker进行通信,同时监听worker 的状态,已处理请求数。要杀死一个worker通过发送信号的方式来实现。 24 | fpm的master进程与worker进程之间不会直接进行通信,master通过共享内存获取worker进程的信息,比如worker进程当前状态、已处理请求数等,当master进程要杀掉一个worker进程时则通过发送信号的方式通知worker进程。 25 | ![交互流程](/Users/zvan/code/mianshi/PHP-interview-myway/images/nginx/fastcgi.png) 26 | ![执行过程](/Users/zvan/code/mianshi/PHP-interview-myway/images/nginx/工作模式.png) 27 | 28 | -------------------------------------------------------------------------------- /git.md: -------------------------------------------------------------------------------- 1 | ![git](images/git/git.png) 2 | 3 | #git reset和git revert的区别是: 4 | reset是用来回滚的,将HEAD的指针指向了想要回滚的版本,作为最新的版本,而后面的版本也都没有了;而revert只是用来撤销某一次更改,对之后的更改并没有影响 5 | 6 | # 版本提交 7 | git commit --amend 在一个commitid里面去添加补充。 8 | 9 | 或者每天commit。 然后reset ,然后再commit。 10 | 11 | # git stash 12 | 13 | # git 同一个目录下,不允许多个分支 14 | 15 | # git worktree 16 | 17 | # 假如master刚合并了一个bug分支。 18 | git cherry-pick 4c805e2 可以将master 的内容给合并过来 19 | 20 | ![](images/git/git1.png) -------------------------------------------------------------------------------- /go.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | # GO 6 | 7 | 1. panic什么时候处理? 8 | 1. 可以在defer里面的recover里面去捕获。 9 | 10 | 2. go的内存管理。 11 | 3. go的调度. 12 | 13 | 1. go一般采用三级的调度。首先从runnext里面取goroutine,如果runnext没有,就去local queue去取。如果local queue没有,就去global queue里面去取。 14 | 2. 15 | 4. 往一个已经关闭的channel里面发送数据会怎么样? 16 | 1. 会panic。 17 | 18 | 5. go的垃圾回收 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/.DS_Store -------------------------------------------------------------------------------- /images/git/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/git/git.png -------------------------------------------------------------------------------- /images/git/git1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/git/git1.png -------------------------------------------------------------------------------- /images/nginx/fastcgi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/nginx/fastcgi.png -------------------------------------------------------------------------------- /images/nginx/工作模式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/nginx/工作模式.png -------------------------------------------------------------------------------- /images/php/php.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/php/php.png -------------------------------------------------------------------------------- /images/wechat/wechat.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/wechat/wechat.jpeg -------------------------------------------------------------------------------- /images/收款码.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/收款码.jpeg -------------------------------------------------------------------------------- /images/网络协议/tcp.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/网络协议/tcp.jpeg -------------------------------------------------------------------------------- /images/网络协议/udp.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/网络协议/udp.jpeg -------------------------------------------------------------------------------- /images/网络协议/三次握手.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/网络协议/三次握手.png -------------------------------------------------------------------------------- /images/网络协议/下载.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/网络协议/下载.jpeg -------------------------------------------------------------------------------- /images/网络协议/四次挥手.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/网络协议/四次挥手.png -------------------------------------------------------------------------------- /images/自己画的架构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/自己画的架构.png -------------------------------------------------------------------------------- /images/项目/权益售卖流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/images/项目/权益售卖流程.png -------------------------------------------------------------------------------- /kafka.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | # kafka 为什么快? 6 | 7 | 1. 零拷贝技术 8 | 2. 写缓存。 9 | 10 | # kafka消息等幂 11 | 1. 可以针对消息id,去判断全局唯一的id 12 | 2. 可以根据消息在数据库生成一个全局唯一id。比如,转账人id和收款人id为唯一索引,然后插入成功后,然后再异步去执行消息。 13 | 3. 设置前置条件,比如满足一定条件之后才进行处理,否则的话,不处理消息。 14 | 15 | -------------------------------------------------------------------------------- /linux.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | ## 查询一个日志文件中访问次数最多前10个IP? 6 | 7 | 第一步:按照IP进行将记录排序。 8 | 第二步:按照IP去重,并且显示重复次数 9 | 第三步:按照次数升序排列 10 | 第四步:显示前10行 11 | cat log.txt|awk -F" " '{print $1}' |sort|uniq -c|sort -nrt " "|awk -F" " 'print &2' |head -10 12 | 13 | cat url.log | sort | uniq -c |sort -n -r -k 1 -t ' ' | awk -F '//' '{print $2}' | head -10 14 | 15 | ## 常用的 linux 命令有哪些 16 | ```` 17 | touch 18 | mkdir 19 | rm 20 | ls 21 | cd 22 | chmod 23 | chgrp 24 | chown 25 | systemctl 26 | ps 27 | top 28 | df 29 | kill 30 | ping 31 | iostat 32 | scp 33 | lftp 34 | screen。 35 | uniq 36 | wc 37 | head 38 | tail 39 | find 40 | whereis 41 | 42 | ```` 43 | 44 | # 借鉴一下别人的例子:比较 file1的1-4字符 和 file2的2-5 字符,如果相同,将file2 的第二列 与 file1 合并 file3(http://bbs.chinaunix.net/thread-577044-1-1.html) awk 处理多个文件的情况 45 | ```` 46 | $ cat file1 47 | 0011AAA 200.00 20050321 48 | 0012BBB 300.00 20050621 49 | 0013DDD 400.00 20050622 50 | 0014FFF 500.00 20050401 51 | 52 | $ cat file2 53 | I0011 11111 54 | I0012 22222 55 | I0014 55555 56 | I0013 66666 57 | 58 | $ awk 'NR==FNR{a[substr($1,1,4)]=$0}NR!=FNR&&a[b=substr($1,2,5)]{print a[b] $2}' file1 file2 59 | 0011AAA 200.00 20050321 11111 60 | 0012BBB 300.00 20050621 22222 61 | 0014FFF 500.00 20050401 55555 62 | 0013DDD 400.00 20050622 66666 63 | ```` 64 | 当第一个文件的1-4个字段 与 第二个文件里面的2-5个里面的字段相同时,将文件进行合并。 65 | 66 | # 查看日志一个ip 在一个小时内,请求了多少次? 67 | ```` 68 | 查看某一时间段的IP访问量(4-5点) 69 | grep "07/Apr/2017:0[4-5]" nginx.log | awk '{print $1}' | sort | uniq -c| sort -nr | wc -l 70 | ```` 71 | 72 | ```` 73 | 查看访问100次以上的IP 74 | awk '{print $1}' nginx.log | sort -n |uniq -c |awk '{if($1 >100) print $0}'|sort -rn 75 | ```` 76 | 77 | 很棒的一个介绍 78 | https://codante.org/blog/post/service-log-common-commands/#%E6%9F%A5%E7%9C%8B%E6%9F%90%E4%B8%80%E6%97%B6%E9%97%B4%E6%AE%B5%E7%9A%84IP%E8%AE%BF%E9%97%AE%E9%87%8F-4-5%E7%82%B9 79 | 80 | # 查询一个时间段内的ip请求量,取前100个值。 81 | ```` 82 | grep "03/Mar/2020:1[7-8]" access.log | awk '{print $1}' | sort -n | uniq -c | sort -nr | head -n 100 83 | 84 | 3 127.0.0.1 85 | ```` 86 | # grep 87 | 搜索多个文件并查找匹配文本在哪些文件中: 88 | ```` 89 | grep -l "text" file1 file2 file3... 90 | ```` 91 | # 如何排查问题? 92 | 1. cpu是否沾满,一般会是mysql的索引有问题 93 | 2. NGINX的报错啊,502 504 等 94 | 3. php-fpm 的慢日志查询 95 | 4. MySQL的下游是否有问题。 96 | 97 | # sort 命令是怎么排序的? 98 | 归并? 还是快拍? 99 | 100 | # 看端口是否被调用 101 | lsof :9000 用来查看端口是否在被使用 102 | 103 | # 看cpu或者内存 104 | top 查看cpu占用最高的进程。 105 | 106 | -------------------------------------------------------------------------------- /mysql.md: -------------------------------------------------------------------------------- 1 | 对内容进行分类和归档 2 | 3 | [TOC] 4 | 5 | #MySQL原理 6 | 7 | 8 | ## MySQL引擎 9 | #### 1. 一个SQL语句的执行过程 10 | 逻辑优化,是优化SQL语句,使SQL语句执行起来更高效。 11 | 12 | 优化器,会判断扫描行数,会选择一个行数,最小的方式,去进行查询。 13 | 会通过抽样的方式来进行。 14 | 15 | innodb会默认选n个数据页去进行采样,然后乘以索引的数据页的数量, 就会得到索引的数量。 16 | 17 | 当数据页变更的数量超过1/10之一的数据,会重新计数。 18 | #### 2. MyISAM和InnoDB都使用B+树来实现索引: 19 | 1. MyISAM的索引与数据分开存储 20 | 1. MyISAM的索引叶子存储指针,主键索引与普通索引无太大区别 21 | 1. InnoDB的聚集索引和数据行统一存储 22 | 1. InnoDB的聚集索引存储数据行本身,普通索引存储主键 23 | 1. InnoDB一定有且只有一个聚集索引 24 | 1. InnoDB建议使用趋势递增整数作为PK,而不宜使用较长的列作为PK 25 | 26 | #### 3. mysql where in (几个) where in (几万个) 有什么区别 27 | select * from single_table where key1 in ('aa), 'aa1', 'aa2', ..., 'zz100'); 28 | 29 | mysql在5.7.3之前的版本是的eq_range_index_dive_limit的默认值是10,在5.7.3之后是200. 30 | 31 | 当in语句的单点区间数量大于等于eq_range_index_dive_limit的值时,就不会使用index dive来计算各个单点区间对应的索引记录条数,而是使用索引统计数据。 32 | 33 | 例如rows是9693,key1列的不重复值为968,所以key1列的平均重复次数为:9693/968 = 10条。 34 | 35 | 当in的数量为20000个时,意味着有20000个单点区间的时候,就直接使用统计数据来估算对应的记录条数。每个区间对应10条,对应的回表记录数就是20000 * 10 = 200000条。 36 | 37 | 当in的数量为几个的时候,由于key1列只是一个普通索引的话,每个单点的值对应多少条记录并不确定。计算方式就是直接获取索引对应的B+树的区间最左记录和区间最右记录,然后再计算这两条记录之间有多少条记录。 38 | 39 | 这种直接访问索引对应B+树来计算某个扫描区间内对应的索引记录条数的方式就是index dive 40 | 41 | 内容来自《MySQL是怎样运行的》 42 | 43 | --- 44 | 45 | mysql --help | grep max-allowed-packet 46 | mysql: [Warning] World-writable config file '/usr/local/etc/my.cnf' is ignored. 47 | --max-allowed-packet=# 48 | max-allowed-packet 16777216 49 | 50 | in 没有大小限制。但是受max-allowed-packet的限制,最多也就2000个吧 51 | 52 | 53 | 54 | #### 4. 引用《mysql45讲》里面的一个留言 55 | 1. 数据库——解决数据存储的问题 56 | 2. WAL——解决数据一致性问题 57 | 3. 多线程——解决性能差异的问题 58 | 4. 锁——解决多线程并发导致数据不一致的问题 59 | 5. 索引——解决数据查询或者操作慢的问题 60 | 6. 日志——解决数据备份、同步、恢复等问题 61 | 7. 数据库主备——解决数据高可用的问题 62 | 8. 数据库读写分离——解决数据库压力的问题 63 | 9. 数据库分库分表——解决数据量大的问题 64 | 65 | #### 5. JOIN和UNION区别 66 | join 是两张表做交连后里面条件相同的部分记录产生一个记录集, 67 | 68 | union是产生的两个记录集(字段要一样的)并在一起,成为一个新的记录集 。 69 | 70 | #### 6. int(4)和int(11)的区别 71 | 超过4的话,其实是没有影响的, 然后使用zerofill的时候其实是能看到的吧。不满足长度的时候,会在前面补充字段。 72 | 73 | #### 7. select sum(colomn3),column2 from tableA where column1>0 group by column2 having sum(colomn3)>0 order by column2;语句的执行顺序 74 | 1. from 75 | 2. on 76 | 3. join 77 | 4. where 78 | 5. group by 79 | 6. having 80 | 7. select 81 | 8. distinct 82 | 9. order by 83 | 10. limit 84 | 85 | https://blog.csdn.net/jiadajing267/article/details/81269067 86 | 87 | 88 | #### 8. drop delete truncate 89 | drop 删除表和数据 90 | 91 | delete 删除数据 带where 92 | 93 | truncate 不带where 的删除,不太安全。 94 | 95 | 96 | 97 | ## MySQL索引 98 | 99 | #### 1. 索引设计成树形,和SQL的需求相关。为什么不是哈希。 100 | 对于这样一个单行查询的SQL需求: 101 | 102 | ``` 103 | select * from t where name=”zhaoyang”; 104 | ``` 105 | 106 | 确实是哈希索引更快,因为每次都只查询一条记录。 107 | 108 | 画外音:所以,如果业务需求都是单行访问,例如passport,确实可以使用哈希索引。 109 | 110 | 但是对于排序查询的SQL需求: 111 | 112 | 分组:group by 113 | 114 | 排序:order by 115 | 116 | 比较:<、> 117 | 118 | 哈希型的索引,时间复杂度会退化为O(n),而树型的“有序”特性,依然能够保持O(log(n)) 的高效率。 119 | 120 | 121 | #### 2. 两阶段提交吧? 122 | 123 | - 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器; 124 | - 否则,需要先从磁盘读入内存,然后再返回。 125 | - 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。 126 | - 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。 127 | - 然后告知执行器执行完成了,随时可以提交事务。执行器生成这个操作的 binlog,并把 binlog 写入磁盘。 128 | - 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。 129 | 130 | 如果不使用日志的话,可能会导致恢复出来日志里面的数据,与原来数据库里面的数据不一致、 131 | 132 | redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。 133 | 134 | sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。 135 | 136 | 137 | 138 | #### 3. 为什么使用B+树,而不是用B*树 139 | 因为B*树非叶子节点使用了指向兄弟节点的指针。如果一个节点满了之后,自己的兄弟节点还没有满,需要将一部分数据转移到自己的兄弟节点去。如果兄弟节点也满了,就在自己和兄弟节点之间添加新的节点。因为兄弟之间分配新节点的概率还是比较低的,所以空间利用率还是比较高的。 140 | 是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针; 141 | 142 | #### 4. 除了主键索引,还用过什么(2月16,头条) 143 | 1. 唯一索引 144 | 2. 普通索引 145 | 3. 覆盖索引 146 | 4. 前缀索引 147 | 148 | #### 5. 主键索引和唯一索引的区别(2月 16 头条)为什么会回表。 149 | 1. 主键一定会创建一个唯一索引,但是有唯一索引的列不一定是主键; 150 | 1. 主键不允许为空值,唯一索引列允许空值; 151 | 1. 一个表只能有一个主键,但是可以有多个唯一索引; 152 | 1. 主键可以被其他表引用为外键,唯一索引列不可以; 153 | 1. 主键是一种约束,而唯一索引是一种索引,是表的冗余数据结构,两者有本质的差别 154 | 155 | #### 6. 最左匹配原则? 156 | ``` 157 | where a = 1 and b=1 and c = 1. 能命中abc 158 | where a = 1 and b > 1 and c = 1 不能命中c 因为b是范围索引。范围索引的话,意味着b可能是无序的。 159 | where a > 1 and b = 1 and = 1 bc不能命中索引,因为范围查询是不能命中索引的。 160 | ``` 161 | 162 | 163 | #### 7、mysql索引的底层B+树,说说为什么使用B+树,跟红黑树有什么区别,B树和B+树的区别? 164 | 主要考虑的是IO影响吧。因为B+ 树只有叶子节点存储数据,B树内部也存储数据。在查询相同数据量的情况下,B树高度更高,IO次数更多,然后只能一点点加载数据页。 165 | B树的话,所有的节点都是数据地址。需要在内部节点和叶子之间去查询数据。b树的分支节点也有数据。 b树范围查询只能中序遍历。 166 | 167 | B+树只有叶子节点数据,而且叶子节点之间由链表构成的,在叶子节点直接顺序查询会比较快。b+树的数据都集中在叶子节点。分支节点只负责索引。b+树的层高 会小于 B树 平均的Io次数会远大于 B+树(因为B+树是顺序查找)b+树更擅长范围查询。叶子节点 数据是按顺序放置的双向链表。 168 | b+树可以把索引完全加载至内存中。支持多路,多路的好处:可以每次只加载一个节点的数据进去,因为内存的容量是有限的。【这个就是多路的好处了 169 | #### 8. 普通索引和覆盖索引 170 | 普通所以的话,需要先查询出主键id,还得需要回表一次。覆盖索引的话,不需要回表。 171 | 172 | 173 | #### 9. order by 能用上索引么? 174 | ```` 175 | CREATE TABLE `t` ( 176 | `id` int(11) NOT NULL, 177 | `city` varchar(16) NOT NULL, 178 | `name` varchar(16) NOT NULL, 179 | `age` int(11) NOT NULL, 180 | `addr` varchar(128) DEFAULT NULL, 181 | PRIMARY KEY (`id`), 182 | KEY `city` (`city`)) ENGINE=InnoDB; 183 | ```` 184 | 185 | 这种情况下对name进行排序的话,是不会用上索引的。因为是对全文进行排序。 186 | ```` 187 | select city,name,age from t where city='杭州' order by name limit 1000 ; 188 | ```` 189 | 如果认为字段值过大的话,会进行rowid排序,也就是每行,根据city 取到行数据之后。只取 id 和 name,然后去按name进行排序。这种情况下,其实是内存不够的情况。这种情况下,name是无序的,需要多一次排序的操作。 190 | 191 | 如果在city 和name上面建立联合索引的话,根据city取的值,name就是有序的,减少排序的操作。 192 | ```` 193 | alter table t add index city_user(city, name); 194 | ```` 195 | 这种情况下, 不需要临时表,也不需要排序。 196 | 197 | using index 说明使用了覆盖索引,覆盖索引的效率还是比较高的。 198 | 199 | ## MySQL锁 200 | #### 1. 什么是mysql的锁机制,以及什么是死锁,以及发生死锁的场景 201 | 202 | MySQL的锁机制,就是数据库为了保证数据的一致性而设计的面对并发场景的一种规则。最显著的特点是不同的存储引擎支持不同的锁机制,InnoDB支持行锁和表锁,MyISAM支持表锁。 203 | 1. 表锁就是把整张表锁起来,特点是加锁快,开销小,不会出现死锁,锁粒度大,发生锁冲突的概率高,并发相对较低。 204 | 2. 行锁就是以行为单位把数据锁起来,特点是加锁慢,开销大,会出现死锁,锁粒度小,发生锁冲突的概率低,并发度也相对表锁较高。 205 | 206 | 锁等待是指一个事务过程中产生的锁,其他事务需要等待上一个事务释放它的锁,才能占用该资源,如果该事务一直不释放,就需要继续等待下去,直到超过了锁等待时间,会报一个超时错误。 207 | 208 | #### 2. 出现死锁的问题并不可怕,解决死锁通常有如下办法: 209 | 1. 不要把无关的操作放到事务里,小事务发生冲突的概率较低。 210 | 2. 如果不同的程序会并发存取多个表,应尽量约定以相同的顺序来访问表,这样事务就会形成定义良好的查询并且没有死锁。 211 | 3. 尽量按照索引去查数据,范围查找增加了锁冲突的可能性。 212 | 4. 对于非常容易产生死锁的业务部分,可以尝试升级锁粒度,通过表锁定来减少死锁产生的概率。 213 | 214 | 引用: https://www.cnblogs.com/xxcn/p/9941365.html 215 | 216 | 217 | #### 3. InnoDB的锁 218 | 1. InnoDB的索引与行记录存储在一起,这一点和MyISAM不一样; 219 | 1. InnoDB的聚集索引存储行记录,普通索引存储PK,所以普通索引要查询两次; 220 | 1. 记录锁锁定索引记录; 221 | 1. 间隙锁锁定间隔,防止间隔中被其他事务插入; 222 | 1. 临键锁锁定索引记录+间隔,防止幻读; 223 | 224 | #### 4. 锁的类型 225 | 全局锁,用来做全库逻辑备份。。Flush tables with read lock 这个是一个全局锁。 226 | 227 | 表锁,表级锁,lock tables t1 read, t2 write; unlock tables 228 | 229 | 表锁的语法是 lock tables … read/write 230 | 231 | 另一类表级的锁是 MDL(metadata lock)(默认会启动) 232 | 233 | 如果想要拿到表的结构,可以选择等待多长时间,如果等待能拿到的话,最好。拿不到的话,也不会阻塞。 234 | ```` 235 | ALTER TABLE tbl_name NOWAIT add column ... 236 | ALTER TABLE tbl_name WAIT N add column ... 237 | ```` 238 | 行锁 239 | 240 | 241 | 242 | #### 5、mysql的悲观锁和乐观锁区别和应用,ABA问题的解决 243 | 244 | 245 | 246 | 247 | #### 6. 死锁 248 | 多个事务在同一资源上相互占用,并请求锁定对方占用资源,从而导致恶性循环的现象 249 | InnoDB 目前处理方法:将持有最少行级排他锁的事务进行回滚 250 | 251 | ## MySQL日志 252 | #### 1. redo log和undo log 253 | redo log是将随机写,变为了顺序写。会保证acid特性。 254 | undo日志会恢复数据。不会对acid造成影响。insert 回滚会删除,delete或恢复。 255 | 256 | 257 | ## MySQL事务 258 | 259 | #### 1. 隔离级别:(2月16头条问过) 260 | 1. 读未提交:就是他能读取到别人未提交的内容。可以读取到未提交的内容。【会出现脏读的情况】 261 | 1. 读提交;也就是只能读取到别人提交后的内容。别人提交后才能读。【会出现不可重复读的情况】 262 | 1. 可重复度:就是前后读取时一致的。事务执行过程中的数据,跟执行后读取到的数据是一致的。【会出现幻读的情况】 263 | 1. 串行化:就是事务吧,这种的话,就是效率会比较慢。写会加写锁,读会加读锁。读写冲突的时候,必须等到前一个事务结束之后,才能之后后一个事务。(加锁, 来避免访问。)【会出现超时和锁竞争的情况】 264 | 265 | #### 2. 可重复读解决了哪些问题,还有哪些问题没有解决,为什么默认的级别是可重复读? 266 | 可重复读解决了不可重复读的问题。(读未提交的情况下,一个事务读取了一个还未提交或者还未回滚的事务) 267 | 268 | 可重复读没有解决幻读的问题,是通过日志和事务id来解决的不可重复读的问题。 269 | 270 | 因为性能比较高,可以通过next key lock 解决幻读的问题。 271 | 272 | #### 3. 串行化解决了什么问题?(2月16,头条) 273 | 事务的安全问题。但是性能比较慢 274 | 275 | #### 4. crash-safe 276 | 有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。 要理解 crash-safe 这个概念,可以想想我们前面赊账记录的例子。只要赊账记录记在了粉板上或写在了账本上,之后即使掌柜忘记了,比如突然停业几天,恢复生意后依然可以通过账本和粉板上的数据明确赊账账目。 277 | 278 | 279 | 280 | #### 5. 原子性是通过什么实现的。 281 | undo log。 282 | #### 6. 隔离性怎么理解? 283 | mvcc。 284 | 多版本控制。事务id只能看到自己本事务内的id,以及已经提交的事务id。其他事务的内容则看不到。 285 | #### 7. MySQL如何保证 事务 286 | 287 | https://www.cnblogs.com/jianzh5/p/11643151.html 288 | 289 | 290 | #### 8. 脏读: 291 | 当数据库中一个事务A正在修改一个数据但是还未提交或者回滚, 292 | 另一个事务B 来读取了修改后的内容并且使用了, 293 | 之后事务A提交了,此时就引起了脏读。 294 | 295 | 此情况仅会发生在: 读未提交的的隔离级别. 296 | 297 | 主从 298 | 1. 主库和从库之间的网络问题 299 | 2. 从库和主库机器的性能可能有差异。可能从库执行比较慢。 300 | 301 | #### 9. 幻读 302 | 一个事务读取2次,得到的记录条数不一致: 303 | 304 | 上图很明显的表示了这个情况,由于在会话 1 之间插入了一个新的值,所以得到的两次数据就不一样了。 305 | 就问了我的项目经历,有啥能说得出的亮点。问我的规划和对教育行业的看法。项目开发流程是啥。问我并发是多少。就聊了20分钟 306 | 307 | ## MVCC 308 | #### 1。 MVCC 是支持并发的原因 309 | 数据多版本是一种能够进一步提高并发的方法,它的核心原理是: 310 | - 写任务发生时,将数据克隆一份,以版本号区分; 311 | - 写任务操作新克隆的数据,直至提交; 312 | - 并发读任务可以继续读取旧版本的数据,不至于阻塞; 313 | 314 | 如上图: 315 | 1. 最开始数据的版本是V0; 316 | 2. T1时刻发起了一个写任务,这是把数据clone了一份,进行修改,版本变为V1,但任务还未完成; 317 | 3. T2时刻并发了一个读任务,依然可以读V0版本的数据; 318 | 4. T3时刻又并发了一个读任务,依然不会阻塞; 319 | 320 | 可以看到,数据多版本,通过“读取旧版本数据”能够极大提高任务的并发度。 321 | 322 | 提高并发的演进思路,就在如此: 323 | 324 | 普通锁,本质是串行执行 325 | 326 | 读写锁,可以实现读读并发 327 | 328 | 数据多版本,可以实现读写并发 329 | 330 | 画外音:这个思路,比整篇文章的其他技术细节更重要,希望大家牢记。 331 | 332 | InnoDB是高并发互联网场景最为推荐的存储引擎,根本原因,就是其多版本并发控制(Multi Version Concurrency Control, MVCC)。行锁,并发,事务回滚等多种特性都和MVCC相关。 333 | 334 | #### 2. MVCC解决了什么问题?(2月 16 头条) 335 | 1. 行锁,并发,事务回滚。 336 | 只会读取事务开始之前提交的数据。针对每一个事务都有一个事务id 的概念,是严格递增的,回滚的时候,也会按顺序回滚的。 337 | 338 | #### 3. MVCC 当前读和快照读。 339 | mvcc 是通过日志和事务id来解决问题的。 340 | 341 | 342 | #MySQL主从 343 | 344 | #### 1. 如何实现 MySQL 的读写分离? 345 | 其实很简单,就是基于主从复制架构,简单来说,就搞一个主库,挂多个从库,然后我们就单单只是写主库,然后主库会自动把数据给同步到从库上去。 346 | #### 2. MySQL 主从复制原理的是啥? 347 | 主库将变更写入 binlog 日志,然后从库连接到主库之后,从库有一个 IO 线程,将主库的 binlog 日志拷贝到自己本地,写入一个 relay 中继日志中。接着从库中有一个 SQL 线程会从中继日志读取 binlog,然后执行 binlog 日志中的内容,也就是在自己本地再次执行一遍 SQL,这样就可以保证自己跟主库的数据是一样的。 348 | 349 | 这里有一个非常重要的一点,就是从库同步主库数据的过程是串行化的,也就是说主库上并行的操作,在从库上会串行执行。所以这就是一个非常重要的点了,由于从库从主库拷贝日志以及串行执行 SQL 的特点,在高并发场景下,从库的数据一定会比主库慢一些,是有延时的。所以经常出现,刚写入主库的数据可能是读不到的,要过几十毫秒,甚至几百毫秒才能读取到。 350 | 351 | 而且这里还有另外一个问题,就是如果主库突然宕机,然后恰好数据还没同步到从库,那么有些数据可能在从库上是没有的,有些数据可能就丢失了。 352 | 所以 MySQL 实际上在这一块有两个机制,一个是半同步复制,用来解决主库数据丢失问题;一个是并行复制,用来解决主从同步延时问题。 353 | 354 | 这个所谓半同步复制,也叫 semi-sync 复制,指的就是主库写入 binlog 日志之后,就会将强制此时立即将数据同步到从库,从库将日志写入自己本地的 relay log 之后,接着会返回一个 ack 给主库,主库接收到至少一个从库的 ack 之后才会认为写操作完成了。 355 | 所谓并行复制,指的是从库开启多个线程,并行读取 relay log 中不同库的日志,然后并行重放不同库的日志,这是库级别的并行。 356 | 357 | #### 3. MySQL 主从同步延时问题 358 | 以前线上确实处理过因为主从同步延时问题而导致的线上的 bug,属于小型的生产事故。 359 | 是这个么场景。有个同学是这样写代码逻辑的。先插入一条数据,再把它查出来,然后更新这条数据。在生产环境高峰期,写并发达到了 2000/s,这个时候,主从复制延时大概是在小几十毫秒。线上会发现,每天总有那么一些数据,我们期望更新一些重要的数据状态,但在高峰期时候却没更新。用户跟客服反馈,而客服就会反馈给我们。 360 | 361 | 我们通过 MySQL 命令: 362 | ```` 363 | show status 364 | ```` 365 | 查看 Seconds_Behind_Master,可以看到从库复制主库的数据落后了几 ms。 366 | 一般来说,如果主从延迟较为严重,有以下解决方案: 367 | 1. 分库,将一个主库拆分为多个主库,每个主库的写并发就减少了几倍,此时主从延迟可以忽略不计。【此时是主库的执行性能可能不好】 368 | 1. 打开 MySQL 支持的并行复制,多个库并行复制。如果说某个库的写入并发就是特别高,单库写并发达到了 2000/s,并行复制还是没意义。 369 | 1. 重写代码,写代码的同学,要慎重,插入数据时立马查询可能查不到。 370 | 1. 如果确实是存在必须先插入,立马要求就查询到,然后立马就要反过来执行一些操作,对这个查询设置直连主库。不推荐这种方法,你要是这么搞,读写分离的意义就丧失了。 371 | 372 | 373 | # MySQL实践和优化 374 | 375 | #### 1. count为什么这么这样操作。 376 | myisam保存了一个行数。所以查询起来会比较快。但是不支持事务。 377 | innodb是需要将所有数据一行一行读入进来,然后统计计数。准确,但是会导致性能问题。 378 | 379 | 由于MVCC,innodb对于返回多少行,是不确定的。 380 | 如果都加where的count数据时一致的。 381 | 382 | #### 2. 一个大表(数据有1000w)该怎么加索引? 383 | 存在锁住表的可能哦。 384 | 385 | 建一个一样的 tpm表,给 tmp表加索引,然后两个表rename,给主表加索引,再RENAME回来,把TMP表的 新增数据在主表中没有的给INSERT回主表。 386 | 387 | 388 | 389 | #### 3. sql 优化题 390 | ```` 391 | select * from order where status in (2,3,4,5) and price>0 and channel is null limit 10; 392 | ```` 393 | SQL很慢,且没有索引,也不让修改表结构,该怎么办? 394 | 395 | 那就是每次通过id去查询,然后记录id值,这样的话,是能命中id这个索引的, 查询效率还是比较高的。 396 | 397 | #### 4. mysql 保存表情 398 | MYSQL_CHARSET = 'utf8mb4' 将字符集改为utf8mb4就可以了。 399 | 400 | #### 5. MySQL优化 401 | 1. 加索引 402 | 2. 解决SQL语句没有命中索引的问题? 403 | 3. 没有使用索引,或者使用了索引,但是优化器没有选择。 404 | #### 6. 分库分表有什么实践 405 |  1. 目前公司的表,已经很大了,但是目前没有拆表的需求。因为数据量不会再剧增了。 406 | 407 | #### 7. 有个很大的日志表会选用哪个索引? 408 | innodb,还有就是可以根据时间做分表。还有就是加索引。 409 | 410 | #### 8. MYSQL 总结 411 | 1. 常见并发控制保证数据一致性的方法有锁,数据多版本; 412 | 1. 普通锁串行,读写锁读读并行,数据多版本读写并行; 413 | 1. redo日志保证已提交事务的ACID特性,设计思路是,通过顺序写替代随机写,提高并发; 414 | 1. undo日志用来回滚未提交的事务,它存储在回滚段里; 415 | 1. InnoDB是基于MVCC的存储引擎,它利用了存储在回滚段里的undo日志,即数据的旧版本,提高并发; 416 | 1. InnoDB之所以并发高,快照读不加锁; 417 | 1. InnoDB所有普通select都是快照读; 418 | 419 | #### 9. 什么样的select是快照读?InnoDB并发如此之高 420 | 除非显示加锁,普通的select语句都是快照读,例如: 421 | select * from t where id>2; 422 | 423 | 这里的显示加锁,非快照读是指: 424 | 425 | ``` 426 | select * from t where id>2 lock in share mode; 427 | select * from t where id>2 for update; 428 | ``` 429 | 430 | 快照读(Snapshot Read),这种一致性不加锁的读(Consistent Nonlocking Read),就是InnoDB并发如此之高的核心原因之一。 431 | 这里的一致性是指,事务读取到的数据,要么是事务开始前就已经存在的数据(当然,是其他已提交事务产生的),要么是事务自身插入或者修改的数据。 432 | 433 | #### 10。 删除数据之后 434 | 标记为可复用,alter table t engine = innodb; 可以重建表。 435 | 436 | 437 | #### 11. 数据库设计规范 438 | 1. 命名规范 439 | 2. 索引设计,命名要规范 440 | 3. 数据类型选择 441 | 4. 数据表命名规范,表与表之间相同的字段,命名要一致。 442 | 5. 为每个表创建,主键索引。 443 | 6. 根据需要,尽量使用覆盖索引,能加快查询效率。 444 | 7. 索引字段不要过长,因为索引页占用空间。 445 | 8. 尽量不使用唯一索引吧,在业务端去进行处理,保证数据唯一。内存操作,要比读取磁盘io要快。 446 | 447 | #### 12. MySQL的优化 448 | 可以通过explain来分析语句的执行效率 449 | 450 | 可以加索引 451 | 452 | 可以分库分表 453 | 454 | #### 13、mysql如果发生了抖动,怎么排查问题 455 | 1. 在flush 456 | 2. redo log 满了需要刷一下数据 457 | 3. 内存不够了,需要刷一下数据 458 | 4. MySQL在空闲的时候,需要刷一下数据 459 | 5. MySQL在关闭的时候需要刷一下数据 460 | 461 | #### 14. explain 462 | https://www.cnblogs.com/tufujie/p/9413852.html 463 | 其实主要考察的就是 464 | 465 | #### 15. select_type 466 | all 全表查询。index是使用的索引。range也相当于是范围查询,比index好一些。ref是使用了普通索引。ref_eq说明是使用了主键索引或者是唯一索引。const应该是对一个主键进行了where查询 467 | 468 | #### 16. keys 用到的索引 469 | 470 | #### 17. possible_key 可能用到的索引 471 | 472 | #### 18. extra 详情吧?? 473 | 1. Using where:不用读取表中所有信息,仅通过索引就可以获取所需数据,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤 474 | 2. Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见 group by ; order by 475 | 3. Using filesort:当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序” 476 | 4. Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。 477 | 5. Impossible where:这个值强调了where语句会导致没有符合条件的行(通过收集统计信息不可能存在结果)。 478 | 6. Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行 479 | 7. No tables used:Query语句中使用from dual 或不含任何from子句 480 | 481 | 482 | #### 19. MySQL数据库中的字段类型varchar和char的主要区别是什么? 483 | Varchar是变长,节省存储空间,char是固定长度。查找效率要char型快,因为varchar是非定长,必须先查找长度,然后进行数据的提取,比char定长类型多了一个步骤,所以效率低一些。 484 | 485 | 486 | InnoDB表比MyISAM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表(alter table tablename type=innodb) 487 | 488 | #### 20. 写出mysql中,插入数据,读出数据,更新数据的语句 489 | INSERT INTO 表名 VALUES (””,””); 490 | SELECT * FROM 表名;。 491 | UPDATE 表名 SET 字段名1=’a’,字段名2=’b’ WHERE 字段名3=’c’;。 492 | 493 | #### 21. 写入数据时,聚簇索引所在的列的内容是随机的,会引起什么性能问题? 494 | 聚簇索引情况下,写入数据时,插入速度严重依赖插入顺序,按照主键的顺序插入是加载数据到InnoDB表中速度最快的方式。但如果不是按照主键顺序加载数据,那么在加载完成后最好使用OPTIMIZE TABLE命令重新组织一下表。 495 | 496 | 基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临“页分裂”的问题。当行的主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是一次分裂操作。页分裂会导致表占用更多的磁盘空间。 497 | 聚簇索引可能导致全表扫描变慢,尤其是行比较稀疏,或者由于页分裂导致数据存储不连续的时候。最好是重建索引,会比较好。 498 | 499 | ``` 500 | alter table t engine = innodb; 501 | ``` 502 | #### 22. 请描述一下mysql主从服务器之间是如何同步数据的,什么样的sql会造成主从无法正确同步? 503 | 从库生成两个线程,一个I/O线程,一个SQL线程; 504 | 505 | i/o线程去请求主库 的binlog,并将得到的binlog日志写到relay log(中继日志) 文件中; 506 | 507 | 主库会生成一个 log dump 线程,用来给从库 i/o线程传binlog; 508 | SQL 线程,会读取relay log文件中的日志,并解析成具体操作,来实现主从的操作一致,而最终数据一致; 509 | 510 | 511 | #### 23. php防止sql注入式攻击,用什么函数转换字符串? 512 | htmlspecialchars addslashes(); 其实pdo也已经处理好的。 513 | #### 24. 常用的mysql工具? 514 | • phpmyadmin; Navicat for mysql 515 | 516 | 517 | #### 25. MySQL优化方法 518 | 1. 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。 519 | 520 | 2. 选择合适的表字段数据类型和存储引擎,适当的添加索引。 521 | 522 | 3. mysql库主从读写分离。 523 | 524 | 4. 找规律分表,减少单表中的数据量提高查询速度。 525 | 526 | 5. 添加缓存机制,比如memcached,apc等。 527 | 528 | 6. 不经常改动的页面,生成静态页面。 529 | 530 | 7. 书写高效率的SQL。 531 | 532 | ``` 533 | 比如 SELECT * FROM TABEL 改为 SELECT field_1, field_2, field_3 FROM TABLE. 534 | ``` 535 | 536 | 537 | #### 26. 慢SQL优化 538 | 1. 数据库CPU负载高。一般是查询语句中有很多计算逻辑,导致数据库cpu负载。 539 | 2. IO负载高导致服务器卡住。这个一般和全表查询没索引有关系。 540 | 3. 查询语句正常,索引正常但是还是慢。如果表面上索引正常,但是查询慢,需要看看是否索引没有生效。 541 | 542 | 另一套答案: 543 | 544 | 解答思路:针对SQL语句的优化,我们不要一上来就回答添加索引,这样显得太不专业。我们可以从如下几个角度去分析: 545 | 546 | 1. 回归到表的设计层面,数据类型选择是否合理。 547 | 2. 大表碎片的整理是否完善。 548 | 3. 表的统计信息是不是准确的。 549 | 4. 审查表的执行计划,判断字段上面有没有合适的索引。 550 | 5. 针对索引的选择性,建立合适的索引(就又涉及大表DDL的操作问题。所以说,我们要有能力把各个知识点联系起来) -------------------------------------------------------------------------------- /nginx.md: -------------------------------------------------------------------------------- 1 | 对内容进行分类和归档 2 | 3 | [TOC] 4 | 5 | # 1. NGINX惊群现象 6 | 在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下线程进程也没多大区别)等待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群。可以想见,效率很低下,许多进程被内核重新调度唤醒,同时去响应这一个事件,当然只有一个进程能处理事件成功,其他的进程在处理该事件失败后重新休眠(也有其他选择)。这种性能浪费现象就是惊群。 7 | 8 | 简单了说,就是同一时刻只允许一个nginx worker在自己的epoll中处理监听句柄。它的负载均衡也很简单,当达到最大connection的7/8时,本worker不会去试图拿accept锁,也不会去处理新连接,这样其他nginx worker进程就更有机会去处理监听句柄,建立新连接了。而且,由于timeout的设定,使得没有拿到锁的worker进程,去拿锁的频繁更高。 9 | 10 | https://blog.csdn.net/russell_tao/article/details/7204260 11 | 12 | # 2. NGINX与fpm的方式,主要是两种工作方式 13 | tcp socket:tcp socket通信方式,需要在nginx配置文件中填写php-fpm运行的ip地址和端口号。 14 | ```` 15 | location ~ \.php$ { 16 | include fastcgi_params; 17 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;; 18 | fastcgi_pass 127.0.0.1:9000; 19 | fastcgi_index index.php; 20 | } 21 | ```` 22 | unix socket:unix socket通信方式,需要在nginx配置文件中填写php-fpm运行的pid文件地址。 23 | ```` 24 | //service php-fpm start生成.sock文件 25 | location ~ \.php$ { 26 | include fastcgi_params; 27 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;; 28 | fastcgi_pass unix:/var/run/php5-fpm.sock; 29 | fastcgi_index index.php; 30 | } 31 | ```` 32 | 33 | # 3. NGINX上传文件大小的限制,修改命令 34 | client_max_body_size 10M 设置上传文件大小限制 35 | 36 | 37 | # 4. PHP +Nginx 默认配置,突然收到大量请求,服务器开始报错,哪个错误错误比较多 ? 502、503、504 38 | 502 原因:php_fpm 配置文件错误,或者是接口请求有问题 39 | 503:机器维护或者暂时不可用 40 | 504:一般是NGINX配置有问题 41 | 42 | # 5. fpm的平滑重启过程 43 | 通过观察,可以分析出大致的平滑重启过程为: 44 | 1. master使用新配置 fork出n-1个worker及新master 45 | 2. 新worker处理新情求,旧worker执行完退出 46 | 3. master重新加载配置,期间使用新master接管服务 47 | 4. master加载配置完毕,新master切换为worker工作模式 平滑重启完,master进程号并不会发生变化。 48 | 49 | # 6. NGINX的错误码 50 | 502 一般是fpm配置有问题。比如请求超时。max_children和request_terminate_timeout。max_children最大子进程数 超过了php-fpm 的最大响应数,就会出现502.netstat可以查看连接数。一个php-cgi消耗的内存在20M左右,php-cgi所占用的内存为20M*max_request数量。为单个请求的超时时间(request_terminate_timeout)。当数据库连接超时,或者大量请求超时的时候,会出现502. 51 | 52 | 504一般是NGINX配置有问题。Gateway Time-out。fastcgi_connect_timeout、fastcgi_send_timeout、fastcgi_read_timeout、fastcgi_buffer_size、fastcgi_buffers、fastcgi_busy_buffers_size、fastcgi_temp_file_write_size、fastcgi_intercept_errors。fgi缓冲区太小,会导致504. 53 | 或者是程序无故退出, 54 | 或者是程序执行太慢。 55 | proxy_read_timeout 60 56 | proxy_send_timeout 60 57 | 58 | 从网络角度,502已经与后端建立了连接,但超时;504与后端连接未建立,超时。 59 | 60 | # 7. 如果包含正在处理的进程,会报什么错误? 61 | ``` 62 | [error] 29841#0: *1646 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 127.0.0.1, server: localhost, request: "GET /test.php HTTP/1.1", upstream: "fastcgi://127 .0.0.1:9001 ", host: "localhost" 68 | process_control_timeout 设置子进程接受主进程复用信号的超时时间 可以解决这个问题。如果不能保证平滑重启,就可以设定一下这个值。 69 | # 8. NGINX的max_requests 70 | max_requests意味着,子进程处理多少请求之后,就会关闭。因为子进程差不多都能同时打满,同时关闭,所以会出现502的问题。默认max_request为500个。可以修改这个参数。或者增加机器的内存,修改参数。 71 | 72 | # 9. NGINX的负载均衡 73 | ip hash 根据ip进行hash可以解决session问题 74 | ```` 75 | upstream backserver { 76 | ip_hash; 77 | server 192.168.0.14:88; 78 | server 192.168.0.15:80; 79 | } 80 | ```` 81 | 82 | 轮询(是默认的方式) 83 | ```` 84 | upstream backserver { 85 | server 192.168.0.14; 86 | server 192.168.0.15; 87 | } 88 | ```` 89 | 90 | 权重 91 | ```` 92 | upstream backserver { 93 | server 192.168.0.14 weight=3; 94 | server 192.168.0.15 weight=7; 95 | } 96 | ```` 97 | 98 | fair 根据响应时间来进行分配,响应时间短的优先分配。 99 | ```` 100 | upstream backserver { 101 | server server1; 102 | server server2; 103 | fair; 104 | } 105 | ```` 106 | 107 | 108 | # 10. nginx与fpm通信(php fpm进程master进程和worker进程分别的责任是什么) 109 | 动态程序,将请求发送给php-fpm, fpm的master启动worker去执行phpcgi,然后将编译结果返回 110 | fcgi执行原理 111 | 1. Web Server启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module)。 112 | 2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待来自Web Server的连接。 113 | 3. 当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。 114 | 4. FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 而在CGI模式中,php-cgi在此便退出了。 115 | 在上述情况中,你可以想象CGI通常有多慢。每一个Web 请求PHP都必须重新解析php.ini、重新载入全部扩展并重初始化全部数据结构。使用FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。 116 | 117 | FastCGI 与传统 CGI 模式的区别之一则是 Web 服务器不是直接执行 CGI 程序了,而是通过 Socket 与 FastCGI 响应器(FastCGI 进程管理器)进行交互,也正是由于 FastCGI 进程管理器是基于 Socket 通信的,所以也是分布式的,Web 服务器可以和 CGI 响应器服务器分开部署。Web 服务器需要将数据 CGI/1.1 的规范封装在遵循 FastCGI 协议包中发送给 FastCGI 响应器程序。 118 | 119 | # 11. cgi协议流程 120 | 客户端访问 http://127.0.0.1:9003/cgi-bin/user?id=1 121 | 127.0.0.1 上监听 9003 端口的守护进程接受到该请求 122 | 通过解析 HTTP 头信息,得知是 GET 请求,并且请求的是 /cgi-bin/ 目录下的 user 文件。 123 | 将 uri 里的 id=1 通过存入 QUERY_STRING 环境变量。 124 | Web 守护进程 fork 一个子进程,然后在子进程中执行 user 程序,通过环境变量获取到id。 125 | 执行完毕之后,将结果通过标准输出返回到子进程。 126 | 子进程将结果返回给客户端。 127 | 128 | # 12. 遇到过一个问题,开多少进程合适?曾经被问了两遍。 129 | 各位可以补充下,这个问题,应该是去看自己的硬件资源来决定。 130 | # lvs 和NGINX的区别,都是四层的一个负载均衡。 131 | lvs:重量级的四层负载软件,应该就是直接进行转发。 132 | 133 | nginx:轻量级的四层负载软件,带缓存功能,正则表达式较灵活。可以进行限流,重定向吧。 134 | 135 | # 13. restart和reload 的区别 136 | reload --重新加载,reload会重新加载配置文件,Nginx服务不会中断。而且reload时会测试conf语法等,如果出错会rollback用上一次正确配置文件保持正常运行。 137 | 138 | restart --重启(先stop后start),会重启Nginx服务。这个重启会造成服务一瞬间的中断,如果配置文件出错会导致服务启动失败,那就是更长时间的服务中断了。 139 | 所以,如果是线上的服务,修改的配置文件一定要备份。为了保证线上服务高可用,最好使用reload。 140 | 141 | # 14. NGINX进程模式(NGINX的模型,worker与master是如何通信的,每个worker需要监听一个端口吗) 142 | 143 | ![http://tengine.taobao.org/book/_images/chapter-2-1.PNG](http://tengine.taobao.org/book/_images/chapter-2-1.PNG) 144 | 145 | 在nginx启动后,如果我们要操作nginx,要怎么做呢?从上文中我们可以看到,master来管理worker进程,所以**我们只需要与master进程通信就行了**。(所以每个worker不需要简单端口) 146 | 147 | master进程会接收来自外界发来的信号,再根据信号做不同的事情。所以我们要控制nginx,只需要通过kill向master进程发送信号就行了。比如kill -HUP pid,则是告诉nginx,从容地重启nginx,我们一般用这个信号来重启nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。 148 | 149 | master进程在接收到HUP信号后是怎么做的呢?首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发送信号,告诉他们可以光荣退休了。新的worker在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出。 150 | 151 | 当然,直接给master进程发送信号,这是比较老的操作方式,nginx在0.8版本之后,引入了一系列命令行参数,来方便我们管理。比如,./nginx -s reload,就是来重启nginx,./nginx -s stop,就是来停止nginx的运行。如何做到的呢?我们还是拿reload来说,我们看到,执行命令时,我们是启动一个新的nginx进程,而新的nginx进程在解析到reload参数后,就知道我们的目的是控制nginx来重新加载配置文件了,它会向master进程发送信号,然后接下来的动作,就和我们直接向master进程发送信号一样了。 152 | 153 | **worker进程又是如何处理请求的呢?**我们前面有提到,worker进程之间是平等的,每个进程处理请求的机会也是一样的。 154 | 155 | 当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求、解析请求、处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。[节选自PHP7内核剖析] 156 | 157 | 158 | 159 | # NGINX的全局变量 160 | 161 | $host 162 | 163 | $remote_addr 164 | 165 | $http_cookie 166 | 167 | $remote_port 168 | 169 | $server_addr 170 | 171 | $server_name 172 | 173 | $server_port 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /php.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | ## SOAP 6 | 7 | SOAP 是基于 XML 的简易协议,可使应用程序在 HTTP 之上进行信息交换。 8 | 9 | 或者更简单地说:SOAP 是用于访问网络服务的协议。 10 | 11 | ## PHP的api安全防护 12 | 13 | ## lvs和NGINX的区别 14 | lvs好像是直接转发吧,不做任何处理 15 | 16 | NGINX是反向代理,可以负载均衡。 17 | 18 | ## 网关了解过么? 19 | 1. 就是负载均衡,流量控制,权限控制吧 20 | 21 | ## 自己擅长什么技术。 22 | 1. 我基础扎实 23 | 2. 代码规范意识不错。 24 | 25 | ## 以下会输出什么? 26 | ``` 27 | 37 | string(5) "baidu" 38 | [1]=> 39 | string(5) "hello" 40 | [3]=> 41 | string(5) "world" 42 | } 43 | ``` 44 | 45 | 46 | ## 以下会输出什么? 47 | ``` 48 | set('lock', $random, array('nx', 'ex' => $ttl)); 138 | } 139 | 140 | if ($redis->get('goods.num') <= 0) { 141 | echo ("秒杀已经结束"); 142 | //删除锁 143 | if ($redis->get('lock') == $random) { 144 | $redis->del('lock'); 145 | } 146 | return false; 147 | } 148 | 149 | $redis->decr('goods.num'); 150 | echo ("秒杀成功"); 151 | //删除锁 152 | if ($redis->get('lock') == $random) { 153 | $redis->del('lock'); 154 | } 155 | return true; 156 | ``` 157 | 158 | 159 | ## include与require没有本质上的区别, 160 | 唯一的不同在于错误级别,当文件无法被正常加载时include会抛出warning警告,而require则会抛出error错误, 161 | 162 | require() 语句的性能与 include() 相类似,都是包括并运行指定文件。不同之处在于:对 include() 语句来说,在执行文件时每次都要进行读取和评估;而对于 require() 来说,文件只处理一次(实际上,文件内容替换 require() 语句)。这就意味着如果可能执行多次的代码,则使用 require() 效率比较高 163 | 164 | ## 内存泄漏 165 | 因为,apache这种是伴随着操作系统 长时间来运行的,如果内存不回收的话,可能会导致操作系统不能主动的回收内存,导致严重的内存泄漏错误。 166 | 频繁的内核态和用户态的切换会导致性能问题。 167 | 168 | ## PHP的垃圾回收 169 | 首先由一个引用计数的概念,当计数为0 的时候就会收回。 170 | 如果是循环引用的话,当本身被回收之后,引用计数为1,还是大于0的,这个时候,会变成垃圾,可能会导致内存泄漏。这个时候需要垃圾回收期,弄到缓冲区,然后进行下一步的处理。 171 | 172 | 缓冲区,到了**10000**个以后,会进行处理。这种垃圾只会出现在数组和对象的情况下, 因为自己引用自己。所以垃圾处理,只会处理这两种类型的垃圾。 173 | 会循环遍历缓冲区,然后依次减一,因为基本减一后,就为0了,表明都是自己引用本身,别人不会用到它,可以被删除。 174 | 175 | https://learnku.com/articles/33451 176 | 177 | (1) 从缓冲区链表的 roots 开始遍历,把当前 value 标为灰色 (zend_refcounted_h.gc_info 置为 GC_GREY),然后对当前 value 的成员进行深度优先遍历,把成员 value 的 refcount 减 1,并且也标为灰色; 178 | 179 | (2) 重复遍历缓冲区链表,检查当前 value 引用是否为 0,为 0 则表示确实是垃圾,把它标为白色 (GC_WHITE),如果不为 0 则排除了引用全部来自自身成员的可能,表示还有外部的引用,并不是垃圾,这时候因为步骤 (1) 对成员进行了 refcount 减 1 操作,需要再还原回去,对所有成员进行深度遍历,把成员 refcount 加 1,同时标为黑色; 180 | 181 | (3) 再次遍历缓冲区链表,将非 GC_WHITE 的节点从 roots 链表中移出,最终 roots 链表中全部为真正的垃圾,最后将这些垃圾清除。【这个时候,剩下的就是array和object这种类型的垃圾了】 182 | 183 | https://github.com/pangudashu/php7-internal/blob/master/5/gc.md 184 | 185 | 如果一个value的refcount为0的话,可以释放掉,不属于垃圾。 186 | 如果value的recount大于0的话,不能释放,属于垃圾。 187 | 只有IS_TYPE_COLLECTABLE 才会被回收。 188 | 只会被插入一次,不会被重复插入,会被标记为GC_PURPLE 189 | 190 | ## // 缓存的更新模式 191 | 192 | 193 | ## 浏览器访问页面源码如下,后端执行 php reload,页面会出现什么情况 194 | 195 | ``` 196 | 217 | string(5) "baidu" 218 | [1]=> 219 | string(5) "hello" 220 | [3]=> 221 | string(5) "world" 222 | } 223 | ``` 224 | 225 | 226 | ## 以下会输出什么? 227 | 228 | ``` 229 | "a"; // 0 344 | echo "a" <=> "b"; // -1 345 | echo "b" <=> "a"; // 1 346 | 通过 define() 定义常量数组 347 | 356 | ``` 357 | 358 | 359 | 5.匿名类 360 | 现在支持通过new class 来实例化一个匿名类,这可以用来替代一些“用后即焚”的完整类定义。 361 | 362 | ``` 363 | logger; 373 | } 374 | 375 | public function setLogger(Logger $logger) { 376 | $this->logger = $logger; 377 | } 378 | } 379 | 380 | $app = new Application; 381 | $app->setLogger(new class implements Logger { 382 | public function log(string $msg) { 383 | echo $msg; 384 | } 385 | }); 386 | 387 | var_dump($app->getLogger()); 388 | ?> 389 | 以上例程会输出: 390 | 391 | object(class@anonymous)#2 (0) { 392 | } 393 | ``` 394 | 395 | 396 | new class实例化 397 | Closure::call() ¶ 398 | Closure::call() 现在有着更好的性能,简短干练的暂时绑定一个方法到对象上闭包并调用它。 399 | 400 | ``` 401 | x;}; 406 | $getX = $getXCB->bindTo(new A, 'A'); // 中间层闭包 407 | echo $getX(); 408 | 409 | // PHP 7+ 及更高版本的代码 410 | $getX = function() {return $this->x;}; 411 | echo $getX->call(new A); 412 | 以上例程会输出: 413 | 1 414 | 1 415 | ``` 416 | 417 | 418 | ## 字符串”\r”,”\n”,”\t”,”\x20”分别代表什么 419 | 答案: “\r”代表的含义是: 420 | 在Linux、unix 中表示返回到当行的最开始位置,在Mac OS 中表示换行且返回到下一行的最开始位置,相当于Windows 里的 \n 的效果。 421 | “\n”代表的含义是: 422 | 在Windows 中表示换行且回到下一行的最开始位置。相当于Mac OS 里的 \r 的效果,在Linux、unix 中只表示换行,但不会回到下一行的开始位置。 423 | “\t”所代表的含义是: 424 | 键盘上的“TAB”键,跳格(移至下一列)。 425 | “\x20”所代表的含义是:是32在ASCII表中16进制的表示。 426 | ## 以下语句输出的结果是什么 427 | $a = 3; 428 | echo "$a",'$a',"\\\$a","${a}","$a"."$a","$a"+"$a"; 429 | 得到的结果是: 430 | 3$a\$a3336 431 | 432 | var_dump(empty(array(array()))); 433 | 434 | ## 以下语句输出的结果是什么 435 | setcookie("a","value"); 436 | print $_COOKIE['a']; 437 | 438 | 得到的结果是: 439 | value(若只是这两段编码运行,则会提示PHP Notice: Undefined index: a) 440 | ## php中将当前页面重定向到另一个页面怎么写? 441 | header(); 442 | ## .什么是魔术引号(magic_quotes_gpc)? 443 | 魔术引号(Magic Quotes)是一个自动将进入 PHP 脚本的数据进行转义的过程。提示:最好在编码时不要转义而在运行时根据需要而转义。 444 | ## 在类的方法中,如何调用其父类的同名方法? 445 | parent::方法名 446 | ## 如何取得客户端的ip(要求取得一个int) 447 | $_SERVER["REMOTE_ADDR"]; 448 | ip2long进行转换 449 | ## include和require的区别 450 | require:出现错误后直接终止退出,程序不再执行,读取后会成为文件的一部分。 451 | include:包含一个不存在的文件,会提示警告程序会继续执行。每次都会读取 452 | ## extends的作用是什么 453 | 类的继承 454 | ## php中如何取得get,post参数,和上传的文件 455 | $_GET, 456 | $_POST, 457 | $_FILES 458 | ## @test()和&test()的区别 459 | @test()的作用是屏蔽test()方法中警告的作用 460 | &test()引用test()方法 461 | ## array+array与array_merge()的区别 462 | 二者之间的区别是: 463 | 1 键名为数字时,array_merge()不会覆盖掉原来的值,但+合并数组则会把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉(不是覆盖) 464 | 465 | 2 键名为字符时,+仍然把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉,但array_merge()此时会覆盖掉前面相同键名的值 466 | ## 请列举最少3个php对象的魔术方法和说明它们的用途 467 | - __construct() :实例化对象时被调用; 468 | - __destruct():当删除一个对象或者对象操作终止是被执行; 469 | - __call():调用对象不存在方法时被调用; 470 | - __get():调用对象不存在的属性时被调用; 471 | - __set():设置对象不存在的属性时被调用; 472 | - __toString():打印一个对象时被调用,比如echo $obj,print($obj); 473 | - __clone():克隆对象时被调用,比如$t = new Test();$tt = clone $t; 474 | - __sleep():serialize之前被调用,若对象比较大,想做一些删除在序列化,可以考虑使用该方法; 475 | - __wakeup():unserialize之前被调用,做些对象的初始化; 476 | - __isset():检测对象是否存在属性的时候被调用,如 isset($c->name); 477 | - __unset():unset一个对象属性时被调用,如:unset($c->name); 478 | - __set_state():调用var_export时被调用,用__set_state的返回值作为 var_export的返回值; 479 | - __autoload():实例化一个对象时,如果对应的类不存在,在该方法被调用。 480 | ## echo intval(0.58*100) 输出的结果是57,试分析这是为什么? 481 | 原因就是浮点数精度的问题。 482 | 简单的十进制分数如同 0.1 或 0.7 不能在不丢失一点点精度的情况下转换为内部二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999…。这和一个事实有关,那就是不可能精确的用有限位数表达某些十进制分数。例如,十进制的 1/3 变成了 0.3333333…。所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数。 483 | ## 内存回收机制 484 | 1. 一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息。第一个是"is_ref",是个bool值,用来标识这个变量是否是属于引用集合(reference set)。通过这个字节,php引擎才能把普通变量和引用变量区分开来 485 | zval变量容器中还有一个内部引用计数机制,来优化内存使用。第二个额外字节是"refcount",用以表示指向这个zval变量容器的变量(也称符号即symbol)个数。所有的符号存在一个符号表中,其中每个符号都有作用域(scope),那些主脚本(比如:通过浏览器请求的的脚本)和每个函数或者方法也都有作用域。 486 | $c=$b=$a; refcount就是等于3.unset之后 次数就减1. 为0时,就进行回收。 为0时,这个类型和容器就从内存中删除。 487 | ## 回收周期 488 | 首先,我们先要建立一些基本规则,如果一个引用计数增加,它将继续被使用,当然就不再在垃圾中。如果引用计数减少到零,所在变量容器将被清除(free)。就是说,仅仅在引用计数减少到非零值时,才会产生垃圾周期(garbage cycle)。 489 | 490 | 其次,在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。(通过检查是否为0,其实就是开启了回收周期) 491 | ## .php中web上传文件的原理是什么,如何限制上传文件的大小? 492 | 上传文件的表单使用 post 方式,并且要在 form 中添加 enctype='multipart/form-data'。 493 | pHp上传文件默认大小为2M,设置上传大小的配置项是upload_max_filesize, post_max_size设置一次pOST中pHp能接收的最大数据量,应该比upload_max_filesize大。 494 | 495 | **client_max_body_size** 10M 这个是配置NGINX的的配置。 496 | 497 | ## 列举出PHP中一些正则函数(至少2个) 498 | - preg_match(), 执行一个正则表达式匹配 499 | - preg_match_all(), 执行一个全局正则表达式匹配 500 | - preg_quote() 转义正则表达式字符 501 | - preg_replace(), 执行并替换 502 | - preg_split(), 通过一个正则表达式分隔字符串 503 | - preg_replace_callback() 504 | ## UTC是什么? 505 | 每个地区都有自己的本地时间,在网上以及无线电通信中,时间的转换问题就显得格外突出。整个地球分为二十四个时区,每个时区都有自己的本地时间。在国际无线电或网络通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(UTC,Universal Time Coordinated),是由世界时间标准设定的全球标准时间。 506 | ## .解释下php://input 叫什么? 507 | 答:php输入流 508 | ## .请说明在php.ini中safe_mode开启之后对于php系统函数的影响(很多系统函数就不能用了) 509 | 答:safe_mode是提供一个基本安全的共享环境。在一个多用户共享的phpweb服务器上,当这台服务器开启了safe_mode模式,有以下函数将会受到影响。首先,一下尝试访问文件系统的函数将会被限制,运行服务器的用户id,如果想要尝试操作某个文件,必须要用户该文件的读取或者写入的访问权限。 510 | 因此,在safe_mode打开的情况下,下列函数将会收到限制: 511 | 512 | ``` 513 | ckdir,move_uploaded_file, 514 | chgrp, 515 | parse_ini_file, 516 | chown 517 | ,rmdir 518 | ,copy 519 | ,rename, 520 | fopen, 521 | require, 522 | highlight_file, 523 | show_source, 524 | include, 525 | symlink, 526 | link, 527 | touch, 528 | mkdir 529 | ,unlink 530 | ``` 531 | 532 | 533 | 以上都是跟操作文件系统有关的函数,除此之外,一些php扩展的函数也会受到限制,不能在程序里面直接加载扩展,只能到php.ini里加载,而且php如果需要执行操作系统的程序时,必须在safe_mode_exec_dir中指定程序的路径,否则执行将失败。此外还有exec, 534 | shell_exec, 535 | pasathru, 536 | system 537 | ,popen等函数会收到限制 538 | 539 | ## .魔术常量 540 | - __LINE__:返回当前行号; 541 | - __FILE__:返回文件的完整路径和文件名,如果用在包含文件里面,则返回包含文件名,自 php4.0.2开始,__FILE__总是包含一个绝对路径,而在此前的版本有时候会包含一个相对路径 542 | - __FUNCTION__:返回函数名称(自 php4.3.0新加的)。自php5起本常量返回该函数被定义时的名称,区分大小写,在php4中该值总是小写; 543 | - __CLASS__:返回类的名称,自 php4.3.0新加的,自php5起本常量返回该类被定义时的名称,区分大小写,在php4中该值总是小写的; 544 | - __METHOD__:返回类的方法名。 php5新加的 545 | ## 你用什么方法检查php脚本的执行效率(通常是脚本执行时间)和数据库SQL的效率(通常是数据库Query时间),并定位和分析脚本执行和数据库查询的瓶颈所在? 546 | 1. php脚本的执行效率 547 | 1. 代码脚本里计时。 548 | 1. xdebug统计函数执行次数和具体时间进行分析。,最好使用工具winCacheGrind分析 549 | 1. 在线系统用strace跟踪相关进程的具体系统调用。 550 | 1. 数据库SQL的效率 551 | 1. sql的explain(mysql),启用slow query log记录慢查询。 552 | 1. 通常还要看数据库设计是否合理,需求是否合理等。 553 | ## .请写一段php代码,确保多个进程同时写入同一个文件成功 (这个很难,可以不考) 554 | 555 | ``` 556 | function writeData($path, $mode, $data){ 557 | $fp = fopen($path, $mode); 558 | $retries = 0; 559 | $max_retries = 100; 560 | do { 561 | if ($retries > 0) { 562 | usleep(rand(1, 10000)); 563 | } 564 | $retries += 1; 565 | }while (!flock($fp, LOCK_EX) and $retries <= $max_retries); 566 | 567 | if ($retries == $max_retries) { 568 | return false; 569 | } 570 | 571 | fwrite($fp, "$data\\n"); 572 | flock($fp, LOCK_UN); 573 | fclose($fp); 574 | return true; 575 | } 576 | ``` 577 | 578 | 579 | ## .如何在命令行下运行php脚本(写出两种方式)同时向php脚本传递参数? 580 | • window下,假设php安装目录为c:\\program files\\php5\\,那么使用命令窗口进入到该路径下,敲入php hello.php回车,则会执行当前路径下的hello.php文件, 581 | 582 | • 如果要指向其他路径下php文件,可以在php 路径/hello.php ,这种形式称为CLI模式,我们平时通过浏览器看到的那种成为CGI模式,至于传递参数,php文件在cli模式下,直接通过在文件名称后面接参数,多个参数中间用空格隔开,在php文件里面是通过两个变量来获取参数的,一个是$argv,一个是$argc,前者是传递参数的数组,默认第一个为php文件的名称;后者为$argv的数组个数。 583 | 584 | • linux下,一般程序安装都会安装在/usr/bin/php下面,可以通过man php查看一下,如果有信息说明可以使用,使用方法类似于window下。如果前面这步成立,那么你可以直接 php php文件 来运行php文件,如果man php没有信息,则说明当前php执行文件没有在环境路径里面,可以修改环境路径包含php路径,也可以类似于window进入php路径,在执行 php php文件。其他类似于window下。 585 | ## 使对象可以像数组一样进行foreach循环,要求属性必须是私有 586 | • php5里面已经有了iterator接口,只要实现该接口,即可以实现对象私有属性被foreach遍历 587 | var);} 592 | public function current(){return current($this->var);} 593 | public function key(){return key($this->var);} 594 | public function next(){return next($this->var);} 595 | public function valid(){return ($this->current()!==false);} 596 | } 597 | $s = new Sample(); 598 | foreach($s as $k=>$v){ echo $k.\=\.$v.\
\;} 599 | ?> 600 | 601 | ## 用PHP构建一个链表 602 | 603 | ``` 604 | class ListNode { 605 | public $val = 0; 606 | public $next = null; 607 | function __construct($val) { 608 | $this->val = $val; 609 | } 610 | } 611 | ``` 612 | 613 | 614 | 615 | ## php保存序列化对象到session中,并可以从session中获取序列化对象的值,用什么序列化函数? 616 | 617 | • serialize()函数序列化对象; unserialize()函数还原序列化对象。 618 | ## .以下php程序的结果 619 | 620 | ``` 621 | 8%-3=?; =>2 622 | $a="hello"; 623 | $b=&$a; 624 | unset($b); 625 | $b="world"; 626 | $a=? ; =>"hello" 627 | ``` 628 | ## .JWT 629 | 用户B 你好,用户A,我想要操作jwt内容,这是我的凭证。 630 | ## .php中的文件读写操作,读取文件test.txt中前300字节的内容? 631 | ``` 632 | $handle=fopen("test.txt","r"); 633 | $contents=fread($handle,300); 634 | fclose($handle); 635 | ``` 636 | ## .PHP获取客户端和服务器端IP客户端IP相关的变量 637 | $_SERVER['REMOTE_ADDR'] 客户端IP,有可能是用户的IP,也可能是代理的IP。(这个就是获取客户端的ip) 638 | 639 | $_SERVER['HTTP_CLIENT_IP'] 代理端的IP,可能存在可伪造。 640 | 641 | $_SERVER['HTTP_X_FORWARDER_FOR'] 用户是在哪个IP使用的代理,可能存在,可以伪造。(这个应该是客户端的真是IP) 642 | 643 | $_SERVER['SERVER_ADDR'] 获取服务器端IP(获取服务器端的IP) 644 | 645 | ## .PHP中对象的深拷贝与浅拷贝 646 | 先说一下深拷贝和浅拷贝通俗理解 647 | 648 | 深拷贝:赋值时值完全复制,完全的copy,对其中一个作出改变,不会影响另一个 649 | 650 | 浅拷贝:赋值时,引用赋值,相当于取了一个别名。对其中一个修改,会影响另一个 651 | 652 | PHP中, = 赋值时,普通对象是深拷贝,但对对象来说,是浅拷贝。也就是说,对象的赋值是引用赋值。(对象作为参数传递时,也是引用传递,无论函数定义时参数前面是否有&符号) 653 | 654 | https://yq.aliyun.com/articles/514030 655 | 656 | ## PHP7比PHP5性能提升的原因? 657 | 1、存储变量的结构体变小,尽量使结构体里成员共用内存空间,减少引用,这样内存占用降低,变量的操作速度得到提升。 658 | 659 | 2、字符串结构体的改变,字符串信息和数据本身原来是分成两个独立内存块存放,php7尽量将它们存入同一块内存,提升了cpu缓存命中率。(zend_string) 660 | 661 | 3、数组结构的改变,数组元素和hash映射表在php5中会存入多个内存块,php7尽量将它们分配在同一块内存里,降低了内存占用、提升了cpu缓存命中率。 662 | 663 | 4、 **改进了函数的调用机制** ,通过对参数传递环节的优化,减少一些指令操作,提高了执行效率。 664 | 665 | ## null 在 == 相等的值。 666 | '' "" 0 '0' array() false 667 | 668 | ## PHP的数据,key的 数字也好,string也好,是如何实现的。 669 | 存放记录的数组称做散列表,这个数组用来存储value,而value具体在数组中的存储位置由映射函数根据key计算确定,映射函数可以采用取模的方式,key可以通过一些譬如**“times 33”**的算法得到一个整形值,然后与数组总大小**取模**得到在散列表中的存储位置。这是一个普通散列表的实现,PHP散列表的实现整体也是这个思路,只是有几个特殊的地方,下面就是PHP中HashTable的数据结构: 670 | ``` 671 | //Bucket:散列表中存储的元素 672 | typedef struct _Bucket { 673 | zval val; //存储的具体value,这里嵌入了一个zval,而不是一个指针 674 | zend_ulong h; //key根据times 33计算得到的哈希值,或者是数值索引编号 675 | zend_string *key; //存储元素的key 676 | } Bucket; 677 | 678 | //HashTable结构 679 | typedef struct _zend_array HashTable; 680 | struct _zend_array { 681 | zend_refcounted_h gc; 682 | union { 683 | struct { 684 | ZEND_ENDIAN_LOHI_4( 685 | zend_uchar flags, 686 | zend_uchar nApplyCount, 687 | zend_uchar nIteratorsCount, 688 | zend_uchar reserve) 689 | } v; 690 | uint32_t flags; 691 | } u; 692 | uint32_t nTableMask; //哈希值计算掩码,等于nTableSize的负值(nTableMask = -nTableSize) 693 | Bucket *arData; //存储元素数组,指向第一个Bucket 694 | uint32_t nNumUsed; //已用Bucket数 695 | uint32_t nNumOfElements; //哈希表有效元素数 696 | uint32_t nTableSize; //哈希表总大小,为2的n次方 697 | uint32_t nInternalPointer; 698 | zend_long nNextFreeElement; //下一个可用的数值索引,如:arr[] = 1;arr["a"] = 2;arr[] = 3; 则nNextFreeElement = 2; 699 | dtor_func_t pDestructor; 700 | }; 701 | ``` 702 | HashTable中有两个非常相近的值:nNumUsed、nNumOfElements,nNumOfElements表示哈希表已有元素数,那这个值不跟nNumUsed一样吗?为什么要定义两个呢?实际上它们有不同的含义,当将一个元素从哈希表删除时并不会将对应的Bucket移除,而是将Bucket存储的zval修改为IS_UNDEF,只有扩容时发现nNumOfElements与nNumUsed相差达到一定数量(这个数量是:ht->nNumUsed - ht->nNumOfElements > (ht->nNumOfElements >> 5))时才会将已删除的元素全部移除,重新构建哈希表。所以nNumUsed>=nNumOfElements。 703 | 704 | HashTable中另外一个非常重要的值arData,这个值指向存储元素数组的第一个Bucket,插入元素时按顺序 依次插入 数组,比如第一个元素在arData[0]、第二个在arData[1]...arData[nNumUsed]。PHP数组的有序性正是通过arData保证的,这是第一个与普通散列表实现不同的地方。 705 | 706 | 既然arData并不是按key映射的散列表,那么映射函数是如何将key与arData中的value建立映射关系的呢? 707 | 708 | 实际上这个散列表也在arData中,比较特别的是散列表在ht->arData内存之前,分配内存时这个散列表与Bucket数组一起分配,arData向后移动到了Bucket数组的起始位置,并不是申请内存的起始位置,这样散列表可以由arData指针向前移动访问到,即arData[-1]、arData[-2]、arData[-3]......散列表的结构是uint32_t,它保存的是value在Bucket数组中的位置。 709 | 710 | 所以,整体来看HashTable主要依赖arData实现元素的存储、索引。插入一个元素时先将元素按先后顺序插入Bucket数组,位置是idx,再根据key的哈希值映射到散列表中的某个位置nIndex,将idx存入这个位置;查找时先在散列表中映射到nIndex,得到value在Bucket数组的位置idx,再从Bucket数组中取出元素。 711 | 712 | ## 数组通过key 查询比较快,还是通过value? 713 | key。具体原因,待补充。 714 | 715 | ## php 的数据类型。 716 | 基本数据结构 bool string int float 717 | 718 | 两种特殊结构 array object 719 | 720 | 两种特殊类型 resource null 721 | 722 | ## 项目中常用的算法? 723 | 1. 我觉得我没有用过。 724 | 2. 递归可能用一些。 725 | 726 | # 单点登录 727 | 1. 登录的时候,去验证中心获得一个唯一的ticket,然后保存在cookie里面。 728 | 2. 登录其他子网站的时候,需要获取本地的cookie然后去验证中心去验证一下。 729 | 3. 退出的时候,清除登录状态和本地唯一的ticket。 730 | 731 | // 删除session '$_SESSION = array();'; 732 | empty($_SESSION) ? true : false; 733 | 734 | # array_key_exists()和isset() 735 | 736 | array_key_exists() 会检查键值的存在. 这个函数会返回TRUE,只要键值存在,即使值为NULL. 737 | ```` 738 | $arr = array( "one"=>"1", "two"=>"2", "three"=>null ); 739 | array_key_exists("one", $arr); // true 740 | array_key_exists("two", $arr); // true 741 | array_key_exists("three", $arr); // true 742 | ```` 743 | isset()和arrry_key_exitst()不同,isset()会同时检查键和值,只有当健存在,对应的变量不为NUll的时候才会返回TURE。 744 | ```` 745 | $arr = array( "one"=>"1", "two"=>"2", "three"=>null ); 746 | isset($arr["one"]); // true 747 | isset($arr["two"]); // true 748 | isset($arr["three"]); // false 749 | ```` 750 | # 数组的底层实现 751 | 数组是PHP中非常强大、灵活的一种数据类型,它的底层实现为散列表(HashTable,也称作:哈希表)。 752 | ![](images/php/php.png) 753 | https://github.com/huqinlou0123/php-internals-extended-development-course/blob/master/1-3-2:%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2.md 754 | 755 | https://github.com/pangudashu/php7-internal/blob/master/2/zend_ht.md 756 | 757 | # 758 | 759 | -------------------------------------------------------------------------------- /rabbitmq.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | # rabbitmq的消息发送机制 4 | 5 | 1. 事务模式,会确保消息发送成功,但是效率比较慢。 6 | 2. confirm机制,会给消息发送一个唯一id,写入rabbitmq之后会返回一个ack,就ok了,是一个异步的机制。 7 | 8 | # 消费端丢失消息 9 | 关闭自动ack机制,也就是消费成功之后,才丢弃这条消息,否则继续消费。 10 | 11 | # 使用rabbitmq的场景 12 | 1. 服务间异步通信 13 | 2. 顺序消费 14 | 3. 定时任务 15 | 4. 请求削峰 16 | 17 | # rabbitmq 的问题 18 | 1. 复杂性增加 19 | 2. 数据一致性问题 20 | 3. 可用性降低 21 | 22 | # heartbeat如果设置过低的话,可能会在短暂的网络拥塞情况下, 导致误报。 23 | 如果将心跳时间设置的过低,会在短暂的网络拥塞或流量控制的清下下导致误报。在选择超时时间时,这也应该作为一个考虑因素。 24 | 25 | 从用户反馈的多年的经验值及客户端的maintainer的建议来看,低于5s都容易发生误报。对于大多数环境5s到20s之间是最佳的。 26 | 27 | # 为什么选择rabbitmq? 28 | 1. 是一个成熟 29 | 2. 稳定 30 | 3. 并发毕竟好 31 | 4. 基本不丢数据 32 | 33 | # rabbitmq如何保证有序消费 34 | 35 | # 你用过kafka没有??知道原理不? 36 | 37 | 38 | # rabbitmq和rocketmq的区别? 39 | ![](rabbitmq/rabbitmq1.png) 40 | 41 | ![](rabbitmq/rabbitmq2.png) 42 | 43 | ![](rabbitmq/rabbitmq3.png) 44 | 45 | 46 | 47 | 1. 上下游解耦,下游不需要知道上游的地址。 48 | 49 | 这种情况下,网关只需要消息分发就好了。 50 | 51 | 不关心消息结果,或者异步执行时间很长,可以使用mq 52 | 53 | 54 | 二,mq如何做到消息必达。 55 | 56 | 消息落地 57 | 超时,重传,确认。 58 | 59 | 60 | 61 | 62 | 63 | 64 | 如何保证幂等 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 等幂,需要接收方和mq配合来使用的。 78 | 79 | 80 | 81 | 82 | mq如何做到,削峰填谷的呢? 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | mq client 可以批量拉 93 | 94 | 95 | 96 | mq 如何做到消息延时。 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /rabbitmq/rabbitmq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/rabbitmq/rabbitmq.png -------------------------------------------------------------------------------- /rabbitmq/rabbitmq1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/rabbitmq/rabbitmq1.png -------------------------------------------------------------------------------- /rabbitmq/rabbitmq2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/rabbitmq/rabbitmq2.png -------------------------------------------------------------------------------- /rabbitmq/rabbitmq3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZvanYang/PHP-interview-myway/0093b460b5dfcbeb009fb7725d8874c407cb4811/rabbitmq/rabbitmq3.png -------------------------------------------------------------------------------- /redis.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | # 常见数据问题 4 | 5 | ### 1.布隆过滤器是什么? 6 | 7 | 对一个值,进行多次hash,然后将对应位置的hash值置为1,就代表着个值,同时查询的时候计算多次,然后只要有一个位置为0,说明就是不存在。 8 | 9 | ### 2. redis 常用的数据结构【2月18好未来】【2月20滴滴】 10 | 11 | - string 可以用来计数,缓存,分布式锁 12 | - hash 可以用来保存用户的一些属性信息,用户的详情页 13 | - list 可以用来做队列,可以用来做栈,可以用来做数据, 可以维护一个评论列表。lrange区间操作。 14 | - set 可以用来做 交集 并集 差集,微博抽奖,随机事件问题。无序、去重 15 | - sorted set 可以用来做排行榜,带权重的队列。 16 | - bitmap 用来保存用户的登录信息,可以查询最近几个月的登录情况:bitop 可以用来做and or意味着有更多的选择。 17 | - pub/sub 发布订阅 18 | - stream 19 | - hyperloglog 布隆过滤器器(滑动窗口) 20 | 21 | ### 3. redis 底层存储也是使用的字典 22 | 23 | 也就是hashmap,基本随机性比较大,然后很少有冲突。 24 | 25 | ### 4. 跳跃表的时间复杂度 26 | 27 | 一般是O(logn) 最差是 O(n) 28 | 29 | # Redis 高性能、高并发 30 | 31 | ### 1. redis 为什么快 32 | 33 | - 因为全部是内存操作,纯是内存操作,查找和操作的时间都是O(1) 34 | - 单线程操作避免了上下文切换。不存在多线程和多进程导致的切换和消耗CPU。不用考虑各种锁问题 35 | - 可以将一些事务由并行改为串行。 36 | - 使用的是非阻塞io, 37 | - 是使用自己的特定的数据结构,且数据结构比较简单, 38 | - 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。 39 | - 它的数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1); 40 | 41 | ### 2. redis的扩容和扩容是怎样实现的,是否会阻塞 42 | 43 | 因为插入和删除,当数量太多或者太少的时候,就会进行rehash,为了让hash表达的负载因子在一个合理的范围内。需要扩展时,需要将ht[1] = ht[0].used *2 的 2^n。需要收缩时,需要将ht[1]= ht[0].used*2 的2^n。然后将 ht[0] 置为空,ht1[1] = ht[0], 然后再创建新的ht[1] 为下一次rehash做准备。 44 | 45 | 不是一次性,集中式的执行,而是分批次,渐进执行 46 | 因为如果键值数量过大的话,会导致阻塞,在一段时间内不能提供服务 47 | 采用的是分而治之的方式来执行的 48 | 索引期间,查询的话,会先在ht[0] 上面查询,然后新家的话,只会在ht[1]上面新加,这样的话,会保证ht[0] 上面的数据只增不减 49 | 50 | ### 3. 哈希表的扩展与收缩 51 | 52 | 当没有bgsave 和BGREWRITEAOF 的时候,负载因子为1。当有bgsave 和bswriteaof的时候,负载因为大于5。负载因子 = 哈希表已保存节点数量 / 哈希表大小`load_factor = ht[0].used / ht[0].size`。 53 | 54 | ### 4. redis过期提供了几种可选策略 55 | 56 | - `noeviction` 不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。 57 | - `volatile-lru` 尝试淘汰设置了过期时间的 key,最少使用的 key 优先被淘汰。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。 58 | - `volatile-ttl` 跟上面一样,除了淘汰的策略不是 LRU,而是 key 的剩余寿命 ttl 的值,ttl 越小越优先被淘汰。 59 | - `volatile-random` 跟上面一样,不过淘汰的 key 是过期 key 集合中随机的 key。 60 | - `allkeys-lru` 区别于 volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不只是过期的 key 集合。这意味着没有设置过期时间的 key 也会被淘汰。 61 | - `allkeys-random` 跟上面一样,不过淘汰的策略是随机的 key。 62 | 63 | ### 5. redis的LRU删除机制 64 | 65 | LRU 淘汰不一样,它的处理方式只有懒惰处理。当 Redis 执行写操作时,发现内存超出 maxmemory,就会执行一次 LRU 淘汰算法。 66 | 67 | 这个算法也很简单,就是随机采样出 5(可以配置) 个 key,然后淘汰掉最旧的 key,如果淘汰后内存还是超出 maxmemory,那就继续随机采样淘汰,直到内存低于 maxmemory 为止。 68 | 69 | ### 6. ROB的原理 70 | 71 | copy on write。父进程会fork一个子进程,父进程和子进程共享内存空间。父进程继续提供读写服务,脏数据会继续和子进程区分开来。 72 | 73 | 你给出两个词汇就可以了,fork和cow。fork是指redis通过创建子进程来进行RDB操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,脏数据会逐渐和子进程分离开来。 74 | 75 | ### 7. redis rdb持久化方式 76 | 77 | - save 900 1 900秒(15分钟)内有1个更改就同步 78 | - save 300 10 300秒(5分钟)内有10个更改就同步 79 | - save 60 10000 60秒内有10000个更改就同步 80 | 81 | ### 8. redis aof 持久化机制 82 | 83 | - appendfsync=always 每次同步 安全最高,最多丢失一个写入的数据 84 | - appendfsync=everysec 每秒同步 安全最折中,最多丢失1S的数据 85 | - appendfsync=no 安全最低,最多上一次保存AOF文件到当前时刻的全部数据 86 | 87 | ### 9. redis 的持久化, rdb 和aof的区别 88 | 89 | rdb 是做的全量的快照,rdb是fork一个子进程去做持久化的。可以做冷备份,一旦挂了想恢复多久之前的数据,直接拷贝一份就好了。如果丢失数据的话,丢失数据还是比较多的。 90 | 91 | aof 就是做增量添加。可以设置持久化的频率。也可以手动在低频率请求的时候做持久化。后台一秒执行一次fsync的话,丢失的最多也就一秒的的数据。aof是一个非常可读的数据。可以通过修改aof文件,来恢复之前的数据。 92 | 93 | redis在重启的时候,会优先使用aof,因为aof要比rdb完整。rdb丢数据的话,丢失的数据还是比较多的。aof的话,丢数据的话,丢失的还是比较少的。 94 | 95 | ### 10. 主从如何同步 96 | 97 | 当启动一个slave的时候,他发送psync给master。如果是首次连接master,那么master会启动一个线程,进行全量的rdb快照,然后发送给slave。然后把新的请求写到缓存里面,然后slave会执行rdb文件,然后写到自己的本地。然后再读取master里面新增的请求。 98 | 99 | 100 | ### 11. redis 的集群。 101 | 102 | redis cluster 着眼于可扩展。当单个redis不足时,使用cluster进行分片存储。。 103 | 104 | Redis 支持主从同步,提供 Cluster 集群部署模式,通过 Sentine l哨兵来监控 Redis 主服务器的状态。当主挂掉时,在从节点中根据一定策略选出新主,并调整其他从 slaveof 到新主。 105 | 106 | 选主的策略简单来说有三个: 107 | 108 | - slave 的 priority 设置的越低,优先级越高; 109 | - 同等情况下,slave 复制的数据越多优先级越高; 110 | - 相同的条件下 runid 越小越容易被选中。 111 | 112 | 在 Redis 集群中,sentinel 也会进行多实例部署,sentinel 之间通过 Raft 协议来保证自身的高可用。 113 | 114 | ### 12. redis的Sentinal哨兵模式。 115 | 116 | Sentinal就是高可用,master宕机之后,会将slave提升为master继续提供服务。 117 | 118 | 哨兵组件的作用: 119 | - 集群监控:负责监控 Redis master 和 slave 进程是否正常工作。 120 | - 消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。 121 | - 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。 122 | - 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。 123 | 124 | ### 13. 缓存雪崩和缓存穿透 125 | 126 | (1)缓存雪崩:如果大量缓存同时失效,导致流量打到数据库,会给数据库造成压力。 127 | 128 | 为了避免流量全部打到数据库,解决办法可以参考以下几种方式: 129 | 130 | 给过期时间加一个随机值。使过期时间随机一些。比如1-3分钟的过期时间。 131 | 如果是redis实例挂了的话,可以采用限流或者服务降级。针对非核心业务的话,直接降级,等服务恢复。针对核心业务,服务不能停的话,采用限流的方式,每1w次请求,只允许1000个请求通过。可以采用集群的方式,避免实例不可用的情况 132 | 133 | 134 | (2)缓存穿透:就是一直访问不存在的key,绕过缓存。这种情况的解决办法就是使用布隆过滤器。 135 | 136 | 每次设置一个空缓存。这个值可以和业务方沟通好。 137 | 138 | 使用布隆过滤器。若是缓存中,可以知道数据是否存在就可以直接响应返回。若缓存缺失的话,就去数据库中读取。 139 | 140 | 前端可以做一些简单的检测,如果是恶意请求,直接过滤掉,不向后端发请求。 141 | 142 | 143 | (3)缓存击穿:是一个热点的key,在这个key失效的瞬间,持续的高并发就突破了缓存,给数据库造成压力。好想在一个桶上面凿了一个洞。 144 | 145 | 可以将热点数据设置为永远不过期; 146 | 147 | ### 14. redis删除方式 148 | 149 | 惰性删除。惰性删除,避免对每个键,维护删除机制,查询时过期的健值,返回空。无法删除过期但不删除的健 150 | 定期删除。定时删除,10s运行一次,快慢模式删除 151 | 152 | 153 | 154 | ### 15. redis连接数的最大上限 155 | 156 | maxClients:10000 -------------------------------------------------------------------------------- /学习中.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 1. linux 思维导图 4 | 5 | https://www.processon.com/view/link/5f80748f1e085307a07fa38c 6 | 7 | 2. redis 思维导图 8 | 9 | https://www.processon.com/view/link/5f5ad485f346fb7afd52caf0 10 | 11 | 3. 了解架构 12 | 13 | 14 | https://www.processon.com/diagraming/5e757585e4b011fccea4bcef 15 | 16 | 4. mysql 思维导图 17 | 18 | https://www.processon.com/view/link/5e67240ce4b04949eaf0c99e 19 | 20 | https://www.processon.com/view/link/61868e587d9c0828a157400d -------------------------------------------------------------------------------- /操作系统.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | # 进程、线程和协程的区别 6 | 7 | ### 进程 8 | 1. 资源分配的最小单位 9 | 2. 进程拥有独立的地址空间 10 | 3. 进程间通信通过信号量,信号、共享内存、消息传递、管道、队列 ==会经常考这个问题== 11 | 4. 进程由操作系统调度 12 | 5. 多进程方式比多线程稳定 13 | 14 | ### 线程 15 | 16 | 1. 程序执行的最小单元。CPU调度的基本单位 17 | 2. 线程来自于进程,一个进程下可以产生多个线程 18 | 3. 每个线程都有自己一个栈,不共享栈,但多个线程能共享同一个属于进程的堆 19 | 4. 线程共享内存 20 | 5. 线程消耗低于进程 21 | 6. 某个线程产生致命错误会导致进程奔溃 22 | 7. 线程间读写变量存在锁的问题处理起来相对麻烦 23 | ### 协程 24 | 1. 协程的控制由应用程序显式调度,非抢占式的 25 | 2. 协程的执行最终靠的还是线程,应用程序来调度协程选择合适的线程来获取执行权 26 | 3. 切换非常快,成本低。一般占用栈大小远小于线程(协程KB级别,线程MB级别),所以可以开更多的协程 27 | 协程比进程更轻量级 28 | 29 | # 堆和栈的区别? 30 | 栈 是由操作系统来分配的。存放函数的参数,局部变量。其操作方式类似于数据结构中的栈。 31 | 32 | 堆,一般是由程序员分配,若程序员不分配,可能由OS收回。 33 | 34 | -------------------------------------------------------------------------------- /数据结构.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | ## 一致性哈希。 4 | 5 | 将所有节点都分配到0-2^32-1的环上面,所有数据,都按顺时针,存储到他就近的节点上面。 6 | 所有机器按2^32取模。 7 | https://mp.weixin.qq.com/s/bnuo7knTnp1U7ggEh840iQ 8 | 9 | ## 如何保证一段时间内,只允许固定数量的人访问 10 | redis可以实现一个滑动窗口。 -------------------------------------------------------------------------------- /杂项/小米三面.md: -------------------------------------------------------------------------------- 1 | 1. 一年半,你学过什么? 2 | 2. 如何评价你的mysql水平?给自己几分? 3 | 4 | 3. MySQL两个存储引擎的区别? 5 | 4. 遇到过死锁没有? 6 | 5. 用过事务没有,为什么要默认开启事务。PHP还有长连接。长链接和事务,会不会搞死?听起来会很危险的。运行中事务出错之后,能确保一定会回滚呢?如何保证事务一定会回滚?PHP不是所有的场合都会exception。长连接如何管理自己的事务?放到析构函数,能否保证事务一定会回滚?进程崩溃会发生什么?在什么情况下,析构函数不能保证释放? 7 | 8 | 6. 面向对象的好处在哪? 9 | 10 | 7. 你有看过MySQL的代码没有? 11 | 12 | 8. 如何评价PHP这门语言? 代码早期是没有类型的,PHP为什么类型加强? 弱类型好还是不好?php7的 type hint。PHP7的这个能力是为了性能么?你怎么评价类型?为什么弱类型会让人方便?为什么类型会变为一个负担。 13 | 14 | 9. 你用PHP做过好玩的东西没有?做出来没有让你兴奋的东西? 15 | 16 | 10. PHP的GD函数用过没有。要生成一个高度为400 宽度为300 ,写一段文字并在图片的中间居中。 17 | 18 | 11. 你现在对什么感兴趣呢? 19 | 20 | 12. 写一个编程题吧, 就是识别注解。 21 | 22 | //@require(logined, hasPerm('do_xxx') 23 | function getXXX() { 24 | } 25 | 26 | //@require(logined, hasPerm('do_xxx', 'view_xxx')) 27 | 28 | require 29 | logined 30 | hasPerm 31 | do_xxx 32 | view_xxx 33 | 34 | 13. 你觉得如何学好PHP? 35 | 36 | 14. 常用的设计模式用的多么? 37 | 38 | 15. 你对依赖注入的理解? 39 | 40 | 16. 自己评价自己的优点和缺点? -------------------------------------------------------------------------------- /架构.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | 架构学习 -------------------------------------------------------------------------------- /算法题.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | ## 1.查询最长子串 6 | 7 | ``` 8 | count($res)) { 20 | $res = $r; 21 | } 22 | } 23 | return implode('', $res); 24 | } 25 | 26 | $str = 'abcab'; 27 | 28 | print_r(getString($str)); 29 | ``` 30 | 31 | ## 2. 母牛问题。有一母牛,到4岁可生育,每年一头,所生均是一样的母牛,到15岁绝育,不再能生,20岁死亡,问n年后有多少头牛? 32 | 33 | ``` 34 | 0; $j--) { 43 | $cowMap[$j] = $cowMap[$j - 1] ?? 0; 44 | } 45 | $cowMap[0] = 0; 46 | for ($j = 4; $j < 15; $j++) { 47 | $cowMap[0] += $cowMap[$j] ?? 0; 48 | } 49 | } 50 | array_walk($cowMap, function ($value) use (&$count) { 51 | $count += $value; 52 | }); 53 | return $count; 54 | } 55 | ``` 56 | 57 | ## 给定String类型的数组strArr,再给定整数k,请严格按照出现顺序打印出现出现次数前k名的字符串。strArr=["1","3","3","4","1","5","1"], k=3 No.1:1,times:3 No.2:3,times:2 No.3:4,times:1 要求:如果strArr长度为N,时间复杂度请达到O(Nlogk)【2月16 头条】 58 | ``` 59 | function getSort($str, $k = 1) { 60 | $arr = array(); 61 | 62 | foreach ($str as $val) { 63 | if (!isset($arr[$val])) { 64 | $arr[$val] = 1; 65 | } else { 66 | $arr[$val]++; 67 | } 68 | } 69 | arsort($arr); 70 | return array_slice($arr, 0, $k); 71 | } 72 | 73 | $strArr = ["1", "3", "3", "3", "3", "4", "1", "5", "1"]; 74 | 75 | var_dump(getSort($strArr, 3)); 76 | ``` 77 | 78 | ## 求中位数。(【1,2】, 【3,4】) 79 | ``` 80 | findMedianSortedArrays($nums1, $nums2)); 123 | ``` 124 | # 算法题,100元红包,分给10个人,没人金额在8-12元之间 125 | 先分给没人8元,然后再随机跟每个用户分钱。这种方式还是比较简单和粗暴的。 126 | ``` 127 | 0){ 131 | $user_id = rand(0, 9); 132 | if($user_arr[$user_id]<12){ 133 | $user_arr[$user_id]++; 134 | $cash--; 135 | } 136 | } 137 | var_dump($user_arr,array_sum($user_arr));die; 138 | ``` 139 | 140 | # 翻转链表 141 | 142 | ```` 143 | /** 144 | * Definition for a singly-linked list. 145 | * class ListNode { 146 | * public $val = 0; 147 | * public $next = null; 148 | * function __construct($val) { $this->val = $val; } 149 | * } 150 | */ 151 | class Solution { 152 | 153 | /** 154 | * @param ListNode $head 155 | * @return ListNode 156 | */ 157 | function reverseList($head) { 158 | if (empty($head) || empty($head->next)) return $head; 159 | $p = $this->reverseList($head->next); 160 | $head->next->next = $head; 161 | $head->next = null; 162 | return $p; 163 | } 164 | } 165 | ```` 166 | 167 | 168 | # 已知一随机发生器,产生0的概率是P,产生1的概率是1-P。现在需要构造一个发生器,使得它构造0和1的概率均为1/2,请写出思路或伪代码 169 | ```` 170 | 由于需要产生1/2,而用1位0,或1位1无法产生等概率,因此,考虑将随机数扩展成2位: 171 | 172 | 00 p*p 173 | 174 | 01 p*(1-p) 175 | 176 | 10 (1-p)*p 177 | 178 | 11 (1-p)*(1-p) 179 | 180 | 有上述分析知道,01和10是等概率的,因此我们只需要产生01和10就行了。于是可以,遇到00和11就丢弃,只记录01和10。可以令,01表示0,10表示1,则等概率1/2产生0和1了。 181 | ```` 182 | 183 | # 求解最大的k个数。 184 | 185 | 186 | # 求解两个有序数据里面,是否有相同的元素,不允许用php函数。 187 | 188 | # 求解1的个数 189 | ```` 190 | deep($root); 234 | } 235 | 236 | function deep($root) { 237 | if (empty($root->left) && empty($root->right)) { 238 | return 1; 239 | } 240 | 241 | if ($root->left) { 242 | $leftDeep = $this->deep($root->left) + 1; 243 | } 244 | 245 | if ($root->right) { 246 | $rightDeep = $this->deep($root->right) + 1; 247 | } 248 | 249 | return min($leftDeep, $rightDeep); 250 | } 251 | ```` 252 | 253 | # 什么是hash 254 | hash就是对数组的一种延伸吧。 255 | 256 | 以数组为例,key从1-89.但是假如用户的key是0105111111这种形式呢?就得需要hash计算去进行对应到响应的key,比如说对应的key值是1. 257 | 258 | 259 | 260 | # 快速排序 261 | ```` 262 | quickSort($left); 288 | $right = $this->quickSort($right); 289 | 290 | return array_merge($left, [$provit], $right); 291 | } 292 | ```` 293 | 294 | # 一个索引表 a => 2000W,数据有2000w,然后索引字段为 295 | { 296 | b, 297 | c, 298 | d 299 | } 300 | 301 | b+c index 索引为bc 302 | 下面这个语句能命中索引么? 303 | ```` 304 | select * from a where c = 1 and b = 2 order by d asc limit 10; 305 | ```` 306 | 能命中bc索引,但是这个语句效率不高,因为d上面没有索引,导致按d排序会很慢。 307 | 308 | # 只考虑数据库的情况,A => 500个库存,2000个人同时过来抢。如何避免超卖的问题。 309 | ```` 310 | 商品的库存表 311 | { 312 | sku_no, 313 | num, 314 | } 315 | ```` 316 | // 比如用户抢购商品数量为5 317 | // 如果抢购数量表比库存大, 直接返回 318 | ```` 319 | ---- 320 | begin; 321 | select num from goods where sku_no = xxxxx for update; 322 | update goods set num = num - 5 where sku_no = xxxxx; 323 | commit 324 | ------ 325 | ```` 326 | 用户订单表 327 | ```` 328 | { 329 | user_id, 330 | sku_no, 331 | num 332 | } 333 | ```` 334 | ```` 335 | insert into order values(xxxx, xxxx, 5); 336 | ```` 337 | 338 | # 有64只兔子,8条跑道,每次跑8只兔子,可以决出1~8名次,最少跑几次可以算出前3名 339 | 1. 首先64只兔子,分成8组,然后都跑一遍。跑8次 340 | 2. 然后每组里面的第一名都跑一次。然后 分出名次。第8名,以及他所在的组,就不用考虑了。 341 | 3. 然后让第一组的 23 和第二组的第12 第三组的1,再去跑一下。 342 | 第一二三组的第一名,都取取出来。 343 | 344 | 345 | # 求每个渠道的转换率 346 | select channel,sum(register)/sum(activate) as per from t where activate_time = '2020-03-10' group by channel; 347 | 348 | # 是很大的文件,每一行都是一个数。最大的一个数值。 349 | 1,2,3,2,3,4,5 350 | 351 | # 二叉树的反转 352 | ```` 353 | left) { 364 | $left = get($root->left); 365 | } 366 | 367 | if ($root->right) { 368 | $right = get($root->right); 369 | } 370 | 371 | $root->left = $right; 372 | $root->right = $left; 373 | return $root; 374 | } 375 | ```` 376 | 如果不用递归,该怎么实现呢?面试问道了。用队列就可以。 377 | 378 | # ip to int 将一个ip地址(182.92.31.125)转成整数? 379 | ```` 380 | 0 && j < len(s) - 1 && s[k - 1] == s[j + 1] { 417 | k = k - 1 418 | j = j + 1 419 | } 420 | 421 | tmpLen := j - k + 1 422 | if tmpLen > maxLen { 423 | minIndex = k 424 | maxIndex = j + 1 425 | } 426 | } 427 | 428 | return s[minIndex:maxIndex] 429 | } 430 | ``` 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | ``` 439 | function filterIP($ip){ 440 | $ip_low = ip2Int($ip_low); 441 | $ip_top = ip2Int($ip_top); 442 | $black_low = ip2Int($black_low); 443 | $black_top = ip2Int($black_top); 444 | $ip = ip2Int($ip); 445 | if ($ip >= $black_low && $ip <= $black_top) { 446 | return false; 447 | } 448 | if ($p < $ip_low || $ip > $ip_top) { 449 | return false; 450 | } 451 | 452 | return true; 453 | } 454 | 455 | function ip2Int($ip) { 456 | $data = 0; 457 | $str = explode('.', $p); 458 | for ($i = 0; $i < count($str); $i++) { 459 | $data &= ($str[$i] << (8 * $i)); 460 | } 461 | return $data; 462 | } 463 | ``` 464 | 465 | # 目标路径和。 466 | ``` 467 | /** 468 | * Definition for a binary tree node. 469 | * struct TreeNode { 470 | * int val; 471 | * TreeNode *left; 472 | * TreeNode *right; 473 | * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 474 | * }; 475 | */ 476 | class Solution { 477 | public: 478 | bool hasPathSum(TreeNode* root, int sum) { 479 | if(!root)return false; 480 | if((!root->left)&&(!root->right)&&sum==root->val){ 481 | return true; 482 | } 483 | return hasPathSum(root->left,sum-root->val)||hasPathSum(root->right,sum-root->val); 484 | } 485 | }; 486 | ``` 487 | 488 | 非递归形式怎么实现? 489 | 490 | 491 | # 下面会输出什么? 492 | ``` 493 | 494 | $items = ['a', 'b', 'c', 'd']; 495 | 496 | 497 | foreach($items as &$item) { 498 | } 499 | 500 | foreach($items as $item) { 501 | } 502 | 503 | echo json_encode($items); 504 | 505 | ----------------------------- 506 | 507 | $items = ['a', 'b', 'c', 'd']; 508 | 509 | 510 | foreach($items as $item) { 511 | } 512 | 513 | foreach($items as &$item) { 514 | } 515 | 516 | echo json_encode($items); 517 | 518 | ``` 519 | 520 | 521 | # 输出一个合法的ip 522 | 题目 523 | 524 | 全部名单 [127.0.0.1] [195.0.0.1] 525 | 526 | 黑名单 527 | [185.0.0.1] [187.0.0.1] 528 | 529 | ``` 530 | 531 | ip2long('195.0.0.1')) { 539 | return false; 540 | } 541 | if (ip2long($ip) > ip2long('185.0.0.1') && ip2long($ip) < ip2long('187.0.0.1')) { 542 | return false; 543 | } 544 | return $ip; 545 | } 546 | 547 | var_dump(getValidIp('184.0.0.1')); 548 | var_dump(getValidIp('18666666.0.0.1')); 549 | ``` 550 | 551 | # 每隔k个节点,进行翻转 552 | 553 | 554 | 555 | 556 | # 动态最短路径之和,面试官说是入门级 557 | ``` 558 | 559 | 560 | ``` 561 | 562 | 563 | # 最近公共父节点 564 | ``` 565 | 566 | 567 | ``` 568 | 569 | 570 | # 去掉几个数字,剩下的数字最大? 571 | 572 | 例如数字9784596 比如去掉4个数字,剩下的数字最大,应该是剩 456. 573 | 574 | 应该考虑的是一个增进的关系吧。 575 | ``` 576 | 577 | 578 | 579 | ``` 580 | 581 | # 零钱兑换 头条 582 | 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 583 | 584 | 585 | 586 | 示例 1: 587 | 588 | 输入: coins = [1, 2, 5], amount = 11 589 | 输出: 3 590 | 解释: 11 = 5 + 5 + 1 591 | -------------------------------------------------------------------------------- /计算机网络.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [toc] 4 | 5 | 6 | 7 | # 关闭TCP连接的四次挥手 8 | 9 | 第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了; 10 | 11 | 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我"同意"你的关闭请求;进去close_wait状态 12 | 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态; 13 | 14 | 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。就是CLOSED状态。 15 | 16 | ![三次握手](images/网络协议/四次挥手.png) 17 | 18 | # 建立TCP连接 19 | 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认; 20 | 21 | 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态; 22 | 23 | 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。 24 | ![三次握手](images/网络协议/三次握手.png) 25 | 26 | # HTTP会慢吗? 27 | 使用ssl通信会比较慢,因为涉及CPU和内存消耗,需要加密解密;并不是所有的通信都需要加密;有些公司会觉得购买证书不划算,会不采用https。 28 | 29 | 30 | # TCP 粘包如何解决? 31 | TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。 32 | 33 | 如何解决: 34 | 35 | (1)发送方 36 | 37 | 对于发送方造成的粘包现象,我们可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭Nagle算法。 38 | 39 | (3)应用层处理 40 | 41 | 应用层的处理简单易行!并且不仅可以解决接收方造成的粘包问题,还能解决发送方造成的粘包问题。解决方法就是循环处理:应用程序在处理从缓存读来的分组时,读完一条数据时,就应该循环读下一条数据,直到所有的数据都被处理;但是如何判断每条数据的长度呢?两种途径: 42 | 43 | 1)格式化数据:每条数据有固定的格式(开始符、结束符),这种方法简单易行,但选择开始符和结束符的时候一定要注意每条数据的内部一定不能出现开始符或结束符; 44 | 45 | 2)发送长度:发送每条数据的时候,将数据的长度一并发送,比如可以选择每条数据的前4位是数据的长度,应用层处理时可以根据长度来判断每条数据的开始和结束。 46 | 47 | # 堆栈 48 | 栈 是由操作系统来分配的。存放函数的参数,局部变量。其操作方式类似于数据结构中的栈。 49 | 50 | 堆,一般是由程序员分配,若程序员不分配,可能由OS收回。 51 | 52 | # RESTful 是什么 53 | 54 | 看url就知道要干什么。 55 | 56 | 看method就知道要干什么 57 | 58 | 看状态码就知道结果怎么样 59 | 60 | # HTTP 与 HTTPS 区别 61 | 62 | HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。 63 | 64 | 使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。证书颁发机构如:Symantec、Comodo、GoDaddy 和 GlobalSign 等。 65 | HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。 66 | http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。 67 | HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。 68 | # TIME-WAIT 是什么,为什么必须等待 2MSL 69 | TIME-WAIT 是一种 TCP 状态。等待 2MSL 可以保证客户端最后一个报文段能够到达服务器,如果未到达,服务器则会超时重传连接释放报文段,使得客户端、服务器都可以正常进入到 CLOSE(关闭) 状态 70 | # cookie、session的联系和区别,多台web服务器如何共享session? 71 | cookie在客户端保存状态,session在服务器端保存状态。但是由于在服务器端保存状态的时候,在客户端也需要一个标识,所以session也可能要借助cookie来实现保存标识位的作用。 72 | 73 | cookie包括名字,值,域,路径,过期时间。路径和域构成cookie的作用范围。cookie如果不设置过期时间,则这个cookie在浏览器进程 74 | 存在时有效,关闭时销毁。如果设置了过期时间,则cookie存储在本地硬盘上,在各浏览器进程间可以共享。 75 | 76 | session存储在服务器端,服务器用一种散列表类型的结构存储信息。当一个连接建立的时候,服务器首先搜索有没有存储的session id,如果没有,则建立一个新的session,将session id返回给客户端,客户端可以选择使用cookie来存储session id。也可以用其他的方法,比如服务器端将session id附在URL上。 77 | 78 | 区别: 79 | 80 | (1).cookie在本地,session在服务器端。 81 | 82 | (2).cookie不安全,容易被欺骗,session相对安全。 83 | 84 | (3).session在服务器端,访问多了会影响服务器性能。 85 | 86 | (4). cookie有大小限制,为3K。多服务器共享session可以尝试将session存储在memcache或者redis中。 87 | 88 | # cookie 的作用: 89 | 会话状态管理 90 | 91 | 个性化推荐 92 | 93 | 浏览器行为追踪。 94 | 95 | domain决定了哪些主机可以使用cookie 96 | 97 | path决定了哪些端口可以使用cookie。 98 | # http协议中的post和get有何区别? 99 | 100 | GET用于获取信息,不应该用于修改信息,pOST可用于更新修改信息。 101 | 102 | GET可传输数据大小和URL有关,而pOST没有限定大小,大小和服务器配置有关。 103 | 104 | GET放在URL中,因此不安全,而pOST传输数据对于用户来说是不可见的,所以相对安全。 105 | 106 | 在ajax: post不被缓存,get被缓存所以一般在请求结尾加Math.random(); 107 | 108 | SERVER端接受:因为在submit提交的时候是按不同方式进行编码的,所以服务端在接受的时候会按照不同的方式进行接受! 109 | 110 | 编码方式:如果传递数据是非-ASCII,那么GET一般是不适应的,所以在传递的时候会做编码处理! 111 | # 说一下熟悉的 http 状态码和其代表的意义 112 | 200 : 请求成功,请求的数据随之返回。 113 | 114 | 301 : 永久性重定向。 115 | 116 | 302 : 暂时行重定向。 117 | 118 | 401 : 当前请求需要用户验证。 119 | 120 | 403 : 服务器拒绝执行请求,即没有权限。 121 | 122 | 404 : 请求失败,请求的数据在服务器上未发现。 123 | 124 | 500 : 服务器错误。一般服务器端程序执行错误。 125 | 126 | 501 :服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。 127 | 128 | 502: 一般是fpm出问题了 129 | 130 | 503 : 服务器临时维护或过载。这个状态时临时性的。 131 | 132 | 504:一般是NGINX配置有问题。 133 | # TCP和UDP 134 | udp是非面向连接的,尽量保证数据交付,非按顺序到达。头部开销小,实现简单 135 | 1. 源端口号 136 | 2. 目的端口号 137 | 3. udp长度 138 | 4. udp校验和 139 | ![udp](images/网络协议/udp.jpeg) 140 | 141 | tcp是面向连接的,能保证不丢失数据。流量控制,阻塞控制。提供可靠的数据服务。一对一。 142 | 143 | tcp头部字段: 144 | 1. 源端口号,目的端口号 145 | 2. 序号 146 | 3. 确认 序号 147 | 4. ack 确认字段 148 | 5. psh 是否立即发往顶层 149 | 6. rst 是否重发 150 | 7. syn 序列号 151 | 8. fin 关闭 152 | 9. 窗口大小 153 | 10. 校验和 154 | ![tcp](images/网络协议/tcp.jpeg) 155 | 156 | # https的建立连接过程 157 | 1. 服务器端将自己的公钥发送到CA,然后CA通过数字加密服务器的公钥,然后生成公钥证书,返回给服务器 158 | 2. 服务器端将公钥证书发送给客户端 159 | 3. 客户端拿公钥证书去CA进行验证, 如果验证通过获取公钥。 160 | 4. 客户端通过公钥加密自己生成的对称加密的秘钥,然后发送给服务器。 161 | 5. 服务器通过自己的私钥进行解密获取秘钥。 162 | 6. 双方通过对称加密进行通信。 163 | 164 | # 什么是cdn 165 | 是内容、分发、网络。主要是保存的静态资源。 166 | 全局负载均衡(Global Sever Load Balance)一般简称为 GSLB,它是 CDN 的“大脑” 他会选择一个最佳的节点,然后提供服务,他的任务就是选择一个最佳的节点。对整个cdn网络进行负载均衡。 167 | 168 | 加入cdn后,dns返回的不是服务器的ip,而是gslb的地址。因为dns没有拿到ip,就去请求gslb,进入他的全局负载均衡里面。 169 | 170 | - 看用户的 IP 地址,查表得知地理位置,找相对最近的边缘节点; 171 | - 看用户所在的运营商网络,找相同网络的边缘节点; 172 | - 检查边缘节点的负载情况,找负载较轻的节点; 173 | 其他,比如节点的“健康状况”、服务能力、带宽、响应时间等。 174 | 175 | 命中率就是 命中次数和所有访问次数之比。回源率就是回源次数和所有访问次数之比。 176 | 177 | CDN 发展到现在已经有二十来年的历史了,早期的 CDN 功能比较简单,只能加速静态资源。随着这些年 Web 2.0、HTTPS、视频、直播等新技术、新业务的崛起,它也在不断进步,增加了很多的新功能,比如 SSL 加速、内容优化(数据压缩、图片格式转换、视频转码)、资源防盗链、WAF 安全防护等等。 178 | 179 | # 状态码499  180 | 可以看到,499对应的是 “client has closed connection”。这很有可能是因为服务器端处理的时间过长,客户端“不耐烦”了。 181 | 测试nginx发现如果两次提交post过快就会出现499的情况,看来是nginx认为是不安全的连接,主动拒绝了客户端的连接. 182 | 183 | 在google上搜索到一英文论坛上有关于此错误的解决方法: 184 | proxy_ignore_client_abort on; 185 | Don’t know if this is safe. 186 | 就是说要配置参数 proxy_ignore_client_abort on; 187 | 表示代理服务端不要主要主动关闭客户端连接。 188 | 189 | # Websocket 190 | HTTP 的“请求 - 应答”模式不适合开发“实时通信”应用,效率低,难以实现动态页面,所以出现了 WebSocket; 191 | WebSocket 是一个“全双工”的通信协议,相当于对 TCP 做了一层“薄薄的包装”,让它运行在浏览器环境里; 192 | WebSocket 使用兼容 HTTP 的 URI 来发现服务,但定义了新的协议名“ws”和“wss”,端口号也沿用了 80 和 443; 193 | WebSocket 使用二进制帧,结构比较简单,特殊的地方是有个“掩码”操作,客户端发数据必须掩码,服务器则不用; 194 | WebSocket 利用 HTTP 协议实现连接握手,发送 GET 请求要求“协议升级”,握手过程中有个非常简单的认证机制,目的是防止误连接。 195 | 196 | 而 WebSocket 针对的是“请求 - 应答”通信模式。那么,“请求 - 应答”有什么不好的地方呢? 197 | 198 | “请求 - 应答”是一种“半双工”的通信模式,虽然可以双向收发数据,但同一时刻只能一个方向上有动作,传输效率低。更关键的一点,它是一种“被动”通信模式,服务器只能“被动”响应客户端的请求,无法主动向客户端发送数据。 199 | 200 | 虽然后来的 HTTP/2、HTTP/3 新增了 Stream、Server Push 等特性,但“请求 - 应答”依然是主要的工作方式。这就导致 HTTP 难以应用在动态页面、即时消息、网络游戏等要求“实时通信”的领域。 201 | 202 | # 对HTTP协议的理解。包括什么,由什么构成,怎么去发现问题。 203 | 1. 三次握手 204 | 2. 四次挥手 205 | 206 | 1. 请求头 207 | 2. 请求体 208 | 3. body 209 | 210 | 1. 请求头的方法。put, head get post 211 | 2. 状态码 500 和501 212 | 213 | 501 Not Implemented。服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求 214 | 215 | cookie 和 session的区别 216 | 217 | 单点登录自己搞过么,是怎么实现的。 218 | 219 | session共享。单点,没有自己搞过么?那有了解过么?单点登录。 220 | 221 | # 用消息队列,解决什么问题? 222 | 223 | 224 | 225 | 226 | 227 | # 301和302 的区别和使用场景 228 | 301 是永久重定向,比如 域名换了,浏览器更新历史记录,更新书签。或者是使用新域名,或者是使用新机器,或者是 目录结构进行了改变。 229 | 230 | 302 临时重定向,不会更新url。只是暂时不能访问。或者用来进行服务降级。 231 | 232 | 通过location 进行跳转。或者code 233 | 234 | 会浪费资源。 235 | 236 | 重复调用的话,也会增加资源的浪费, 237 | 238 | # Http的的实体 239 | 1. 数据类型,实体内容的类型是什么,主要是accept和 content-type 240 | 241 | 比如 accept: text/html, application/xml, image/png, image/webp, 告诉服务器,我可以接受这些类型的数据。 242 | 243 | content: text/html, image/png 告诉客户端, 我返回的数据类型。 244 | 2. 数据压缩类型实体数据的压缩方式。accept-encoding, content-encoding。 245 | 246 | accept-encoding: gzip, deflate, br 247 | 248 | content-encoding:gzip 249 | 250 | 3. 语言类型类型。 accept-language, content-language. zh-En, zh, en 等方式 251 | 252 | accept-language: zh-CN, en,zh 253 | 254 | content-language:zh-CN 255 | 256 | 4. 字符集表示的编码方式。accept-charset, content-type。 257 | 258 | accept-charset:utf-8 259 | 260 | content-type:text/html, charset=utf-8 261 | 262 | 5. 客户端需要在accept等字段里面,跟服务器进行内容协商。要求服务器返回合适的数据。 263 | 6. accept里面可以设置权重。 用"," 进行分隔。 264 | 265 | # Http 如何传输大文件? 视频之类的数据如何 分段传输? 266 | 1. chucked 267 | 2. 范围传输,范围选择 268 | ``` 269 | HTTP/1.1 206 Partial Content 270 | Content-Length: 32 271 | Accept-Ranges: bytes 272 | Content-Range: bytes 0-31/96 273 | 274 | // this is a plain text json doc 275 | ``` 276 | Accept-Ranges 表示接受范围传输,Content-Range标识内容的范围。状态码为206 277 | 278 | 因为传输的结果里面有长度标识,所以\r\n 不会截断数据。 279 | 280 | # X-Forwarded-For 和 X-Real-IP 281 | 而“X-Forwarded-For”追加的是请求方的 IP 地址 282 | 283 | X-Real-IP 记录的是客户端的地址,如果只有一层代理的话。 284 | 285 | X-Real-IP 和 X-Forwarded-For 是一致的。 286 | 287 | # 代理的缺点 288 | 代理做做一些逻辑操作, 会影响性能,增加链路长度,增加响应时间 289 | -------------------------------------------------------------------------------- /设计模式.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | 4 | 5 | ## 1. 单例模式(单例模式写一个数据库连接) 6 | 7 | ``` 8 | 路由->一至多个队列 61 | 消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。 62 | 通过队列路由键,可以把队列绑定到交换器上。 63 | 64 | 消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则); 65 | 66 | 常用的交换器主要分为一下三种 67 | 68 | - fanout:如果交换器收到消息,将会广播到所有绑定的队列上 69 | - direct:如果路由键完全匹配,消息就被投递到相应的队列 70 | - topic:可以使来自不同源头的消息能够到达同一个队列。 使用topic交换器时,可以使用通配符 71 | 72 | 73 | ## 9、如何确保消息不丢失? 74 | 消息持久化,当然前提是队列必须持久化 75 | RabbitMQ确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上的一个持久化日志文件,当发布一条持久性消息到持久交换器上时,Rabbit会在消息提交到日志文件后才发送响应。 76 | 77 | 一旦消费者从持久队列中消费了一条持久化消息,RabbitMQ会在持久化日志中把这条消息标记为等待垃圾收集。如果持久化消息在被消费之前RabbitMQ重启,那么Rabbit会自动重建交换器和队列(以及绑定),并重新发布持久化日志文件中的消息到合适的队列。 78 | 79 | ## 10、使用RabbitMQ有什么好处? 80 | 1、服务间高度解耦 81 | 2、异步通信性能高 82 | 3、流量削峰 83 | 84 | 85 | ## 11、rabbitmq的集群 86 | 镜像集群模式 87 | 88 | 你创建的queue,无论元数据还是queue里的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。 89 | 90 | 好处在于,你任何一个机器宕机了,没事儿,别的机器都可以用。坏处在于,第一,这个性能开销也太大了吧,消息同步所有机器,导致网络带宽压力和消耗很重!第二,这么玩儿,就没有扩展性可言了,如果某个queue负载很重,你加机器,新增的机器也包含了这个queue的所有数据,并没有办法线性扩展你的queue 91 | 92 | 93 | ## 12.mq的缺点 94 | 系统可用性降低 95 | 96 | 系统引入的外部依赖越多,越容易挂掉,本来你就是A系统调用BCD三个系统的接口就好了,人ABCD四个系统好好的,没啥问题,你偏加个MQ进来,万一MQ挂了咋整?MQ挂了,整套系统崩溃了,你不就完了么。 97 | 98 | 系统复杂性提高 99 | 100 | 硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已 101 | 102 | ## 13.一致性问题 103 | 104 | A系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,咋整?你这数据就不一致了。 105 | 106 | 所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,最好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了10倍。但是关键时刻,用,还是得用的 107 | 108 | ## 14. 分布式事务 109 | 分段提交。会有一个仲裁者,然后给所有节点来发消息。当所有节点都ack之后,才会成功。否则就得等待重发。 110 | 111 | ## 15.针对直播这种突然大流量的情况,该怎么设计。 112 | 1. NGINX加机器 113 | 2. cdn缓存静态页面 114 | 3. redis队列,让用户慢点进来。 115 | 4. 加缓存。缓存用户数据,比如用户信息。 116 | 5. 数据库使用主从 117 | 6. 弹性扩容 118 | 7. 限流熔断 119 | --------------------------------------------------------------------------------