diff --git a/.gitattributes b/.gitattributes index e2e421c..2125666 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -*.* linguist-language=php \ No newline at end of file +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/CONTENT.md b/CONTENT.md new file mode 100644 index 0000000..31c86ef --- /dev/null +++ b/CONTENT.md @@ -0,0 +1,41 @@ +# PHP面试指南 + +> 忘掉你的经验,试试我的方法。 + +## 市场前景 + +### php份额 + +### 技术成熟度曲线 + +## 薪资水平 + +### 程序员 + +### php + +## 作者介绍 + +### 从业经验 + +### 为什么要写这个 + +## 使用指南 + +### 应聘者 + +### 面试官 + +## 我理解的面试 + +### 岗位的来由 + +### 面试流程 + +### 工作能力证明 + +## 问答列表 + +## 参考资料 + +## 版权声明 \ No newline at end of file diff --git a/README.md b/README.md index 7c404c0..413e778 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,64 @@ -# PHP面试问答 +# PHP 面试问答 + +> PHP 很没前途,请把工作留给我,谢谢! + +## 一、PHP 篇 + +- [【试读】编程语言基础](./docs/php/编程语言简介.md) +- 【试读】PHP 与编程范式 +- [【试读】当下最流行的 PHP 本地环境搭建方式](./docs/php/当下最流行的PHP本地环境搭建方式.md) +- 【试读】代码风格指南 +- 【试读】代码注释 +- [【试读】将变量打印出来,你知道哪些方式](./docs/php/将变量打印出来你知道哪些方式.md) +- 【试读】使用 xdebug 调试你的代码 +- 【试读】基础知识 +- [【试读】单引号和双引号的区别](./docs/php/单引号和双引号的区别.md) +- [【试读】isset 和 empty 的区别之如何判空](./docs/php/isset和empty的区别之如何判空.md) +- 【试读】面向对象编程 +- 【试读】PHP 与函数式编程 +- 【试读】PHP 与元编程 +- 【试读】命名空间 +- 【试读】PHP 标准库(SPL) +- 【试读】日期和时间 +- 【试读】请使用 UTF-8 编码 +- 【试读】本地化与国际化 +- 【试读】2025 年有哪些流行的框架 +- 【试读】如何进行依赖管理 +- 【试读】依赖注入 +- 【试读】错误与异常 +- [【试读】composer 包升级](./docs/php/composer包升级.md) +- 【试读】MySQL 扩展 +- 【试读】PDO 扩展 +- 【试读】单元测试 +- 【试读】Postman + +## 二、数据结构与算法篇 + +## 三、网络篇 + +## 四、设计模式篇 + +- 【试读】如何解决复杂问题 + +## 五、数据基础设施篇 + +## 六、架构篇 + +-【试读】领域驱动设计(DDD) + +## 七、服务器篇 + +## 八、安全篇 + +- 【试读】密码学简介 +- 【试读】加密与编码 + +## 九、客户端篇 + +## 十、番外篇 + +- [【试读】你的编码热情是如何消退的?](./docs/番外/你的编码热情是如何消退的.md) + 结合实际PHP面试,汇总自己遇到的问题,以及网上其他人遇到的问题,尝试提供简洁准确的答案 @@ -14,54 +74,45 @@ - 本仓库需要什么内容:`实际经典面试题` + `靠谱简答` + `详细深入文章(必要的话)` -## 面试流程 +## 一、面试准备 ![面试流程](./docs/assets/interview.png) -## 问题列表 +> 简历 -> 笔试 -> 技术一面 -> 技术二面 -> 技术三面 -> HR面 -> offer -### 网络篇 +- [技术岗面试潜规则](./docs/面试/技术岗面试潜规则.md) +- [设计一份吸引面试官的简历](./docs/面试/设计一份吸引面试官的简历.md) +- [读懂岗位精准投递](./docs/面试/读懂岗位精准投递.md) +- [做好充分的准备去面试](./docs/面试/做好充分的准备去面试.md) +- [把握面试时的关键点](./docs/面试/把握面试时的关键点.md) +- [捕捉面试官微表情,做出应对策略](./docs/面试/捕捉面试官微表情,做出应对策略.md) +- [巧妙推销自己的3个技巧](./docs/面试/巧妙推销自己的3个技巧.md) +- [判断公司背景,做出合理选择](./docs/面试/判断公司背景,做出合理选择.md) +- [了解行业薪资,清晰找准定位](./docs/面试/了解行业薪资,清晰找准定位.md) +- [目标明确,阐明沟通](./docs/面试/目标明确,阐明沟通.md) +- [工作交接流程福利衔接](./docs/面试/工作交接流程福利衔接.md) +- [如何让工作年限变成优势](./docs/面试/如何让工作年限变成优势.md) -- [计算机网络体系结构](./docs/01.网络.md#1-计算机网络体系结构) -- [UDP 的主要特点](./docs/01.网络.md#2-udp-的主要特点) -- [TCP 的主要特点](./docs/01.网络.md#3-tcp-的主要特点) -- [简述三报文握手建立 TCP 连接](./docs/01.网络.md#4-简述三报文握手建立-tcp-连接) -- [建立 TCP 连接为什么最后还要发送确认](./docs/01.网络.md#5-建立-tcp-连接为什么最后还要发送确认) -- [简述 TCP 连接的释放](./docs/01.网络.md#6-简述-tcp-连接的释放) -- [TIME-WAIT 是什么,为什么必须等待 2MLS](./docs/01.网络.md#7-time-wait-是什么为什么必须等待-2mls) -- [TCP 粘包问题](./docs/01.网络.md#8-tcp-粘包问题) -- [UDP、TCP 区别,适用场景](./docs/01.网络.md#9-udptcp-区别适用场景) -- [建立 socket 需要哪些步骤](./docs/01.网络.md#10-建立-socket-需要哪些步骤) -- [DNS 主要作用是什么](./docs/01.网络.md#11-dns-主要作用是什么) -- [HTTP 报文组成](./docs/01.网络.md#12-http-报文组成) -- [HTTP 状态码](./docs/01.网络.md#13-http-状态码) -- [常见的 HTTP 方法](./docs/01.网络.md#14-常见的-http-方法) -- [GET 与 POST 请求方式区别](./docs/01.网络.md#15-get-与-post-请求方式区别) -- [HTTP 优缺点](./docs/01.网络.md#16-http-优缺点) -- [HTTPS 通信原理](./docs/01.网络.md#17-https-通信原理) -- [HTTP 2.0](./docs/01.网络.md#18-http-20) -- [WebSocket](./docs/01.网络.md#19-websocket) -- [IPv6 与 IPv4 有什么变化](./docs/01.网络.md#20-ipv6-与-ipv4-有什么变化) -- [什么是心跳机制](./docs/01.网络.md#21-什么是心跳机制) -- [什么是长连接](./docs/01.网络.md#22-什么是长连接) +## 二、问答列表 -### 数据结构与算法篇 +问题和内容分级 -- [概述](./docs/02.数据结构与算法.md#1-概述) -- [实现基础](./docs/02.数据结构与算法.md#2-实现基础) -- [线性结构](./docs/02.数据结构与算法.md#3-线性结构) -- [树](./docs/02.数据结构与算法.md#4-树) -- [散列查找](./docs/02.数据结构与算法.md#5-散列查找) -- [图](./docs/02.数据结构与算法.md#6-图) -- [排序](./docs/02.数据结构与算法.md#7-排序) -- [补充](./docs/02.数据结构与算法.md#8-补充) -- [经典算法题](./docs/02.数据结构与算法.md#9-经典算法题) +|星标|难度|岗位|关键字| +|---|---|---|---| +|*|一星|助理工程师|基础知识| +|**|二星|工程师|灵活使用| +|***|三星|高级工程师|深入原理| +|****|四星|资深工程师|疑难杂症| +|****|五星|架构师/专家|领域话语| + +问答模版: -### PHP 篇 +1、工作内容->实际问题->用到知识->设置问题->期望回答->评分表->面试评价 + +2、被安排充当面试官->网上找题库->题库(自己熟悉的)->八股文->筛选 + +### 1、PHP篇 -- [echo、print、print_r、var_dump 区别](./docs/03.PHP/QA.md#echoprintprint_rvar_dump-区别) -- [单引号和双引号的区别](./docs/03.PHP/QA.md#单引号和双引号的区别) -- [isset 和 empty 的区别](./docs/03.PHP/QA.md#isset-和-empty-的区别) - [static、self、$this 的区别](./docs/03.PHP/QA.md#staticselfthis-的区别) - [include、require、include_once、require_once 的区别](./docs/03.PHP/QA.md#includerequireinclude_oncerequire_once-的区别) - [数组处理函数](./docs/03.PHP/QA.md#常见数组函数) @@ -73,6 +124,7 @@ - [public、protected、private、final 区别](./docs/03.PHP/QA.md#publicprotectedprivatefinal-区别) - [客户端/服务端 IP 获取,了解代理透传 实际IP 的概念](./docs/03.PHP/QA.md#客户端服务端-ip-获取了解代理透传-实际ip-的概念) - [类的静态调用和实例化调用](./docs/03.PHP/QA.md#类的静态调用和实例化调用) +- [接口类和抽象类的区别](./docs/03.PHP/QA.md#接口类和抽象类的区别) - [PHP 不实例化调用方法](./docs/03.PHP/QA.md#php-不实例化调用方法) - [php.ini 配置选项](./docs/03.PHP/QA.md#phpini-配置选项) - [php-fpm.conf 配置](./docs/03.PHP/QA.md#php-fpmconf-配置) @@ -87,48 +139,15 @@ - [MVC 的理解](./docs/03.PHP/QA.md#mvc-的理解) - [主流 PHP 框架特点](./docs/03.PHP/QA.md#主流-php-框架特点) - [对象关系映射/ORM](./docs/03.PHP/QA.md#对象关系映射orm) +- [串行、并行、并发的区别](./docs/03.PHP/QA.md#串行、并行、并发的区别) +- [同步与异步的理解](./docs/03.PHP/QA.md#同步与异步的理解) +- [阻塞与非阻塞的理解](./docs/03.PHP/QA.md#阻塞与非阻塞的理解) +- [同步阻塞与非同步阻塞的理解](./docs/03.PHP/QA.md#同步阻塞与非同步阻塞的理解) +- [一条echo输出语句是如何执行的](./docs/php/一条echo输出语句是如何执行的.md) +- [FastCGI Process Manager](./docs/php/FastCGI-Process-Manager.md) +- [php支持哪些注释风格](./docs/php/php支持哪些注释风格.md) -### Web 篇 - -- [SEO 有哪些需要注意的](./docs/04.Web/QA.md#seo-有哪些需要注意的) -- [img 标签的 title 和 alt 有什么区别](./docs/04.Web/QA.md#img-标签的-title-和-alt-有什么区别) -- [CSS 选择器的分类](./docs/04.Web/QA.md#css-选择器的分类) -- [CSS sprite 是什么,有什么优缺点](./docs/04.Web/QA.md#css-sprite-是什么有什么优缺点) -- [display: none 与 visibility: hidden 的区别](./docs/04.Web/QA.md#display-none-与-visibility-hidden-的区别) -- [display: block 和 display: inline 的区别](./docs/04.Web/QA.md#display-block-和-display-inline-的区别) -- [CSS 文件、style 标签、行内 style 属性优先级](./docs/04.Web/QA.md#css-文件style-标签行内-style-属性优先级) -- [link 与 @import 的区别](./docs/04.Web/QA.md#link-与-import-的区别) -- [盒子模型](./docs/04.Web/QA.md#盒子模型) -- [容器包含若干浮动元素时如何清理(包含)浮动](./docs/04.Web/QA.md#容器包含若干浮动元素时如何清理包含浮动) -- [如何水平居中一个元素](./docs/04.Web/QA.md#如何水平居中一个元素) -- [如何竖直居中一个元素](./docs/04.Web/QA.md#如何竖直居中一个元素) -- [flex 与 CSS 盒子模型有什么区别](./docs/04.Web/QA.md#flex-与-css-盒子模型有什么区别) -- [Position 属性](./docs/04.Web/QA.md#position-属性) -- [PNG,GIF,JPG 的区别及如何选](./docs/04.Web/QA.md#pnggifjpg-的区别及如何选) -- [为什么把 JavaScript 文件放在 Html 底部](./docs/04.Web/QA.md#为什么把-javascript-文件放在-html-底部) -- [JavaScript 数据类型](./docs/04.Web/QA.md#javascript-数据类型) -- [JavaScript 操作 DOM 的方法有哪些](./docs/04.Web/QA.md#javascript-操作-dom-的方法有哪些) -- [JavaScript 字符串方法有哪些](./docs/04.Web/QA.md#javascript-字符串方法有哪些) -- [JavaScript 字符串截取方法有哪些?有什么区别](./docs/04.Web/QA.md#javascript-字符串截取方法有哪些有什么区别) -- [setTimeout 和 setInterval 的区别](./docs/04.Web/QA.md#settimeout-和-setinterval-的区别) -- [使用 new 操作符实例化一个对象的具体步骤](./docs/04.Web/QA.md#使用-new-操作符实例化一个对象的具体步骤) -- [如何实现 ajax 请求](./docs/04.Web/QA.md#如何实现-ajax-请求) -- [同源策略是什么](./docs/04.Web/QA.md#同源策略是什么) -- [如何解决跨域问题](./docs/04.Web/QA.md#如何解决跨域问题) -- [引起内存泄漏的操作有哪些](./docs/04.Web/QA.md#引起内存泄漏的操作有哪些) -- [闭包理解及应用](./docs/04.Web/QA.md#闭包理解及应用) -- [对 JavaScript 原型的理解](./docs/04.Web/QA.md#对-javascript-原型的理解) -- [对 JavaScript 模块化的理解](./docs/04.Web/QA.md#对-javascript-模块化的理解) -- [如何判断网页中图片加载成功或者失败](./docs/04.Web/QA.md#如何判断网页中图片加载成功或者失败) -- [如何实现懒加载](./docs/04.Web/QA.md#如何实现懒加载) -- [JSONP 原理](./docs/04.Web/QA.md#jsonp-原理) -- [Cookie 读写](./docs/04.Web/QA.md#cookie-读写) -- 从浏览器地址栏输入 URL 到显示页面的步骤 -- [Vue.js 双向绑定原理](./docs/04.Web/QA.md#vuejs-双向绑定原理) -- 如何进行网站性能优化 -- [渐进增强](./docs/04.Web/QA.md#渐进增强) - -### MySQL 篇 +### 2、存储篇 - [体系结构](./docs/05.MySQL/QA.md#体系结构) - [基础操作](./docs/05.MySQL/QA.md#基础操作) @@ -148,9 +167,34 @@ - [EXPLAIN 输出格式](./docs/05.MySQL/QA.md#explain-输出格式) - my.cnf 配置 - 慢查询 - -### Redis 篇 - +- [一条SQL查询语句是如何执行的](./docs/存储/一条SQL查询语句是如何执行的.md) +- [一条SQL更新语句是如何执行的](./docs/存储/一条SQL更新语句是如何执行的.md) +- [事务隔离:为什么你改了我还看不见?](./docs/存储/事务隔离-为什么你改了我还看不见.md) +- [深入浅出索引(上)](./docs/存储/深入浅出索引-上.md) +- [深入浅出索引(下)](./docs/存储/深入浅出索引-下.md) +- [全局锁和表锁:给表加个字段怎么有这么多阻碍](./docs/存储/全局锁和表锁-给表加个字段怎么有这么多阻碍.md) +- [行锁功过:怎么减少行锁对性能的影响](./docs/存储/行锁功过-怎么减少行锁对性能的影响.md) +- [事务到底是隔离的还是不隔离的](./docs/存储/事务到底是隔离的还是不隔离的.md) +- [普通索引和唯一索引,应该怎么选择](./docs/存储/普通索引和唯一索引,应该怎么选择.md) +- [MySQL为什么有时候会选错索引](./docs/存储/MySQL为什么有时候会选错索引.md) +- [怎么给字符串字段加索引](./docs/存储/怎么给字符串字段加索引.md) +- [为什么我的MySQL会抖一下](./docs/存储/为什么我的MySQL会抖一下.md) +- [为什么表数据删掉一半,表文件大小不变](./docs/存储/为什么表数据删掉一半,表文件大小不变.md) +- [count()这么慢,我该怎么办](./docs/存储/count()这么慢,我该怎么办.md) +- [order by是怎么工作的](./docs/存储/order-by是怎么工作的.md) +- [如何正确地显示随机消息](./docs/存储/如何正确地显示随机消息.md) +- [为什么这些SQL语句逻辑相同,性能却差异巨大](./docs/存储/为什么这些SQL语句逻辑相同,性能却差异巨大.md) +- [为什么我只查一行的语句,也执行这么慢](./docs/存储/为什么我只查一行的语句,也执行这么慢.md) +- [幻读是什么,幻读有什么问题](./docs/存储/幻读是什么,幻读有什么问题.md) +- [为什么我只改一行的语句,锁这么多](./docs/存储/为什么我只改一行的语句,锁这么多.md) +- [MySQL有哪些饮鸩止渴提高性能的方法](./docs/存储/MySQL有哪些饮鸩止渴提高性能的方法.md) +- [MySQL是怎么保证数据不丢的](./docs/存储/MySQL是怎么保证数据不丢的.md) +- [MySQL是怎么保证主备一致的](./docs/存储/MySQL是怎么保证主备一致的.md) +- [MySQL是怎么保证高可用的](./docs/存储/MySQL是怎么保证高可用的.md) +- [备库为什么会延迟好几个小时](./docs/存储/备库为什么会延迟好几个小时.md) +- [主库出问题了,从库怎么办](./docs/存储/主库出问题了,从库怎么办.md) +- [读写分离有哪些坑](./docs/存储/读写分离有哪些坑.md) +- [如何判断一个数据库是不是出问题了](./docs/存储/如何判断一个数据库是不是出问题了.md) - [Redis 介绍](./docs/06.Redis/QA.md#redis-介绍) - [Redis 特点](./docs/06.Redis/QA.md#redis-特点) - [Redis 支持哪些数据结构](./docs/06.Redis/QA.md#redis-支持哪些数据结构) @@ -167,31 +211,44 @@ - redis.conf 配置 - 慢查询 -### Linux 篇 +### 3、网络篇 -- [Linux 目录结构](./docs/07.Linux/QA.md#linux-目录结构) -- [Linux 基础](./docs/07.Linux/QA.md#linux-基础) -- [命令与文件查找](./docs/07.Linux/QA.md#命令与文件查找) -- [数据流重定向](./docs/07.Linux/QA.md#数据流重定向) -- [sed](./docs/07.Linux/QA.md#sed) -- [awk](./docs/07.Linux/QA.md#awk) -- [计划任务](./docs/07.Linux/QA.md#计划任务) -- [Vim](./docs/07.Linux/QA.md#vim) -- [负载查看](./docs/07.Linux/QA.md#负载查看) -- Linux 内存管理 -- [进程、线程、协程区别](./docs/07.Linux/QA.md#进程线程协程区别) -- 进程间通信与信号机制 +- [计算机网络体系结构](./docs/01.网络.md#1-计算机网络体系结构) +- [UDP 的主要特点](./docs/01.网络.md#2-udp-的主要特点) +- [TCP 的主要特点](./docs/01.网络.md#3-tcp-的主要特点) +- [简述三报文握手建立 TCP 连接](./docs/01.网络.md#4-简述三报文握手建立-tcp-连接) +- [建立 TCP 连接为什么最后还要发送确认](./docs/01.网络.md#5-建立-tcp-连接为什么最后还要发送确认) +- [简述 TCP 连接的释放](./docs/01.网络.md#6-简述-tcp-连接的释放) +- [TIME-WAIT 是什么,为什么必须等待 2MLS](./docs/01.网络.md#7-time-wait-是什么为什么必须等待-2mls) +- [TCP 粘包问题](./docs/01.网络.md#8-tcp-粘包问题) +- [UDP、TCP 区别,适用场景](./docs/01.网络.md#9-udptcp-区别适用场景) +- [建立 socket 需要哪些步骤](./docs/01.网络.md#10-建立-socket-需要哪些步骤) +- [DNS 主要作用是什么](./docs/01.网络.md#11-dns-主要作用是什么) +- [HTTP 报文组成](./docs/01.网络.md#12-http-报文组成) +- [HTTP 状态码](./docs/01.网络.md#13-http-状态码) +- [常见的 HTTP 方法](./docs/01.网络.md#14-常见的-http-方法) +- [GET 与 POST 请求方式区别](./docs/01.网络.md#15-get-与-post-请求方式区别) +- [HTTP 优缺点](./docs/01.网络.md#16-http-优缺点) +- [HTTPS 通信原理](./docs/01.网络.md#17-https-通信原理) +- [HTTP 2.0](./docs/01.网络.md#18-http-20) +- [WebSocket](./docs/01.网络.md#19-websocket) +- [IPv6 与 IPv4 有什么变化](./docs/01.网络.md#20-ipv6-与-ipv4-有什么变化) +- [什么是心跳机制](./docs/01.网络.md#21-什么是心跳机制) +- [什么是长连接](./docs/01.网络.md#22-什么是长连接) -### 安全篇 +### 4、数据结构与算法篇 -- [跨站脚本攻击(XSS)](./docs/08.安全/QA.md#跨站脚本攻击xss) -- [跨站点请求伪造(CSRF)](./docs/08.安全/QA.md#跨站点请求伪造csrf) -- [SQL 注入](./docs/08.安全/QA.md#sql-注入) -- [应用层拒绝服务攻击](./docs/08.安全/QA.md#应用层拒绝服务攻击) -- [PHP 安全](./docs/08.安全/QA.md#php-安全) -- [伪随机数和真随机数](./docs/08.安全/QA.md#伪随机数和真随机数) +- [概述](./docs/02.数据结构与算法.md#1-概述) +- [实现基础](./docs/02.数据结构与算法.md#2-实现基础) +- [线性结构](./docs/02.数据结构与算法.md#3-线性结构) +- [树](./docs/02.数据结构与算法.md#4-树) +- [散列查找](./docs/02.数据结构与算法.md#5-散列查找) +- [图](./docs/02.数据结构与算法.md#6-图) +- [排序](./docs/02.数据结构与算法.md#7-排序) +- [补充](./docs/02.数据结构与算法.md#8-补充) +- [经典算法题](./docs/02.数据结构与算法.md#9-经典算法题) -### 设计模式篇 +### 5、设计模式篇 - [什么是设计模式](./docs/09.设计模式/QA.md#什么是设计模式) - [如何理解框架](./docs/09.设计模式/QA.md#如何理解框架) @@ -208,7 +265,22 @@ - [控制反转](./docs/09.设计模式/QA.md#控制反转) - [依赖注入](./docs/09.设计模式/QA.md#依赖注入) -### 架构篇 +### 6、服务器篇 + +- [Linux 目录结构](./docs/07.Linux/QA.md#linux-目录结构) +- [Linux 基础](./docs/07.Linux/QA.md#linux-基础) +- [命令与文件查找](./docs/07.Linux/QA.md#命令与文件查找) +- [数据流重定向](./docs/07.Linux/QA.md#数据流重定向) +- [sed](./docs/07.Linux/QA.md#sed) +- [awk](./docs/07.Linux/QA.md#awk) +- [计划任务](./docs/07.Linux/QA.md#计划任务) +- [Vim](./docs/07.Linux/QA.md#vim) +- [负载查看](./docs/07.Linux/QA.md#负载查看) +- Linux 内存管理 +- [进程、线程、协程区别](./docs/07.Linux/QA.md#进程线程协程区别) +- 进程间通信与信号机制 + +### 7、架构篇 - [OAuth 2.0](./docs/10.架构/QA.md#oauth-20) - [单点登录](./docs/10.架构/QA.md#单点登录) @@ -233,6 +305,58 @@ - 服务降级 - 语言对比 +### 8、安全篇 + +- [跨站脚本攻击(XSS)](./docs/08.安全/QA.md#跨站脚本攻击xss) +- [跨站点请求伪造(CSRF)](./docs/08.安全/QA.md#跨站点请求伪造csrf) +- [SQL 注入](./docs/08.安全/QA.md#sql-注入) +- [应用层拒绝服务攻击](./docs/08.安全/QA.md#应用层拒绝服务攻击) +- [PHP 安全](./docs/08.安全/QA.md#php-安全) +- [伪随机数和真随机数](./docs/08.安全/QA.md#伪随机数和真随机数) + +### 9、面试篇 + +### 10、web篇 + + +- [SEO 有哪些需要注意的](./docs/04.Web/QA.md#seo-有哪些需要注意的) +- [img 标签的 title 和 alt 有什么区别](./docs/04.Web/QA.md#img-标签的-title-和-alt-有什么区别) +- [CSS 选择器的分类](./docs/04.Web/QA.md#css-选择器的分类) +- [CSS sprite 是什么,有什么优缺点](./docs/04.Web/QA.md#css-sprite-是什么有什么优缺点) +- [display: none 与 visibility: hidden 的区别](./docs/04.Web/QA.md#display-none-与-visibility-hidden-的区别) +- [display: block 和 display: inline 的区别](./docs/04.Web/QA.md#display-block-和-display-inline-的区别) +- [CSS 文件、style 标签、行内 style 属性优先级](./docs/04.Web/QA.md#css-文件style-标签行内-style-属性优先级) +- [link 与 @import 的区别](./docs/04.Web/QA.md#link-与-import-的区别) +- [盒子模型](./docs/04.Web/QA.md#盒子模型) +- [容器包含若干浮动元素时如何清理(包含)浮动](./docs/04.Web/QA.md#容器包含若干浮动元素时如何清理包含浮动) +- [如何水平居中一个元素](./docs/04.Web/QA.md#如何水平居中一个元素) +- [如何竖直居中一个元素](./docs/04.Web/QA.md#如何竖直居中一个元素) +- [flex 与 CSS 盒子模型有什么区别](./docs/04.Web/QA.md#flex-与-css-盒子模型有什么区别) +- [Position 属性](./docs/04.Web/QA.md#position-属性) +- [PNG,GIF,JPG 的区别及如何选](./docs/04.Web/QA.md#pnggifjpg-的区别及如何选) +- [为什么把 JavaScript 文件放在 Html 底部](./docs/04.Web/QA.md#为什么把-javascript-文件放在-html-底部) +- [JavaScript 数据类型](./docs/04.Web/QA.md#javascript-数据类型) +- [JavaScript 操作 DOM 的方法有哪些](./docs/04.Web/QA.md#javascript-操作-dom-的方法有哪些) +- [JavaScript 字符串方法有哪些](./docs/04.Web/QA.md#javascript-字符串方法有哪些) +- [JavaScript 字符串截取方法有哪些?有什么区别](./docs/04.Web/QA.md#javascript-字符串截取方法有哪些有什么区别) +- [setTimeout 和 setInterval 的区别](./docs/04.Web/QA.md#settimeout-和-setinterval-的区别) +- [使用 new 操作符实例化一个对象的具体步骤](./docs/04.Web/QA.md#使用-new-操作符实例化一个对象的具体步骤) +- [如何实现 ajax 请求](./docs/04.Web/QA.md#如何实现-ajax-请求) +- [同源策略是什么](./docs/04.Web/QA.md#同源策略是什么) +- [如何解决跨域问题](./docs/04.Web/QA.md#如何解决跨域问题) +- [引起内存泄漏的操作有哪些](./docs/04.Web/QA.md#引起内存泄漏的操作有哪些) +- [闭包理解及应用](./docs/04.Web/QA.md#闭包理解及应用) +- [对 JavaScript 原型的理解](./docs/04.Web/QA.md#对-javascript-原型的理解) +- [对 JavaScript 模块化的理解](./docs/04.Web/QA.md#对-javascript-模块化的理解) +- [如何判断网页中图片加载成功或者失败](./docs/04.Web/QA.md#如何判断网页中图片加载成功或者失败) +- [如何实现懒加载](./docs/04.Web/QA.md#如何实现懒加载) +- [JSONP 原理](./docs/04.Web/QA.md#jsonp-原理) +- [Cookie 读写](./docs/04.Web/QA.md#cookie-读写) +- 从浏览器地址栏输入 URL 到显示页面的步骤 +- [Vue.js 双向绑定原理](./docs/04.Web/QA.md#vuejs-双向绑定原理) +- 如何进行网站性能优化 +- [渐进增强](./docs/04.Web/QA.md#渐进增强) + ## 为何要写这个 从事软件开发,已经接近五个年头了,去年面试中,发现自己依然处于尴尬的位置。简单重复,缺乏挑战的工作,已经没有多大吸引力了,优秀的平台,面试缺屡次碰壁。人上年纪之后,思维敏感度、记忆力都明显有所下滑。 diff --git "a/docs/01.\347\275\221\347\273\234.md" "b/docs/01.\347\275\221\347\273\234.md" index b626a98..1240a51 100644 --- "a/docs/01.\347\275\221\347\273\234.md" +++ "b/docs/01.\347\275\221\347\273\234.md" @@ -39,17 +39,18 @@ ### 4. 简述三报文握手建立 TCP 连接 -- 服务器进程先创建传输控制块 TCB,并处于监听状态,等待客户端的连接请求 -- 客户端创建传输控制块 TCB,并向服务器发出连接请求报文段 -- 服务器收到连接请求报文段后,如同意建立连接,则发送确认报文段 -- 客户端进程收到服务器的确认报文段后,立即回复确认报文段,并进入已建立连接状态 -- 服务器收到确认报文段之后,也进入已建立连接状态 +- 服务器进程先创建传输控制块 TCB,并处于监听状态,等待客户端的连接请求;此时状态为LISTEN +- 客户端创建传输控制块 TCB,并向服务器发出连接请求报文段SYN,此时状态为SYN-SEND +- 服务器收到连接请求报文段后,如同意建立连接,则发送确认报文段ACK,此时状态为SYN-RECV,并且客户端链接进入半链接队列,系统参数/proc/sys/net/ipv4/tcp_max_syn_backlog +- 客户端进程收到服务器的确认报文段后,立即回复确认报文段ACK,并进入已建立连接状态,此时状态为ESTABLISHED +- 服务器收到确认报文段之后,也进入已建立连接状态,此时状态为ESTABLISHED,并且客户端链接进入全连接队列,系统参数/proc/sys/net/core/somaxconn > 传输控制块 TCB(Transmission Control Block)存储了每一个连接中的一些重要信息 ### 5. 建立 TCP 连接为什么最后还要发送确认 这主要是为了防止已失效的连接请求报文段突然又传到了 TCP 服务器,避免产生错误 +简述为了保证数据的正确性和顺序 ### 6. 简述 TCP 连接的释放 @@ -100,9 +101,9 @@ TIME-WAIT 是一种 TCP 状态。等待 2MLS 可以保证客户端最后一个 #### TCP 适用场景 -文件传输(FTP HTTP 对数据准确性要求较高,速度可以相对慢) -发送或接收邮件(POP IMAP SMTP 对数据准确性要求高,非紧急应用) -远程登录(telnet SSH 对数据准确性有要求,有连接的概念) +- 文件传输(FTP HTTP 对数据准确性要求较高,速度可以相对慢) +- 发送或接收邮件(POP IMAP SMTP 对数据准确性要求高,非紧急应用) +- 远程登录(telnet SSH 对数据准确性有要求,有连接的概念) ### 10. 建立 socket 需要哪些步骤 @@ -182,10 +183,11 @@ HTTP 报文组成部分 |GET|POST| |-|-| |后退按钮/刷新无害|数据会被重新提交| -|数据长度限制/URL长度2048字符|长度无限制| +|数据长度限制/URL长度2048字符|长度无限制(协议不限制;实际上受nginx和PHP的限制,php还会限制post报文变量的个数)| |数据可见/安全性差|不可见/更安全| |可以被缓存|不可以被缓存| |书签可收藏|书签不可收藏| +|产生一个TCP数据包| 产生两个TCP数据包 | ### 16. HTTP 优缺点 @@ -224,4 +226,4 @@ WebSocket 是一种通信协议,定义了一个全双工通信信道,仅通 ### 22. 什么是长连接 -长连接,指在一个连接上可以连续发送多个数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包 \ No newline at end of file +长连接,指在一个连接上可以连续发送多个数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包 diff --git "a/docs/03.PHP/03.echo\343\200\201print\343\200\201print_r\343\200\201var_dump\345\214\272\345\210\253.md" "b/docs/03.PHP/03.echo\343\200\201print\343\200\201print_r\343\200\201var_dump\345\214\272\345\210\253.md" deleted file mode 100644 index f5bd059..0000000 --- "a/docs/03.PHP/03.echo\343\200\201print\343\200\201print_r\343\200\201var_dump\345\214\272\345\210\253.md" +++ /dev/null @@ -1,51 +0,0 @@ -# echo、print、print_r、var_dump 区别 - -## echo - -- 输出单个或多个字符,多个使用逗号分隔 -- 无返回值 - -```php -echo "String 1", "String 2"; -``` - -## print - -- 只可以输出单个字符 -- 返回`1`,因此可用于表达式 - -```php -print "Hello"; -if ($expr && print "foo") {} -``` - -## print_r - -- 输出关于变量的易于理解的信息 -- 支持多种数据类型,包括字符、数组、对象,格式化成易读格式 -- 在调试时非常有用 -- 若设置第二个参数,可将输出值返回(而不直接输出) - -```php -$b = [ - 'm' => 'monkey', - 'foo' => 'bar', - 'x' => ['x', 'y', 'z'], -]; -$results = print_r($b, true); //$results 包含了 print_r 的输出 -``` - -## var_dump - -- 输出关于变量的易于理解的信息,多个可用分号分隔 -- 支持多种数据类型,包括字符、数组、对象,格式化成易读格式 -- 输出格式与`print_r`不同,`var_dump`的输出包含`数据类型` -- 在调试时非常有用 -- 无返回值 - -## 注意 - -- 即使`print`可用于表达式,但这种用法,常常不利于代码可读性,与其他操作符混用容易让人误解 -- `echo`和`print`都是语言结构,`print_r`和`var_dump`是普通函数。`echo`或`print`使用时,不需要使用括号将变量括起来 - -**《echo、print、print_r、var_dump区别》 原文链接:[https://blog.maplemark.cn/2019/04/echo-print-print_r-var_dump区别.html](https://blog.maplemark.cn/2019/04/echo-print-print_r-var_dump区别.html)** \ No newline at end of file diff --git a/docs/03.PHP/QA.md b/docs/03.PHP/QA.md index 570f9d5..780114a 100644 --- a/docs/03.PHP/QA.md +++ b/docs/03.PHP/QA.md @@ -1,541 +1,580 @@ -# 问题与简答 - -## PHP 篇 - -### echo、print、print_r、var_dump 区别 - -> `echo`和`print`是语言结构、`print_r`和`var_dump`是普通函数 - -- echo:输出一个或多个字符串 - -- print:输出字符串 - -- print_r:打印关于变量的易于理解的信息 - -- var_dump:打印关于变量的易于理解的信息(带类型) - -拓展阅读 [《echo、print、print_r、var_dump区别》](./03.echo、print、print_r、var_dump区别.md) - -### 单引号和双引号的区别 - -双引号可以被分析器解析,单引号则不行 - -### isset 和 empty 的区别 - -isset:检测变量是否已设置并且非 NULL - -empty:判断变量是否为空,变量为 0/false 也会被认为是空;变量不存在,不会产生警告 - -### static、self、$this 的区别 - -static:static 可以用于静态或非静态方法中,也可以访问类的静态属性、静态方法、常量和非静态方法,但不能访问非静态属性 - -self:可以用于访问类的静态属性、静态方法和常量,但 self 指向的是当前定义所在的类,这是 self 的限制 - -$this:指向的是实际调用时的对象,也就是说,实际运行过程中,谁调用了类的属性或方法,$this 指向的就是哪个对象。但 $this 不能访问类的静态属性和常量,且 $this 不能存在于静态方法中 - -### include、require、include_once、require_once 的区别 - -require 和 include 几乎完全一样,除了处理失败的方式不同之外。require 在出错时产生 E_COMPILE_ERROR 级别的错误。换句话说将导致脚本中止而 include 只产生警告(E_WARNING),脚本会继续运行 - -include_once 语句在脚本执行期间包含并运行指定文件。此行为和 include 语句类似,唯一区别是如果该文件中已经被包含过,则不会再次包含。如同此语句名字暗示的那样,只会包含一次 - -### 常见数组函数 - -array_count_values — 统计数组中所有的值 - -array_flip — 交换数组中的键和值 - -array_merge — 合并一个或多个数组 - -array_multisort — 对多个数组或多维数组进行排序 - -array_pad — 以指定长度将一个值填充进数组 - -array_pop — 弹出数组最后一个单元(出栈) - -array_push — 将一个或多个单元压入数组的末尾(入栈) - -array_rand — 从数组中随机(伪随机)取出一个或多个单元 - -array_keys — 返回数组中部分的或所有的键名 - -array_values — 返回数组中所有的值 - -count — 计算数组中的单元数目,或对象中的属性个数 - -sort — 对数组排序 - -### Cookie 和 Session - -Cookie:PHP 透明的支持 HTTP cookie 。cookie 是一种远程浏览器端存储数据并以此来跟踪和识别用户的机制 - -Session:会话机制(Session)在 PHP 中用于保持用户连续访问Web应用时的相关数据 - -### 预定义变量 - -对于全部脚本而言,PHP 提供了大量的预定义变量 - -超全局变量 — 超全局变量是在全部作用域中始终可用的内置变量 - -```text -$GLOBALS — 引用全局作用域中可用的全部变量 -$_SERVER — 服务器和执行环境信息 -$_GET — HTTP GET 变量 -$_POST — HTTP POST 变量 -$_FILES — HTTP 文件上传变量 -$_REQUEST — HTTP Request 变量 -$_SESSION — Session 变量 -$_ENV — 环境变量 -$_COOKIE — HTTP Cookies -$php_errormsg — 前一个错误信息 -$HTTP_RAW_POST_DATA — 原生POST数据 -$http_response_header — HTTP 响应头 -$argc — 传递给脚本的参数数目 -$argv — 传递给脚本的参数数组 -``` - -- 超全局变量 - -PHP 中的许多预定义变量都是“超全局的”,这意味着它们在一个脚本的全部作用域中都可用。在函数或方法中无需执行 global $variable; 就可以访问它们 - -超全局变量:$GLOBALS、$\_SERVER、$\_GET、$\_POST、$\_FILES、$\_COOKIE、$\_SESSION、$\_REQUEST、$\_ENV - -### 传值和传引用的区别 - -传值导致对象生成了一个拷贝,传引用则可以用两个变量指向同一个内容 - -### 构造函数和析构函数 - -构造函数:PHP 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作 - -析构函数:PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行 - -### 魔术方法 - -\_\_construct(), \_\_destruct(), \_\_call(), \_\_callStatic(), \_\_get(), \_\_set(), \_\_isset(), \_\_unset(), \_\_sleep(), \_\_wakeup(), \_\_toString(), \_\_invoke() 等方法在 PHP 中被称为"魔术方法"(Magic methods) - -### public、protected、private、final 区别 - -对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。被定义为公有的类成员可以在任何地方被访问 - -PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承 - -### 客户端/服务端 IP 获取,了解代理透传 实际IP 的概念 - -客户端IP: $\_SERVER['REMOTE_ADDR'] - -服务端IP: $\_SERVER['SERVER_ADDR'] - -客户端IP(代理透传): $\_SERVER['HTTP_X_FORWARDED_FOR'] - -### 类的静态调用和实例化调用 - -- 占用内存 - -静态方法在内存中只有一份,无论调用多少次,都是共用的 - -实例化不一样,每一个实例化是一个对象,在内存中是多个的 - -- 不同点 - -静态调用不需要实例化即可调用 - -静态方法不能调用非静态属性,因为非静态属性需要实例化后,存放在对象里 - -静态方法可以调用非静态方法,使用 self 关键字。php 里,一个方法被 `self::` 后,自动转变为静态方法 - -调用类的静态函数时不会自动调用类的构造函数 - -### PHP 不实例化调用方法 - -静态调用、使用 PHP 反射方式 - -### php.ini 配置选项 - -- 配置选项 - -|名字|默认|备注| -|-|-|-| -|short_open_tag|"1"|是否开启缩写形式(``)| -|precision|"14"|浮点数中显示有效数字的位数| -|disable_functions|""|禁止某些函数| -|disable_classes|""|禁用某些类| -|expose_php|""|是否暴露 PHP 被安装在服务器上| -|max_execution_time|30|最大执行时间| -|memory_limit|128M|每个脚本执行的内存限制| -|error_reporting|NULL|设置错误报告的级别 `E_ALL` & ~`E_NOTICE` & ~`E_STRICT` & ~`E_DEPRECATED`| -|display_errors|"1"|显示错误| -|log_errors|"0"|设置是否将错误日志记录到 error_log 中| -|error_log|NULL|设置脚本错误将被记录到的文件| -|upload_max_filesize|"2M"|最大上传文件大小| -|post_max_size|"8M"|设置POST最大数据限制| - -```shell -php -ini | grep short_open_tag //查看 php.ini 配置 -``` - -- 动态设置 - -```php -ini_set(string $varname , string $newvalue); - -ini_set('date.timezone', 'Asia/Shanghai'); //设置时区 -ini_set('display_errors', '1'); //设置显示错误 -ini_set('memory_limit', '256M'); //设置最大内存限制 -``` - -### php-fpm.conf 配置 - -|名称|默认|备注| -|-|-|-| -|pid||PID文件的位置| -|error_log||错误日志的位置| -|log_level|notice|错误级别 alert:必须立即处理、error:错误情况、warning:警告情况、notice:一般重要信息、debug:调试信息| -|daemonize|yes|设置 FPM 在后台运行| -|listen|ip:port、port、/path/to/unix/socket|设置接受 FastCGI 请求的地址| -|pm|static、ondemand、dynamic|设置进程管理器如何管理子进程| -|request_slowlog_timeout|'0'|慢日志记录阀值| -|slowlog||慢请求的记录日志| - -### 502、504 错误产生原因及解决方式 - -#### 502 - -502 表示网关错误,当 PHP-CGI 得到一个无效响应,网关就会输出这个错误 - -- `php.ini` 的 memory_limit 过小 -- `php-fpm.conf` 中 max_children、max_requests 设置不合理 -- `php-fpm.conf` 中 request_terminate_timeout、max_execution_time 设置不合理 -- php-fpm 进程处理不过来,进程数不足、脚本存在性能问题 - -#### 504 - -504 表示网关超时,PHP-CGI 没有在指定时间响应请求,网关将输出这个错误 - -- Nginx+PHP 架构,可以调整 FastCGI 超时时间,fastcgi_connect_timeout、fastcgi_send_timeout、fastcgi_read_timeout - -#### 500 - -php 代码问题,文件权限问题,资源问题 - -#### 503 - -超载或者停机维护 - -### 如何返回一个301重定向 - -```php -header('HTTP/1.1 301 Moved Permanently'); -header('Location: https://blog.maplemark.cn'); -``` - -### PHP 与 MySQL 连接方式 - -#### MySQL - -```php -$conn = mysql_connect('127.0.0.1:3306', 'root', '123456'); -if (!$conn) { - die(mysql_error() . "\n"); -} -mysql_query("SET NAMES 'utf8'"); -$select_db = mysql_select_db('app'); -if (!$select_db) { - die(mysql_error() . "\n"); -} -$sql = "SELECT * FROM `user` LIMIT 1"; -$res = mysql_query($sql); -if (!$res) { - die(mysql_error() . "\n"); -} -while ($row = mysql_fetch_assoc($res)) { - var_dump($row); -} -mysql_close($conn); -``` - -#### MySQLi - -```php -$conn = @new mysqli('127.0.0.1:3306', 'root', '123456'); -if ($conn->connect_errno) { - die($conn->connect_error . "\n"); -} -$conn->query("set names 'utf8';"); -$select_db = $conn->select_db('user'); -if (!$select_db) { - die($conn->error . "\n"); -} -$sql = "SELECT * FROM `user` LIMIT 1"; -$res = $conn->query($sql); -if (!$res) { - die($conn->error . "\n"); -} -while ($row = $res->fetch_assoc()) { - var_dump($row); -} -$res->free(); -$conn->close(); -``` - -#### PDO - -```php -$pdo = new PDO('mysql:host=127.0.0.1:3306;dbname=user', 'root', '123456'); -$pdo->exec("set names 'utf8'"); -$sql = "SELECT * FROM `user` LIMIT 1"; -$stmt = $pdo->prepare($sql); -$stmt->bindValue(1, 1, PDO::PARAM_STR); -$rs = $stmt->execute(); -if ($rs) { - while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - var_dump($row); - } -} -$pdo = null; -``` - -### MySQL、MySQLi、PDO 区别 - -#### MySQL - -- 允许 PHP 应用与 MySQL 数据库交互的早期扩展 -- 提供了一个面向过程的接口,不支持后期的一些特性 - -#### MySQLi - -- 面向对象接口 -- prepared 语句支持 -- 多语句执行支持 -- 事务支持 -- 增强的调试能力 - -#### PDO - -- PHP 应用中的一个数据库抽象层规范 -- PDO 提供一个统一的 API 接口,无须关心数据库类型 -- 使用标准的 PDO API,可以快速无缝切换数据库 - -### 数据库持久连接 - -把 PHP 用作多进程 web 服务器的一个模块,这种方法目前只适用于 Apache。 - -对于一个多进程的服务器,其典型特征是有一个父进程和一组子进程协调运行,其中实际生成 web 页面的是子进程。每当客户端向父进程提出请求时,该请求会被传递给还没有被其它的客户端请求占用的子进程。这也就是说当相同的客户端第二次向服务端提出请求时,它将有可能被一个不同的子进程来处理。在开启了一个持久连接后,所有请求 SQL 服务的后继页面都能够重用这个已经建立的 SQL Server 连接。 - -### 代码执行过程 - -PHP 代码 => 启动 php 及 zend 引擎,加载注册拓展模块 => 对代码进行词法/语法分析 => 编译成opcode(opcache) => 执行 opcode - -> PHP7 新增了抽象语法树(AST),在语法分析阶段生成 AST,然后再生成 opcode 数组 - -### base64 编码原理 - -![base64](./assets/php-base64.png) - -### ip2long 实现 - -![ip2long](./assets/php-ip2long.png) - - -``` -124.205.30.150=2093817494 - -list($p1,$p2,$p3,$p4) = explode(',','124.205.30.150'); - -$realNum = $p1<<24+$p2<<16+$p3<<8+$p4; -``` - -### MVC 的理解 - -MVC 包括三类对象。模型 Model 是应用对象,视图 View 是它在屏幕上的表示,控制器 Controller 定义用户界面对用户输入的响应方式。不使用 MVC,用户界面设计往往将这些对象混在一起,而 MVC 则将它们分离以提高灵活性和复用性 - -### 主流 PHP 框架特点 - -#### Laravel - -易于访问,功能强大,并提供大型,强大的应用程序所需的工具 - -- 简单快速的路由引擎 -- 强大的依赖注入容器 -- 富有表现力,直观的数据库 ORM -- 提供数据库迁移功能 -- 灵活的任务调度器 -- 实时事件广播 - -#### Symfony - -- Database engine-independent -- Simple to use, in most cases, but still flexible enough to adapt to complex cases -- Based on the premise of convention over configuration--the developer needs to configure only the unconventional -- Compliant with most web best practices and design patterns -- Enterprise-ready--adaptable to existing information technology (IT) policies and architectures, and stable enough for long-term projects -- Very readable code, with phpDocumentor comments, for easy maintenance -- Easy to extend, allowing for integration with other vendor libraries - -#### CodeIgniter - -- 基于模型-视图-控制器的系统 -- 框架比较轻量 -- 全功能数据库类,支持多个平台 -- Query Builder 数据库支持 -- 表单和数据验证 -- 安全性和 XSS 过滤 -- 全页面缓存 - -#### ThinkPHP - -- 采用容器统一管理对象 -- 支持 Facade -- 更易用的路由 -- 注解路由支持 -- 路由跨域请求支持 -- 验证类增强 -- 配置和路由目录独立 -- 取消系统常量 -- 类库别名机制 -- 模型和数据库增强 -- 依赖注入完善 -- 支持 PSR-3 日志规范 -- 中间件支持 -- 支持 Swoole/Workerman 运行 - -### 对象关系映射/ORM - -#### 优点 - -- 缩短编码时间、减少甚至免除对 model 的编码,降低数据库学习成本 -- 动态的数据表映射,在表结构发生改变时,减少代码修改 -- 可以很方便的引入附加功能(cache 层) - -#### 缺点 - -- 映射消耗性能、ORM 对象消耗内存 -- SQL 语句较为复杂时,ORM 语法可读性不高(使用原生 SQL) - -### 链式调用实现 - -类定义一个内置变量,让类中其他定义方法可访问到 - -### 异常处理 - -set_exception_handler — 设置用户自定义的异常处理函数 - -使用 try / catch 捕获 - -### 如何实现异步调用 - -```php -$fp = fsockopen("blog.maplemark.cn", 80, $errno, $errstr, 30); -if (!$fp) { - echo "$errstr ($errno)
\n"; -} else { - $out = "GET /backend.php / HTTP/1.1\r\n"; - $out .= "Host: blog.maplemark.cn\r\n"; - $out .= "Connection: Close\r\n\r\n"; - fwrite($fp, $out); - /*忽略执行结果 - while (!feof($fp)) { - echo fgets($fp, 128); - }*/ - fclose($fp); -} -``` - -### 多进程同时写一个文件 - -加锁、队列 - -### PHP 进程模型,进程通讯方式,进程线程区别 - -消息队列、socket、信号量、共享内存、信号、管道 - -### PHP 支持回调的函数,实现一个 - -array_map、array_filter、array_walk、usort - -is_callable + callbacks + 匿名函数实现 - -### 发起 HTTP 请求有哪几种方式,它们有何区别 - -cURL、file_get_contents、fopen、fsockopen - -### php for while foreach 迭代数组时候,哪个效率最高 - -### 弱类型变量如何实现 - -PHP 中声明的变量,在 zend 引擎中都是用结构体 zval 来保存,通过共同体实现弱类型变量声明 - -### PHP 拓展初始化 - -- 初始化拓展 - -```shell -$ php /php-src/ext/ext_skel.php --ext -``` - -- 定义拓展函数 - -zend_module_entry 定义 Extension name 编写 PHP_FUNCTION 函数 - -- 编译安装 - -```shell -$ phpize $ ./configure $ make && make install -``` - -### 如何获取扩展安装路径 - -### 垃圾回收机制 - -引用计数器 - -### Trait - -自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait - -### yield 是什么,说个使用场景 yield、yield 核心原理是什么 - -一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以yield生成许多它所需要的值 - -### traits 与 interfaces 区别 及 traits 解决了什么痛点 - -### 如何 foreach 迭代对象、如何数组化操作对象 $obj[key]、如何函数化对象 $obj(123); - -### Swoole 适用场景,协程实现方式 - -那你知道swoole的进程模型 - -### PHP 数组底层实现 (HashTable + Linked list) - -### Copy on write 原理,何时 GC - -### 如何解决 PHP 内存溢出问题 - -### ZVAL - -### HashTable - -### PHP7 新特性 - -标量类型声明、返回值类型声明、通过 define() 定义常量数组、匿名类、相同命名空间类一次性导入 - -### PHP7 底层优化 - -ZVAL 结构体优化,占用由24字节降低为16字节 - -内部类型 zend_string,结构体成员变量采用 char 数组,不是用 char* - -PHP 数组实现由 hashtable 变为 zend array - -函数调用机制,改进函数调用机制,通过优化参数传递环节,减少了一些指令 - -### PSR 介绍,PSR-1, 2, 4, 7 - -### Xhprof 、Xdebug 性能调试工具使用 - -### 字符串、数字比较大小的原理,注意 0 开头的8进制、0x 开头16进制 - -### BOM 头是什么,怎么除去 - -### 模板引擎是什么,解决什么问题、实现原理(Smarty、Twig、Blade) \ No newline at end of file +# 问题与简答 + +## PHP 篇 + +### static、self、$this 的区别 + +static:static 可以用于静态或非静态方法中,也可以访问类的静态属性、静态方法、常量和非静态方法,但不能访问非静态属性 + +self:可以用于访问类的静态属性、静态方法和常量,但 self 指向的是当前定义所在的类,这是 self 的限制 + +$this:指向的是实际调用时的对象,也就是说,实际运行过程中,谁调用了类的属性或方法,$this 指向的就是哪个对象。但 $this 不能访问类的静态属性和常量,且 $this 不能存在于静态方法中 + +### include、require、include_once、require_once 的区别 + +require 和 include 几乎完全一样,除了处理失败的方式不同之外。require 在出错时产生 E_COMPILE_ERROR 级别的错误。换句话说将导致脚本中止而 include 只产生警告(E_WARNING),脚本会继续运行 + +include_once 语句在脚本执行期间包含并运行指定文件。此行为和 include 语句类似,唯一区别是如果该文件中已经被包含过,则不会再次包含。如同此语句名字暗示的那样,只会包含一次 + +### 常见数组函数 + +array_count_values — 统计数组中所有的值 + +array_flip — 交换数组中的键和值 + +array_merge — 合并一个或多个数组 + +array_multisort — 对多个数组或多维数组进行排序 + +array_pad — 以指定长度将一个值填充进数组 + +array_pop — 弹出数组最后一个单元(出栈) + +array_push — 将一个或多个单元压入数组的末尾(入栈) + +array_rand — 从数组中随机(伪随机)取出一个或多个单元 + +array_keys — 返回数组中部分的或所有的键名 + +array_values — 返回数组中所有的值 + +count — 计算数组中的单元数目,或对象中的属性个数 + +sort — 对数组排序 + +### Cookie 和 Session + +Cookie:PHP 透明的支持 HTTP cookie 。cookie 是一种远程浏览器端存储数据并以此来跟踪和识别用户的机制 + +Session:会话机制(Session)在 PHP 中用于保持用户连续访问Web应用时的相关数据 + +### 预定义变量 + +对于全部脚本而言,PHP 提供了大量的预定义变量 + +超全局变量 — 超全局变量是在全部作用域中始终可用的内置变量 + +```text +$GLOBALS — 引用全局作用域中可用的全部变量 +$_SERVER — 服务器和执行环境信息 +$_GET — HTTP GET 变量 +$_POST — HTTP POST 变量 +$_FILES — HTTP 文件上传变量 +$_REQUEST — HTTP Request 变量 +$_SESSION — Session 变量 +$_ENV — 环境变量 +$_COOKIE — HTTP Cookies +$php_errormsg — 前一个错误信息 +$HTTP_RAW_POST_DATA — 原生POST数据 +$http_response_header — HTTP 响应头 +$argc — 传递给脚本的参数数目 +$argv — 传递给脚本的参数数组 +``` + +- 超全局变量 + +PHP 中的许多预定义变量都是“超全局的”,这意味着它们在一个脚本的全部作用域中都可用。在函数或方法中无需执行 global $variable; 就可以访问它们 + +超全局变量:$GLOBALS、$\_SERVER、$\_GET、$\_POST、$\_FILES、$\_COOKIE、$\_SESSION、$\_REQUEST、$\_ENV + +### 传值和传引用的区别 + +传值导致对象生成了一个拷贝,传引用则可以用两个变量指向同一个内容 + +### 构造函数和析构函数 + +构造函数:PHP 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作 + +析构函数:PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行 + +### 魔术方法 + +\_\_construct(), \_\_destruct(), \_\_call(), \_\_callStatic(), \_\_get(), \_\_set(), \_\_isset(), \_\_unset(), \_\_sleep(), \_\_wakeup(), \_\_toString(), \_\_invoke() 等方法在 PHP 中被称为"魔术方法"(Magic methods) + +### public、protected、private、final 区别 + +对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。被定义为公有的类成员可以在任何地方被访问 + +PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承 + +### 客户端/服务端 IP 获取,了解代理透传 实际IP 的概念 + +客户端IP: $\_SERVER['REMOTE_ADDR'] + +服务端IP: $\_SERVER['SERVER_ADDR'] + +客户端IP(代理透传): $\_SERVER['HTTP_X_FORWARDED_FOR'] + +### 类的静态调用和实例化调用 + +- 占用内存 + +静态方法在内存中只有一份,无论调用多少次,都是共用的 + +实例化不一样,每一个实例化是一个对象,在内存中是多个的 + +- 不同点 + +静态调用不需要实例化即可调用 + +静态方法不能调用非静态属性,因为非静态属性需要实例化后,存放在对象里 + +静态方法可以调用非静态方法,使用 self 关键字。php 里,一个方法被 `self::` 后,自动转变为静态方法 + +调用类的静态函数时不会自动调用类的构造函数 + +### 接口和抽象的区别 +抽象用于描述不同的事物,接口用于描述事物的行为。 + +### PHP 不实例化调用方法 + +静态调用、使用 PHP 反射方式 + +### php.ini 配置选项 + +- 配置选项 + +|名字|默认|备注| +|-|-|-| +|short_open_tag|"1"|是否开启缩写形式(``)| +|precision|"14"|浮点数中显示有效数字的位数| +|disable_functions|""|禁止某些函数| +|disable_classes|""|禁用某些类| +|expose_php|""|是否暴露 PHP 被安装在服务器上| +|max_execution_time|30|最大执行时间| +|memory_limit|128M|每个脚本执行的内存限制| +|error_reporting|NULL|设置错误报告的级别 `E_ALL` & ~`E_NOTICE` & ~`E_STRICT` & ~`E_DEPRECATED`| +|display_errors|"1"|显示错误| +|log_errors|"0"|设置是否将错误日志记录到 error_log 中| +|error_log|NULL|设置脚本错误将被记录到的文件| +|upload_max_filesize|"2M"|最大上传文件大小| +|post_max_size|"8M"|设置POST最大数据限制| + +```shell +php -ini | grep short_open_tag //查看 php.ini 配置 +``` + +- 动态设置 + +```php +ini_set(string $varname , string $newvalue); + +ini_set('date.timezone', 'Asia/Shanghai'); //设置时区 +ini_set('display_errors', '1'); //设置显示错误 +ini_set('memory_limit', '256M'); //设置最大内存限制 +``` + +### php-fpm.conf 配置 + +|名称|默认|备注| +|-|-|-| +|pid||PID文件的位置| +|error_log||错误日志的位置| +|log_level|notice|错误级别 alert:必须立即处理、error:错误情况、warning:警告情况、notice:一般重要信息、debug:调试信息| +|daemonize|yes|设置 FPM 在后台运行| +|listen|ip:port、port、/path/to/unix/socket|设置接受 FastCGI 请求的地址| +|pm|static、ondemand、dynamic|设置进程管理器如何管理子进程| +|request_slowlog_timeout|'0'|慢日志记录阀值| +|slowlog||慢请求的记录日志| + +### 502、504 错误产生原因及解决方式 + +#### 502 + +502 表示网关错误,当 PHP-CGI 得到一个无效响应,网关就会输出这个错误 + +- `php.ini` 的 memory_limit 过小 +- `php-fpm.conf` 中 max_children、max_requests 设置不合理 +- `php-fpm.conf` 中 request_terminate_timeout、max_execution_time 设置不合理 +- php-fpm 进程处理不过来,进程数不足、脚本存在性能问题 + +#### 504 + +504 表示网关超时,PHP-CGI 没有在指定时间响应请求,网关将输出这个错误 + +- Nginx+PHP 架构,可以调整 FastCGI 超时时间,fastcgi_connect_timeout、fastcgi_send_timeout、fastcgi_read_timeout + +#### 500 + +php 代码问题,文件权限问题,资源问题 + +#### 503 + +超载或者停机维护 + +### 如何返回一个301重定向 + +```php +header('HTTP/1.1 301 Moved Permanently'); +header('Location: https://blog.maplemark.cn'); +``` + +### PHP 与 MySQL 连接方式 + +#### MySQL + +```php +$conn = mysql_connect('127.0.0.1:3306', 'root', '123456'); +if (!$conn) { + die(mysql_error() . "\n"); +} +mysql_query("SET NAMES 'utf8'"); +$select_db = mysql_select_db('app'); +if (!$select_db) { + die(mysql_error() . "\n"); +} +$sql = "SELECT * FROM `user` LIMIT 1"; +$res = mysql_query($sql); +if (!$res) { + die(mysql_error() . "\n"); +} +while ($row = mysql_fetch_assoc($res)) { + var_dump($row); +} +mysql_close($conn); +``` + +#### MySQLi + +```php +$conn = @new mysqli('127.0.0.1:3306', 'root', '123456'); +if ($conn->connect_errno) { + die($conn->connect_error . "\n"); +} +$conn->query("set names 'utf8';"); +$select_db = $conn->select_db('user'); +if (!$select_db) { + die($conn->error . "\n"); +} +$sql = "SELECT * FROM `user` LIMIT 1"; +$res = $conn->query($sql); +if (!$res) { + die($conn->error . "\n"); +} +while ($row = $res->fetch_assoc()) { + var_dump($row); +} +$res->free(); +$conn->close(); +``` + +#### PDO + +```php +$pdo = new PDO('mysql:host=127.0.0.1:3306;dbname=user', 'root', '123456'); +$pdo->exec("set names 'utf8'"); +$sql = "SELECT * FROM `user` LIMIT 1"; +$stmt = $pdo->prepare($sql); +$stmt->bindValue(1, 1, PDO::PARAM_STR); +$rs = $stmt->execute(); +if ($rs) { + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + var_dump($row); + } +} +$pdo = null; +``` + +### MySQL、MySQLi、PDO 区别 + +#### MySQL + +- 允许 PHP 应用与 MySQL 数据库交互的早期扩展 +- 提供了一个面向过程的接口,不支持后期的一些特性 + +#### MySQLi + +- 面向对象接口 +- prepared 语句支持 +- 多语句执行支持 +- 事务支持 +- 增强的调试能力 + +#### PDO + +- PHP 应用中的一个数据库抽象层规范 +- PDO 提供一个统一的 API 接口,无须关心数据库类型 +- 使用标准的 PDO API,可以快速无缝切换数据库 + +### 数据库持久连接 + +把 PHP 用作多进程 web 服务器的一个模块,这种方法目前只适用于 Apache。 + +对于一个多进程的服务器,其典型特征是有一个父进程和一组子进程协调运行,其中实际生成 web 页面的是子进程。每当客户端向父进程提出请求时,该请求会被传递给还没有被其它的客户端请求占用的子进程。这也就是说当相同的客户端第二次向服务端提出请求时,它将有可能被一个不同的子进程来处理。在开启了一个持久连接后,所有请求 SQL 服务的后继页面都能够重用这个已经建立的 SQL Server 连接。 + +### 代码执行过程 + +PHP 代码 => 启动 php 及 zend 引擎,加载注册拓展模块 => 对代码进行词法/语法分析 => 编译成opcode(opcache) => 执行 opcode + +> PHP7 新增了抽象语法树(AST),在语法分析阶段生成 AST,然后再生成 opcode 数组 + +### base64 编码原理 + +![base64](./assets/php-base64.png) + +### ip2long 实现 + +![ip2long](./assets/php-ip2long.png) + + +``` +124.205.30.150=2093817494 + +list($p1,$p2,$p3,$p4) = explode('.','124.205.30.150'); + +$realNum = ($p1<<24)+($p2<<16)+($p3<<8)+$p4; +``` + +### MVC 的理解 + +MVC 包括三类对象。模型 Model 是应用对象,视图 View 是它在屏幕上的表示,控制器 Controller 定义用户界面对用户输入的响应方式。不使用 MVC,用户界面设计往往将这些对象混在一起,而 MVC 则将它们分离以提高灵活性和复用性 + +### 主流 PHP 框架特点 + +#### Laravel + +易于访问,功能强大,并提供大型,强大的应用程序所需的工具 + +- 简单快速的路由引擎 +- 强大的依赖注入容器 +- 富有表现力,直观的数据库 ORM +- 提供数据库迁移功能 +- 灵活的任务调度器 +- 实时事件广播 + +#### Symfony + +- Database engine-independent +- Simple to use, in most cases, but still flexible enough to adapt to complex cases +- Based on the premise of convention over configuration--the developer needs to configure only the unconventional +- Compliant with most web best practices and design patterns +- Enterprise-ready--adaptable to existing information technology (IT) policies and architectures, and stable enough for long-term projects +- Very readable code, with phpDocumentor comments, for easy maintenance +- Easy to extend, allowing for integration with other vendor libraries + +#### CodeIgniter + +- 基于模型-视图-控制器的系统 +- 框架比较轻量 +- 全功能数据库类,支持多个平台 +- Query Builder 数据库支持 +- 表单和数据验证 +- 安全性和 XSS 过滤 +- 全页面缓存 + +#### ThinkPHP + +- 采用容器统一管理对象 +- 支持 Facade +- 更易用的路由 +- 注解路由支持 +- 路由跨域请求支持 +- 验证类增强 +- 配置和路由目录独立 +- 取消系统常量 +- 类库别名机制 +- 模型和数据库增强 +- 依赖注入完善 +- 支持 PSR-3 日志规范 +- 中间件支持 +- 支持 Swoole/Workerman 运行 + +### 对象关系映射/ORM + +#### 优点 + +- 缩短编码时间、减少甚至免除对 model 的编码,降低数据库学习成本 +- 动态的数据表映射,在表结构发生改变时,减少代码修改 +- 可以很方便的引入附加功能(cache 层) + +#### 缺点 + +- 映射消耗性能、ORM 对象消耗内存 +- SQL 语句较为复杂时,ORM 语法可读性不高(使用原生 SQL) + +### 链式调用实现 + +类定义一个内置变量,让类中其他定义方法可访问到 + +### 异常处理 + +set_exception_handler — 设置用户自定义的异常处理函数 + +使用 try / catch 捕获 + +### 串行、并行、并发的区别 +串行:执行多个任务时,各个任务按顺序执行,完成一个之后才能进行下一个 +并行:多个任务在同一时刻执行 +并发:同一时刻需要执行多个任务 + +### 同步与异步的理解 +**同步和异步是一种消息通信机制**。其关注点在于 `被调用者返回` 和 `结果返回` 之间的关系,描述对象是被调用对象的行为。 + +### 阻塞与非阻塞的理解 +**阻塞和非阻塞是一种业务流程处理方式**。其关注点在于调用发生时 `调用者状态` 和 `被调用者返回结果` 之间的关系,描述对象是等待结果时候调用者的状态。 + +### 同步阻塞与非同步阻塞的理解 +同步阻塞:打电话问老板有没有某书(调用),老板说查一下,让你别挂电话(同步),你一直等待老板给你结果,什么事也不做(阻塞)。 + +同步非阻塞:打电话问老板有没有某书(调用),老板说查一下,让你别挂电话(同步),等电话的过程中你还一边嗑瓜子(非阻塞)。 + +### 异步阻塞与异步非阻塞的理解 +异步阻塞:打电话问老板有没有某书(调用),老板说你先挂电话,有了结果通知你(异步),你挂了电话后(结束调用), 除了等老板电话通知结果,什么事情也不做(阻塞)。 + +异步非阻塞:打电话问老板有没有某书(调用),老板说你先挂电话,有了结果通知你(异步),你挂电话后(结束调用),一遍等电话,一遍嗑瓜子。(非阻塞) + +### 如何实现异步调用 + +```php +$fp = fsockopen("blog.maplemark.cn", 80, $errno, $errstr, 30); +if (!$fp) { + echo "$errstr ($errno)
\n"; +} else { + $out = "GET /backend.php / HTTP/1.1\r\n"; + $out .= "Host: blog.maplemark.cn\r\n"; + $out .= "Connection: Close\r\n\r\n"; + fwrite($fp, $out); + /*忽略执行结果 + while (!feof($fp)) { + echo fgets($fp, 128); + }*/ + fclose($fp); +} +``` + +### 多进程同时写一个文件 + +加锁、队列 + +### PHP 进程模型,进程通讯方式,进程线程区别 + +消息队列、socket、信号量、共享内存、信号、管道 + +### PHP 支持回调的函数,实现一个 + +array_map、array_filter、array_walk、usort + +is_callable + callbacks + 匿名函数实现 + +### 发起 HTTP 请求有哪几种方式,它们有何区别 + +cURL、file_get_contents、fopen、fsockopen + +### php for while foreach 迭代数组时候,哪个效率最高 + +### 弱类型变量如何实现 + +PHP 中声明的变量,在 zend 引擎中都是用结构体 zval 来保存,通过共同体实现弱类型变量声明 + +### PHP 拓展初始化 + +- 初始化拓展 + +```shell +$ php /php-src/ext/ext_skel.php --ext +``` + +- 定义拓展函数 + +zend_module_entry 定义 Extension name 编写 PHP_FUNCTION 函数 + +- 编译安装 + +```shell +$ phpize $ ./configure $ make && make install +``` + +### 如何获取扩展安装路径 + +### 垃圾回收机制 + +引用计数器 + +### Trait + +自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait + +### yield 是什么,说个使用场景 yield、yield 核心原理是什么 + +一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以yield生成许多它所需要的值。 + +yield核心原理: PHP在使用生成器的时候,会返回一个Generator类的对象。每一次迭代,PHP会通过Generator实例计算出下一次需要迭代的值。简述即yield用于生成值。 + +yield使用场景:读取大文件、大量计算。 + +yield好处:节省内存、优化性能 + +### traits 与 interfaces 区别 及 traits 解决了什么痛点 + +### 如何 foreach 迭代对象、如何数组化操作对象 $obj[key]、如何函数化对象 $obj(123); + +### Swoole 适用场景,协程实现方式 + +Swoole 是一个使用 C++ 语言编写的基于异步事件驱动和协程的并行网络通信引擎,为 PHP 提供协程、高性能网络编程支持。提供了多种通信协议的网络服务器和客户端模块,可以方便快速的实现 TCP/UDP服务、高性能Web、WebSocket服务、物联网、实时通讯、游戏、微服务等,使 PHP 不再局限于传统的 Web 领域。 + + +协程可以简单理解为线程,只不过这个线程是用户态的,不需要操作系统参与,创建销毁和切换的成本非常低,和线程不同的是协程没法利用多核 cpu 的,想利用多核 cpu 需要依赖 Swoole 的多进程模型。 +在底层实现上是单线程的,因此同一时间只有一个协程在工作,协程的执行是串行的。 +采用 CSP 编程模型,即不要以共享内存的方式来通信,相反,要通过通信来共享内存。 +swoole4.0采用双栈方式,通过栈桢切换来实现协程;即遇到IO等待就切换到。 + +#### swoole的进程模型 + +同一台主机上两个进程间通信 (简称 IPC) 的方式有很多种,在 Swoole 中使用了 2 种方式 Unix Socket 和 sysvmsg。 + +swoole启动后会生成master进程、reactor线程、worker进程、task进程以及manager进程 + +master进程是一个多线程进程,会生成多个reactor线程 +reactor线程负载网络监听、数据收发 +work进程处理reactor线程投递的请求数据 +task进程处理work进程投递的任务 +manager进程用于管理work进程和task进程 + +### PHP 数组底层实现 (HashTable + Linked list) + +PHP 数组底层依赖的散列表数据结构,定义如下(位于 Zend/zend_types.h)。 + +数据存储在一个散列表中,通过中间层来保存索引与实际存储在散列表中位置的映射。 + +由于哈希函数会存在哈希冲突的可能,因此对冲突的值采用链表来保存。 + +哈希表的查询效率是o(1),链表查询效率是o(n);因此PHP数据索引速度很快;但是相对比较占用空间。 + +### Copy on write 原理,何时 GC + +### 如何解决 PHP 内存溢出问题 + +### ZVAL + +### HashTable + +### PHP7 新特性 + +标量类型声明、返回值类型声明、通过 define() 定义常量数组、匿名类、相同命名空间类一次性导入 + +### PHP7 底层优化 + +ZVAL 结构体优化,占用由24字节降低为16字节 + +内部类型 zend_string,结构体成员变量采用 char 数组,不是用 char* + +PHP 数组实现由 hashtable 变为 zend array + +函数调用机制,改进函数调用机制,通过优化参数传递环节,减少了一些指令 + +### PSR 介绍,PSR-1, 2, 4, 7 + +### Xhprof 、Xdebug 性能调试工具使用 + +### 字符串、数字比较大小的原理,注意 0 开头的8进制、0x 开头16进制 + +### BOM 头是什么,怎么除去 + +WINDOWS自带的记事本,在保存一个以 UTF-8 编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM);它是一串隐藏的字符,用于让记事本等编辑器识别这个文件是否以UTF-8编码。 +去除方法:$result = trim($result, "\xEF\xBB\xBF"); + +### 模板引擎是什么,解决什么问题、实现原理(Smarty、Twig、Blade) + + +### 写一个函数,尽可能高效的从一个标准 URL 中取出文件的扩展名 +parse_str,explode diff --git a/docs/05.MySQL/QA.md b/docs/05.MySQL/QA.md index 4c0f06c..dda3a87 100644 --- a/docs/05.MySQL/QA.md +++ b/docs/05.MySQL/QA.md @@ -194,7 +194,7 @@ MySQL 默认采用自动提交(AUTOCOMMIT)模式,每个查询都当作一个 #### MyISAM - 不支持事务和行级锁,崩溃后无法安全恢复,表锁非常影响性能 -- MyISAM 对整张表加锁,而不是针对行。读取时对需要读到的表加共享锁,写入则加排它锁。在表有读取查询的同事,也可以插入新记录(支持并发插入) +- MyISAM 对整张表加锁,而不是针对行。读取时对需要读到的表加共享锁,写入则加排它锁。在表有读取查询的同时,也可以插入新记录(支持并发插入) - 支持延迟更新索引健,极大的提升写入性能 - 支持全文索引,可以支持复杂的查询 - MyISAM 将表存储在两个文件中,数据文件和索引文件 @@ -286,3 +286,10 @@ MySQL 单表容量在`500万`左右,性能处于最佳状态,此时,MySQL ### my.cnf 配置 ### 慢查询 + + +### 优化 MYSQL 的方法 +- 数据超过一定数量或者体积,请拆分表,垂直或者水平分 +- 务必有自增主键。通过自增主键来查数据是最快的。 +- 常用的查询字段建立联合索引,写 SQL 一定要尊从最左原则,用到这个索引。 +- 不要把逻辑运算放到 sql 里 diff --git a/docs/06.Redis/QA.md b/docs/06.Redis/QA.md index 6cf04b5..09d2222 100644 --- a/docs/06.Redis/QA.md +++ b/docs/06.Redis/QA.md @@ -40,7 +40,7 @@ Redis 是一个高性能的 key-value 数据库。每秒可执行操作高达 10 ### 持久化策略 -#### 快照持久化 +#### 快照持久化 RDB 将某一时刻的所有数据写入硬盘。使用`BGSAVE`命令,随着内存使用量的增加,执行 BGSAVE 可能会导致系统长时间地停顿 @@ -190,6 +190,15 @@ Redis 是基于内存的操作,CPU 不是 Redis 的瓶颈,Redis 瓶颈最有 |ZREM|遍历压缩列表,删除所有包含给定成员的节点,以及被删除成员节点旁边的分值节点|遍历跳跃表,删除所有包含了给定成员的跳跃表节点。并在字典中解除被删除元素的成员和分值关联| |ZSCORE|遍历压缩列表,查找包含了给定成员的节点,然后取出成员节点旁边的分值节点保存的元素分值|直接从字典中取出给定成员的分值| + +### redis 跳跃表 + +跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。 + +跳跃表支持平均O(logN)、最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。 + +在大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树要来得更为简单,所以有不少程序都使用跳跃表来代替平衡树。 + ### redis.conf 配置 ### 慢查询 \ No newline at end of file diff --git a/docs/07.Linux/QA.md b/docs/07.Linux/QA.md index 91966fc..314f37f 100644 --- a/docs/07.Linux/QA.md +++ b/docs/07.Linux/QA.md @@ -148,20 +148,43 @@ X 个 CPU 的电脑,可接受的系统负荷最大为 `X.0` 。将`15分钟` ### Linux 内存管理 +### 函数 +malloc分配虚拟内存 +mmap虚拟内存映射方法 +hugepage虚拟内存映射方法,分配2MB或1GB +#### 虚拟内存 + +一种实现在计算机软硬件之间的内存管理技术,将程序使用到的内存地址映射到计算机内存中的物理地址 +32位虚拟内存大小为0-4g,一个内存也默认大小为4kb + +##### 优点 +提高内存安全性 + +##### 特点 +每个进程都有它自己的虚拟内存 +虚拟内存的大小取决于系统的体系结构 + ### 进程、线程、协程区别 #### 进程 -进程是一个程序在一个数据集中的一次动态执行过程,可以简单理解为“正在执行的程序”,它是CPU 资源分配和调度的独立单位 - +是一个动态概念;由程序、数据和进程控制块组成。系统进行资源分配、调度和管理的最小单位。 #### 线程 -线程是在进程之后发展出来的概念。 线程也叫轻量级进程,它是一个基本的 CPU 执行单元,也是程序执行过程中的最小单元,由线程 ID、程序计数器、寄存器集合和堆栈共同组成。一个进程可以包含多个线程 +是进程的活动成分,是处理器分配资源的最小单位;可以共享进程的资源与地址空间。 #### 协程 协程是一种用户态的轻量级线程,又称微线程,英文名 Coroutine,协程的调度完全由用户控制 + +#### 并发执行机制 + +把一个处理器划分为若干个短的时间片,每个时间片依次轮流地执行处理各个应用程序,由于一个时间片很短,相对于一个应用程序来说,就好像是处理器在为自己单独服务一样,从而达到多个应用程序在同时进行的效果。 +#### 多线程原理 + +多线程就是把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序划分为若干个子任务,多个子任务并发执行,每一个任务就是一个线程 + ### 进程间通信与信号机制 #### 通信方式 diff --git a/docs/assets/qr.png b/docs/assets/qr.png new file mode 100644 index 0000000..3c36ff7 Binary files /dev/null and b/docs/assets/qr.png differ diff --git a/docs/assets/questionnaire.jpeg b/docs/assets/questionnaire.jpeg new file mode 100644 index 0000000..dde0fad Binary files /dev/null and b/docs/assets/questionnaire.jpeg differ diff --git a/docs/php/FastCGI-Process-Manager.md b/docs/php/FastCGI-Process-Manager.md new file mode 100644 index 0000000..b68dcb3 --- /dev/null +++ b/docs/php/FastCGI-Process-Manager.md @@ -0,0 +1 @@ +# FastCGI Process Manager \ No newline at end of file diff --git "a/docs/php/assets/\345\233\2761-\345\207\275\346\225\260\346\257\224\350\276\203.png" "b/docs/php/assets/\345\233\2761-\345\207\275\346\225\260\346\257\224\350\276\203.png" new file mode 100644 index 0000000..ec86115 Binary files /dev/null and "b/docs/php/assets/\345\233\2761-\345\207\275\346\225\260\346\257\224\350\276\203.png" differ diff --git "a/docs/php/assets/\345\233\2762-\346\235\276\346\225\243\346\257\224\350\276\203.png" "b/docs/php/assets/\345\233\2762-\346\235\276\346\225\243\346\257\224\350\276\203.png" new file mode 100644 index 0000000..a75a658 Binary files /dev/null and "b/docs/php/assets/\345\233\2762-\346\235\276\346\225\243\346\257\224\350\276\203.png" differ diff --git "a/docs/php/assets/\345\233\2763-\344\270\245\346\240\274\346\257\224\350\276\203.png" "b/docs/php/assets/\345\233\2763-\344\270\245\346\240\274\346\257\224\350\276\203.png" new file mode 100644 index 0000000..692c208 Binary files /dev/null and "b/docs/php/assets/\345\233\2763-\344\270\245\346\240\274\346\257\224\350\276\203.png" differ diff --git "a/docs/php/composer\345\214\205\345\215\207\347\272\247.md" "b/docs/php/composer\345\214\205\345\215\207\347\272\247.md" new file mode 100644 index 0000000..bac5a65 --- /dev/null +++ "b/docs/php/composer\345\214\205\345\215\207\347\272\247.md" @@ -0,0 +1,110 @@ +# composer 包升级 + +本文从 composer 包升级任务,引出 composer 包生产环境升级技巧。全文共计 2429 字,阅读时间约 10 分钟, 作者:凌枫。如果文章对你有帮助,请关注和转发,谢谢! + +## 一、把 composer 包升级下版本 + +温馨提示:以下故事纯属虚构,如有雷同纯属巧合。 + +### 1、无尽临时任务 + +临近下班,刚想起身喝口水,突然弹出条消息。连忙跑去领导工位, xxx 包准备要升级下。心里想着,这玩意两周前不是刚升级过呢?这周必须上线,有点急,你看下怎么搞,领导强调。你还想再多问几句。我还有个会议,你先测试环境升级观察下,领导打断,表情异常严肃。 + +回到工位,拿着口杯去打水,心里不停的犯嘀咕。手上一共还有 4 个任务在同时跟进,排期也是满满的,而领导只是交代这周上线,也没说我手上任务怎么办,难道这个包升级又要开发自测吗?马上到年底了,已经忙活了一整年。这时候水溢出来了。 + +### 2、毫无意义的工作 + +半小时后,独自一人踏进那家难以下咽的快餐店。吃完后,特意多沿着公司周围多绕了一圈,肚中的米饭和怨气似乎都消化掉了。回到工位,办公室已经没几个人了,自从降本增效后,爱加班的人几乎都消失了。 + +打开本地终端,敲下熟练的命令行。进入本地 PHP7.x 容器,指定要升级的 composer 包,升级到了 1.2.4 版本。对比了本次更新的 1.2.4 版本,和上次 1.2.3 版本,差异点也就是把包依赖的网络配置文件从 A 域名换到 B 域名。看到这,心里莫名的谩骂:**某些人的 KPI 调优,却要自己来买单,对他们来说或许只是 1 个简单变更,但是对自己来说却要升级 5 个系统和走 1 次发布流程。** + +```bash +$ docker exec -it php72-for-dev sh // 进入php 7.x 容器 +$ composer require "xxx/xxx: 1.2.4" -W // 升级到指定版本 +$ composer dump-autoload -o // 更新优化 +``` + +**知识点1:进入 PHP 7.x 容器命令解释。**exec 是一个 docker 命令,用于在正在运行的容器中的执行命令。-i(--interactive) 参数指保持标准输入打开,允许向容器发送命令。-t(--tty)参数指分配一个伪终端,使容器内的 shell 能够以友好的交互模式运行。php72-for-dev 指一个正在运行的目标容器名称或者 ID。sh 指要在容器内执行的命令,这里指启动一个 shell 环境。 + +**知识点2:升级到指定版本命令解释。**require 是 composer 子命令,用于添加依赖包。xxx/xxx: 1.2.4 指要安装的包名为 xxx/xxx,版本为精确 1.2.4,加引号确保命令行正确解析。-W(--with-all-dependencies)指 composer 在执行更新时候,若遇到依赖包和项目中现有包不兼容,可以更全面的尝试解决这些冲突,而不是直接报错中止。 + +**知识点3:更新优化命令行解释。**dump-autoload 是一个用于优化 composer 自动加载性能的命令。不带 -o 为默认模式,依赖 PSR-4 / PSR-0 规则的动态查找,最终会生成 autoload\_psr4.php 等基于命名空间映射的文件,在运行时候需要按目录结构逐级查找文件有 I/O 开销,性能较低一般使用场景在开发环境。而带 -o 为优化模式,会预生成完整的类映射表,最终会生成 autolaod\_classmap.php 等包含类命与文件路径的对应关系,在运行时候直接通过数组键命查找路径而避免文件 I/O 开销,性能较高一般适合在生产环境部署。 + +### 3、独自扛下了所有 + +自测验证通过。按照对项目的熟悉度,挑选了几个适合的场景,抓了几个测试环境的请求,简单构造后,验证均通过。这里或许你有疑问,改动这么小,也需要自测吗?嗯嗯,大厂嘛,作业规范嘛。代码就绪后,发布前置准备流程也都准备就绪,一切就绪就准备火箭发射(生产发布)了。 + +惊心动魄的上线。周五晚上,办公室依然是静悄悄的。心想,改动这么小,肯定也没什么问题,而且测试环境也跑了几天了。抱着侥幸的想法,发布就奔着快的方式,直接梭哈了。紧接着,短信、电话、领导问询等等纷纷来了。职业敏感度让我意识到大事不妙,根据报警及查看大盘二次确认后,条件反射般选择了发布回滚。回滚后,监控指标纷纷回落,紧接着定位原因,原来是网络不通:B 域名出现大量调用失败。于是找运维给域名 B 加白,心里再次默念:看来又是被选中吃螃蟹的那个人。 + +无尽追问及复盘总结。当晚,手忙脚乱的把问题处理完,同时还要面对一堆人的追问。他们也一样会收到报警信息,他们在意的是饭碗,和上一级的问询应付,而并不在意你的实际感受。第二天,你还要被拉进另外一堆人,对这件事情的溯源,假设你不够机智,最后事故责任人可能还是你。 + +温馨提示:以上故事纯属虚构,如有雷同纯属巧合。 + +## 二、composer 简单介绍 + +### 1、简单介绍 + +composer 是什么呢?composer本意是:a person who writes music, especially as a professional occupation. 当然它不是用来艺术创作的。官网是这样介绍的:A Dependency Manager for PHP,简而言之就是:一个为 PHP 而生的依赖管理工具。 + +相信绝大部分 PHP 开发者都使用过,那你有了解过它到底是怎么运行起来的吗?让我们一起来一探究竟。 + +### 2、使用演示 + +参照下列目录结构,进行 composer 项目初始化。先创建一个 composer.json 文件,再使用 composer install 命令进行安装,接着就可以在 index.php 中引入了。 + +```shell +. +├── composer.json // 手动创建 +├── composer.lock +├── index.php // 手动创建 +└── vendor +``` + +```json +{ + // composer.json 文件 + "require": { + "monolog/monolog": "2.0.2" + }, + "autoload": { + "psr-4": { + "Acme\\": "src/" + } + } +} +``` + +```php +// index.php 文件 +require __DIR__ . '/vendor/autoload.php'; + +use Monolog\Logger; +use Monolog\Handler\StreamHandler; + +// create a log channel +$log = new Logger('name'); +$log->pushHandler(new StreamHandler('app.log', Logger::WARNING)); + +// add records to the log +$log->warning('Foo'); +$log->error('Bar'); +``` + +紧接着,你执行 index.php 文件后,可以看到目录下生成了 app.log 文件,里面有 2 行日志。 + +### 3、安装和运行 + +在运行 composer install,会读取 composer.json 配置中的 require 字段,然后从默认的包仓库 packagist 上查找 monolog/monolog,并下载其 2.0.2 版本及其所有依赖项到项目的 vendor 目录。而 composer.json 中的另一项配置 autoload 则定义配置自动加载规则,使用 PSR-4 标准,将项目中的 PHP 类文件与命名空间进行映射,实现自动加载,无须手动 include 文件。 + +在运行时,先假定已经执行过 composer dump-autoload -o 命令。首先,先通过 vendor/autoload.php 引导文件将 composer 引导类(CompoerAutoloaderInit....:getLoader())载入;再实例化自动加载的核心类;最后通过 spl\_autoload\_register 函数将加载方法注册到 PHP 的 SPL 自动加载队列中;在实例化类时候,加载器会按照预设的优先级策略去寻找类文件。 + +## 三、生产实践经验 + +在升级 composer 包时候,你必须清楚意识到,这个操作在应用层面是无法回滚的。意味着出问题了,你没办法快速恢复,肯定要影响生产的。如果你的应用无法接受不可用时间,甚至无法经受产生错误报警。那么你必须做好一下准备。 + +永远不要当第一个吃螃蟹的人。除非你的包提供方,可以百分百承担责任,而且白纸黑字保证。除此之外,你永远不要当第一个吃螃蟹的人。 + +可随时降级回退。既然无法在应用层回滚,那么我们可以考虑在运维层做好降级回退。即不要发全部机器,而是选择性发少量机器,把影响面控制在可以接受的程度,再逐步放量,这样可以用时间换空间。 + +**如果文章对你有帮助,请关注和转发,谢谢!** + diff --git "a/docs/php/isset\345\222\214empty\347\232\204\345\214\272\345\210\253\344\271\213\345\246\202\344\275\225\345\210\244\347\251\272.md" "b/docs/php/isset\345\222\214empty\347\232\204\345\214\272\345\210\253\344\271\213\345\246\202\344\275\225\345\210\244\347\251\272.md" new file mode 100644 index 0000000..9262403 --- /dev/null +++ "b/docs/php/isset\345\222\214empty\347\232\204\345\214\272\345\210\253\344\271\213\345\246\202\344\275\225\345\210\244\347\251\272.md" @@ -0,0 +1,108 @@ +# isset 和 empty 的区别之如何判空 + +本文从 isset 和 empty 的区别,引出常见的类型如何判空方式,帮你梳理清楚开发中常见的判空误区。全文共计 1724 字,阅读时间约 8 分钟,作者:凌枫。如果文章对你有帮助,请关注和转发,谢谢! + +## 一、isset 和 empty 的区别 + +### 1、是否声明 vs 是否设置 + +我们先来看一下在 PHP 语境下,变量的是否声明与是否设置的区别。变量是声明在指的是变量是否创建,是否在当前作用域内诞生,当你尝试使用一个未被声明的变量时,PHP 会抛出一个 E\_NOTICE 级别的错误(如:undefined variable)。变量是否设置指的是变量已经声明好了并且值不为 null,首先满足变量已创建,而且在当前作用域诞生,值有两种情况,非 null 和 null。 + +```php +// 是否存在:key1 存在 +// 是否存在:key3 不存在 +// 是否设置:key1 已设置 +// 是否设置:key2 未设置 +$arr = ['key1' => '凌枫', 'key2' => null]; +``` + +### 2、isset 与 empty 的区别 + +isset 是用于检测变量是否已声明php并且其值不为 null。isset 可以一次传入多个参数,只有在全部参数都已被设置时返回 true,计算过程为从左往右,中途遇到未设置的变量就会即可停止。 + +empty 是用于检查变量是否为空,如果变量不存在或者其值等于 false ,则认为变量为空。empty 不会在变量不存在时产生告警。 + +```php +$a; // isset($a)返回false empty($a)返回true +$b = ''; // true true +$b1 = '凌枫'; // true false +$c = null; // false true +$d = 0; // true true +$d1 = 1; // true false +$e = new stdClass(); // true true +$f = []; // true true +$f1 = ['凌枫']; // true false +``` + +## 二、PHP 的判空机制 + +### 1、松散的类型系统 + +造成编程中判空操作异常复杂都源于 PHP 的松散类型系统。这里我们暂不讨论松散的类型系统的优缺点。PHP 是一种弱类型编程语言,这意味着变量在声明时不需要预定义数据类型,并且可以在运行中根据上下文自动转换类型。我们可以看到这样的写法,前一刻变量还是整型类型,后一刻还是这个变量就变成字符类型。在某些情况下空数组、空字符串和 null 值似乎可以互通。而这些看似奇怪的特性,都源自 PHP 自身的松散的类型系统,进而导致增加了判空操作的复杂性。 + +### 2、动态变量解析机制 + +在判空操作中清楚的知道要检查的变量。动态变量解析机制是另一个体现 PHP 灵活的动态特性,同样也增加了代码的复杂性。在判空操作中,必须清楚的知道要检查的是哪个变量,以及变量名如何动态确定。当 PHP 解析器遇到 \$$name 时,执行操作顺序:首先获取 $name 的值,再将该值视为变量名去查找对应变量 $username,最终返回这个变量的值。 + +```php +$name = 'username'; +$username = '凌枫'; +echo $$name; // 输出: 凌枫 +echo $username; // 二者等价 +$nums = [1, 2, 3]; +echo "$number[1]"; // 输出:2 +echo "{$number[1]}"; // 输出:2 +``` + +### 3、类型转换规则 + +理解 PHP 的类型转换规则是掌握判空的基础。在 PHP 中,主要有两种类型转换方式:自动类型转换和强制类型转换。自动类型转换由 PHP 引擎在运行时根据上下文自动完成,即不同类型的值一起运算时,PHP 会自动将它们转换为统一的类型。强制类型转换指允许开发者显式的控制转换过程,包括类型转换语法、类型转换函数、settype 函数三种方式。 + +```php +// 自动转换 +$number = 1 + "0"; // 输出:1,字符串“0”转为整数0 +$number1 = 1 + "0abc"; // 输出:1,提取数字部分“0” +$number2 = 1 + "abc0"; // 输出:1,字符串开头无数字转为0 +// 强制转换 +$str = "0abc"; +$number = (int)$str; // 输出:0 +$number1 = floatval($str); // 输出:0 +$number2 = settype($str, 'int'); // $str 现在是整数0 +``` + +## 三、更多判空方式 + +### 1、空有哪些形式 + +这里我们先来看一下空有哪些形式。通过前面介绍,已经知道变量会经历以下过程:变量声明、变量赋值、变量使用;在变量声明时候可以知道变量类型,虽然在后续过程中变量类型并不是固定的;在变量使用中,又会发生变量动态解析和变量类型转换。 + +所以,空会包括变量未声明和变量已声明;而已变量已声明又包括这些空:null、空字符、整型0、布尔 false、空数组、空对象。 + +### 2、易错判空 + +未声明、null、空字符、空白字符、整型0、布尔 false、空数组、空对象都会被认为满足 empty 条件。 + +使用松散比较(==)替代严格比较(===),导致 0==null 返回 true 误判类型。 + +混淆 empty($arr\['key']) 和 isset($arr\['key']) 语义。 + +### 3、类型比较表 + +以下表格清晰的显示在 PHP 中不同方式的比较差异,包括函数方式、松散比较、严格比较。 + +![图1-函数比较](./assets/图1-函数比较.png) + +![图2-松散比较](./assets/图2-松散比较.png) + +![图3-严格比较](./assets/图3-严格比较.png) + +## 四、总结概要 + +通过本文的探讨,我们可以清晰地看到 PHP 判空操作背后的复杂性和重要性。常见的 isset()和 empty() 判空函数,看似相同实际上却又本质区别,但它们有着本质的区别。 + +PHP 的松散类型系统和动态变量解析机制使得判空操作需要格外谨慎。类型自动转换规则虽然提供了便利,但也带来了潜在的风险。判空的最终目的是为业务逻辑服务,而非机械地套用函数。 + +理解 PHP 的类型系统和转换规则,结合实际需求做出明智的判空决策,将帮助你编写出更加健壮、可靠的 PHP 代码。 + +如果文章对你有帮助,请关注和转发,谢谢! +![qr](../assets/qr.png) diff --git "a/docs/php/php\346\224\257\346\214\201\345\223\252\344\272\233\346\263\250\351\207\212\351\243\216\346\240\274.md" "b/docs/php/php\346\224\257\346\214\201\345\223\252\344\272\233\346\263\250\351\207\212\351\243\216\346\240\274.md" new file mode 100644 index 0000000..8e5e2b7 --- /dev/null +++ "b/docs/php/php\346\224\257\346\214\201\345\223\252\344\272\233\346\263\250\351\207\212\351\243\216\346\240\274.md" @@ -0,0 +1 @@ +# php支持哪些注释风格 \ No newline at end of file diff --git "a/docs/php/\344\270\200\346\235\241echo\350\276\223\345\207\272\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md" "b/docs/php/\344\270\200\346\235\241echo\350\276\223\345\207\272\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md" new file mode 100644 index 0000000..20cd775 --- /dev/null +++ "b/docs/php/\344\270\200\346\235\241echo\350\276\223\345\207\272\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md" @@ -0,0 +1,7 @@ +# 一条echo输出语句是如何执行的 + +```php +echo "Hello World!"; +``` + +请简述在浏览器输入地址,到页面输出`Hello World!`,其中发生了什么? \ No newline at end of file diff --git "a/docs/php/\345\215\225\345\274\225\345\217\267\345\222\214\345\217\214\345\274\225\345\217\267\347\232\204\345\214\272\345\210\253.md" "b/docs/php/\345\215\225\345\274\225\345\217\267\345\222\214\345\217\214\345\274\225\345\217\267\347\232\204\345\214\272\345\210\253.md" new file mode 100644 index 0000000..c39020d --- /dev/null +++ "b/docs/php/\345\215\225\345\274\225\345\217\267\345\222\214\345\217\214\345\274\225\345\217\267\347\232\204\345\214\272\345\210\253.md" @@ -0,0 +1,60 @@ +# 单引号和双引号的区别 + +本文主要从工程实践和语言设计层面的角度,介绍单引号和双引号的区别。全文共计 1757 字,作者:凌枫。如果文章对你有帮助,请关注和转发,谢谢! + +## 一、最主要区别 + +### 1、初步印象 + +当被问到单引号和双引号的区别,几乎本能反应都是,它们看起来都是一样的,难道真有什么区别?非要说有区别,难道就是语法习惯,最初我也是这样认为的。 + +### 2、最核心差异 + +单引号是“静态”的,双引号是“动态”的,一切差异均源于此。从 PHP 语言内部机制来看,则源于完全不同的编译和执行路径。在词法分析器识别过程中,单引号会被当做普通字符串值原样处理,双引号需要逐个扫描字符串,动态识别多种元素。 + +## 二、内部处理机制差异 + +我们接着从 PHP 语言特性,特别是其内部 Zend 引擎(Zend Engine)的角度来分析单引号和双引号字符串的处理机制。一般按执行顺序,会分为编译阶段和执行阶段。 + +### 1、编译阶段 + +在 PHP 代码运行时,首先会被 Zend Engine 的编译器转换成 Zend 操作码(OPCodes),这是 PHP 内部的一种中间代码。不管是单引号还是双引号编译后的操作码都会被 OPCache 缓存,因此在编译阶段的差异会被消除掉。单引号和双引号字符编译阶段对整体执行效率的影响不大。 + +单引号字符会被 Zend 引擎视为一个简单的常量字符串,简单地收集引号内的所有字符。词法分析器将其内容原样存储,几乎无需额外的处理。 + +双引号字符则会被 Zend 引擎识别为复杂的字符串,需要进行逐个扫描,词法分析器会识别片段为哪种元素,到底是普通字符、转义字符、变量标记,再采用不同的方式存储。所以双引号字符串最终不是作为一个整体存储,而是分解为一个包含多种类型 tokens 的序列。比如:字符串 "hello,$world\n"会被分解为,字符片段:"hello,"、变量:$world、换行符片段。 + +### 2、执行阶段 + +在 PHP 代码在执行阶段,操作码都是由 Zend Executor 执行。单引号字符串的执行,简单地从常量池中取出字符串并输出,这是一个极其快速的内存查找操作,没有额外的计算开销。 + +双引号字符串的执行,需要按顺序计算序列的每一部分。字符串片段,可以直接使用;变量则需要进入当前符号表查找变量,这是一个哈希操作,比直接取常量更慢。再将所有内容连接成一个新字符串,这里会涉及到内存分配和字符串拷贝操作。最终输出拼接好的字符串整体。 + +双引号字符串在运行时需要进行变量查找、值获取和字符串拼接等步骤,这些步骤都需要消耗 CPU 周期和内存资源。而单引号字符串则可以省去这些步骤。 + +## 三、具体不同 case 最佳实践 + +### 1、实践共识 + +一般默认使用单引号。从性能层面上看,单引号性能优于双引号,原因是双引号需要额外的词法分析和运行时拼接开销,而在绝大多数应用场景下,性能差异几乎可忽略不计。选择原因不应将性能作为首要决策因素,代码的可读性和可维护性明显更重要。 + +### 2、单引号优先 case + +从 PHP 内部机制看,可以清晰看到单引号字符串优势主要在运行时的查找和拼接开销,在现代 PHP 版本中,由于 Zend 引擎和 OPCache 的广泛应用,这种差异在绝大多数实际应用中变得微乎其微,执行中无法感知。唯一特例是在处理海量循环(如:百万次迭代)时,遵循“使用单引号除非你需要双引号的功能”这一原则仍然是最佳实践。 + +### 3、双引号优先case + +想要动态拼接字符串,使用双引号是更佳选择。我们的考虑因素,主要是代码的可读性和可维护性。 + +```php +$message = 'key1:' . $value1 . ' key2:' . $value2 . ' key3:' . $value3 . '\n'; // 可读性稍差 +$message = "key1:{$value1} key2:{$value2} key3:{$value3}\n"; // 可读性更佳 +$message = spintf("key1:%s key2:%s key3:%s\n", $value1, $value2, $value2); // 可读性最佳,非本文讨论内容 +``` + +## 四、总结概要 + +回顾全文,我们可以清晰地认识到,在 PHP 中单引号与双引号的选择,并非谁优谁劣。在现代 PHP 开发中,性能常常不是决策的主导因素,最佳实践是意图清晰,能够让代码阅读者秒懂:这里是一个无需变化的字符串,还是需要动态生成的字符串,这才是至关重要的。 + +如果文章对你有帮助,请关注和转发,谢谢! +![qr](../assets/qr.png) diff --git "a/docs/php/\345\260\206\345\217\230\351\207\217\346\211\223\345\215\260\345\207\272\346\235\245\344\275\240\347\237\245\351\201\223\345\223\252\344\272\233\346\226\271\345\274\217.md" "b/docs/php/\345\260\206\345\217\230\351\207\217\346\211\223\345\215\260\345\207\272\346\235\245\344\275\240\347\237\245\351\201\223\345\223\252\344\272\233\346\226\271\345\274\217.md" new file mode 100644 index 0000000..27c658e --- /dev/null +++ "b/docs/php/\345\260\206\345\217\230\351\207\217\346\211\223\345\215\260\345\207\272\346\235\245\344\275\240\347\237\245\351\201\223\345\223\252\344\272\233\346\226\271\345\274\217.md" @@ -0,0 +1,131 @@ +# 将变量打印出来,你知道哪些方式? + +> 本文主要从 PHP 输出的角度,系统的介绍常见的变量打印方式。你肯定都见过,但是肯定没有系统总结过。全文共计 2533 字,作者:凌枫。如果文章对你有帮助,请关注和转发,谢谢! + +## 一、关键字输出语句 + +### 1、echo 关键字 + +PHP 中最常见、最高效的输出语句。echo 不是函数,而是语言结构,可以不使用括号。可以输出一个或多个参数,没有额外的换行符或者空格。echo 没有返回值,因此不能在表达式的上下文中使用。 + +### 2、print 关键字 + +print 同样不是函数,而是语言结构。可以输出一个字符串,并始终返回 1,因此可用于表达式。和 echo 最主要的区别是 print 仅接受一个参数。效率略低于 echo。 + +```php +print 'hello'; +if ($expr && print 'foo') {} // 可用于表达式 +$result = print 'hello'; // 输出hello,且$result的值为1 +echo $result; // 输出1 +``` + +### 3、echo 和 print 如何选择 + +绝大多数情况下使用 echo,因为它相对更灵活,可一次输出多个参数,且速度极微地快一点。只有在需要利用其返回值 1 的极特殊场景下,如:作为三元运算符的一部分,才会使用 print,这时候若换成 echo 则会报错。 + +```php +$isHello = true; +$result = $isHello ? print 'hello' : print 'world'; // 使用print作为三元运算符的一部分 +echo "\n result: $result"; // 输出 hello result: 1 +``` + +## 二、调试输出函数 + +### 1、print_r()函数 + +以人类易读的形式打印显示变量的信息。对于字符串、整数等简单类型,会直接输出值。对于数组和对象,会以缩进的格式显示其键和值,层次结构非常清晰。第二的可选参数设置为 true,则会将结果作为字符串返回,而不直接输出,常常用在处理响应体或者日志。 + +在命中行终端(CLI)和网页浏览器之间的差异。首先 print\_r 函数本身在这两种模式下是完全一致的,而命令行似乎被格式化好了,而网页浏览器却没有格式化,二者差异主要在于网页浏览器是一个 HTML 渲染引擎。所以在浏览器下需要通过 echo 提前输出\
并在 print\_r 之前。
+
+```php
+$arr = ['a', 'b', 'c'];
+echo '
'; // 网页浏览器需要加上这个
+print_r($arr);
+// print_r($arr, true); // 无输出,直接返回
+// 输出结果
+Array
+(
+    [0] => a
+    [1] => b
+    [2] => c
+)
+```
+
+### 2、var_dump()函数
+
+PHP 中功能最强大的调试函数。用于输出一个或多个表达式的结构信息,包括表达式的类型和长度及值,对于数组或对象将递归按层级展开所有元素,并通过缩进显示其结构。没有返回选项,总是直接输出,如果结构特别复杂输出的信息会特别长。
+
+```php
+$age = 24;
+$people = 'phper';
+$arr = ['a', 'b', 'c'];
+echo '
';
+var_dump($age, $people, $arr);
+// 输出结果
+int(24)
+string(5) "phper"
+array(3) {
+  [0]=>
+  string(1) "a"
+  [1]=>
+  string(1) "b"
+  [2]=>
+  string(1) "c"
+}
+```
+
+### 3、var\_export()函数
+
+输出或返回变量的可解析字符串表示。其输出是合法的 PHP 代码,可以直接复制到程序中使用。第二个参数为 true 时,会返回字符串而不是直接输出,这个特性使其非常适合用于缓存或序列化数据。
+
+```php
+$arr = ['a', 'b', 'c'];
+var_export($arr);
+// 输出结果
+array (
+    0 => 'a',
+    1 => 'b',
+    2 => 'c',
+)
+```
+
+## 三、特殊输出场景
+
+### 1、printf() 和 sprintf()
+
+二者均用于格式化字符串输出。printf 会直接输出,sprintf 不会直接输出。sprintf 常常用于按顺序用参数替换格式字符串的占位符,简单理解 sprintf 里面第一个包含的相当于一个模板,可以非常直观的看到最终的效果。
+
+```php
+$name = '凌枫';
+$age = 24;
+printf("name:%s age:%d", $name, $age); // 输出 name:凌枫 age:24
+echo sprintf("name:%s age:%d", $name, $age); // 输出 name:凌枫 age:24
+$message = sprintf("my name is %s and i am %d years old", $name, $age); // 直观看到最终效果
+```
+
+### 2、JSON 输出方式
+
+一般在 Web 服务器可以看到。这种方式并非直接进行内容输出,而是先在输出主体内容之前,先告诉浏览器要输出的内容是 JSON 数据,再进行 JSON 编码,紧接着才输出内容本身。
+
+```php
+header('Content-Type: application/json');
+echo json_encode(['success' => true]);
+exit;
+```
+
+## 四、总结和对比
+
+综上所述,要将变量打印出来,上面一共列举了 8 种,分四大类:关键字、输出函数、格式化、JSON 输出。下面我们再通过一张表格,做一个简单的对比。
+
+|序号|输出类别|输出方式|主要用途|返回值|常用指数|
+|---|---|---|---|---|----|
+|1|关键字    | echo                 | 输出一个或多个字符串        | 无                  | ✳️✳️✳️✳️✳️ |
+|2|关键字    | print                | 输出一个字符串           | 始终返回 1             | ✳️         |
+|3|输出函数  | print_r()           | 调试,打印变量           | 可选返回               | ✳️         |
+|4|输出函数  | var_dump()          | 深度调试,打印类型和值       | 无                  | ✳️✳️✳️✳️✳️ |
+|5|输出函数  | var_export()        | 调试/缓存,生成合法 PHP 代码 | 可选返回               | ✳️✳️✳️     |
+|6|格式化   | printf() / sprintf() | 格式化字符串输出          | printf 无,sprintf 有 | ✳️✳️       |
+|7|JSON 输出| json 输出方式            | Web 服务器输出         | 无                  | ✳️✳️✳️✳️✳️ |
+
+如果文章对你有帮助,请关注和转发,谢谢!
+![qr](../assets/qr.png)
\ No newline at end of file
diff --git "a/docs/php/\345\275\223\344\270\213\346\234\200\346\265\201\350\241\214\347\232\204PHP\346\234\254\345\234\260\347\216\257\345\242\203\346\220\255\345\273\272\346\226\271\345\274\217.md" "b/docs/php/\345\275\223\344\270\213\346\234\200\346\265\201\350\241\214\347\232\204PHP\346\234\254\345\234\260\347\216\257\345\242\203\346\220\255\345\273\272\346\226\271\345\274\217.md"
new file mode 100644
index 0000000..027ff9c
--- /dev/null
+++ "b/docs/php/\345\275\223\344\270\213\346\234\200\346\265\201\350\241\214\347\232\204PHP\346\234\254\345\234\260\347\216\257\345\242\203\346\220\255\345\273\272\346\226\271\345\274\217.md"
@@ -0,0 +1,93 @@
+# 当下最流行的PHP本地环境搭建方式
+
+> 本文主要是互联网从业者视角,介绍一种PHP本地开发环境搭建的最佳实践方法。全文共计1630字,作者:凌枫。如果文章对你有帮助,请关注和转发,谢谢!
+
+## 一、传统环境搭建方式已过时
+
+### 1、曾经用过的本地环境搭建方式
+
+初学者福音,一键安装包。刚入行的时候,也曾经使用过PHPStudy、XAMP、MAMP。这是最简单、最快速的搭建方式,特别适合刚入行的初级开发者。假如没有这种便捷工具,估计自己也要被本地开发环境搭建给劝退。
+
+极致酷炫版,基于虚拟机的方式。在进阶到高级开发者后,要经常爬上服务器,于是迷上了CentOS。非常享受手撸本地开发环境的感觉,输入一行yum命令,终端就开始滚动起来,非常酷炫。可以非常轻松的安装Nginx、PHP、Redis、MySQL等等,而且可以手动修改各类配置。
+
+### 2、本地开发环境的核心痛点
+
+本地开发环境搭建究竟是容易还是难。在漫长的工作历程中,曾经遇到很多人搞不定本地开发环境,不管是刚入行的新人,还是有经验的从业者,都曾遇到过。对于大多数人来说,搭建本地开发环境,大概有这两种方式。方式一:现场Google搜索,现场安装;方式二:打开工具箱记事本,喝着咖啡安装。多问一句,公司用了什么奇怪奇奇的扩展版本,看着错误日志,你能搞定吗?
+
+开箱即用的本地开发环境,可定制、可分享、版本随切。你是否遇到以下问题?有多台电脑,需要配置多遍,还没法保持统一环境;每次换工作都要花1-2天时间跑项目;新员工入职,搞不定开发环境,有人用Mac有人用Windows;公司项目跑在7.x版本的PHP,想体验一下8.x版本的PHP。
+
+团队:需要一个定制的开箱即用本地开发环境,下次有人问起,直接甩过去。个人:一个属于自己的本地开发环境。
+
+### 3、让本地开发环境变得时髦些
+
+本地开发环境也要追求时髦,也可以内嵌到Docker里面。开箱即用 = docker-compose up。耳边天天都在响起起拥抱云原生,似乎跟普通开发不搭边。而当你把本地开发环境内嵌到Docker里面之后,脑海中的这种想法就彻底消失了,你发现在本地可以把4核CPU分配1核给Docker,换而言之,对公司容器化可以提高机器资源的利用率。
+
+公司测试和生产环境都在用容器,而通过本地环境Docker化,你将拥有一个本地实践的机会。在捣腾了很多年,贴合现在工作和个人诉求,终于找到一款好东西:DNMP — 当下最流行的PHP本地环境搭建方式。
+
+## 二、DNMP简单介绍
+
+### 1、DNMP简介
+
+什么是DNMP呢?DNMP = Docker + Nginx + MySQL + PHP + Redis + ElasticSearch + MongoDB + RabbitMQ,是一款全功能的LNMP一键安装程序,也支持Arm CPU。
+
+我心目中的DNMP是什么?基于当下流行的Docker方式,囊括PHP开发的所有运行依赖,只要稍加裁剪即可以完成定制。
+
+### 2、包含功能
+
+文件配置等于运行环境。之前不管是一键安装包,还是虚拟机方式,多少有点让人摸不着头脑的感觉。在你拥有了DNMP之后,瞬间让你感觉深入了源码的感觉。通过配置docker-compose.yml文件,可以定制所需要的services,比如nginx是一个service,php74也是一个service;每个运行的service的环境变量全部在env文件。
+
+### 3、内置丰富的PHP扩展
+
+内置多达152种常见和不常见的PHP扩展。被本地开发环境折磨的一个点,肯定少不了PHP扩展,不管你的编译安装还是源安装的方式,过程肯定非常痛苦。而DNMP内置了几乎所有的PHP扩展,数量多达152种,你需要做的就是设置为启用即可。
+
+## 三、从零开始搭建DNMP开发环境
+
+### 1、三步轻松完成初始化
+
+第一步:克隆项目,git clone https://github.com/zeszao/dnmp.git
+
+第二步:拷贝并命名配置文件,env.sample、docker-compose.sample.yml
+
+第三步:浏览器访问 https://localhost 即可看到效果
+
+### 2、如何在项目中使用
+
+第一步:将你的应用放进项目目录。将工作中需要用到的应用,都放进本地电脑的~/Workshop路径下。
+
+```text
+~/Workshop $ tree ~/Workshop -L 1
+/Users/colin/Workshop
+├── api-a
+├── api-b
+├── api-c
+└── api-d
+```
+
+第二步:将项目根目录挂载进容器。修改docker-compose.yml文件配置。
+
+```yaml
+version: "3"
+services:
+  php82:
+    volumes:
+      - ~/Workshop:/home/www 
+```
+
+第三步:逐个配置nginx的root路径。文件路径为services/nginx/conf.d/api-x.conf
+
+```text
+server {
+    server_name api-x.com;
+    listen 80:
+    
+    root /home/www/api-x/public;
+    index index.php index.html index.htm;
+}
+```
+
+嗯,在项目中使用,就是这么简单方便。
+
+综上所述:DNMP是一种当下最佳的PHP本地搭建方式,不管你是刚入行还是经验丰富开发者。
+
+如果文章对你有帮助,请关注和转发,谢谢!
+![qr](../assets/qr.png)
diff --git "a/docs/php/\347\274\226\347\250\213\350\257\255\350\250\200\347\256\200\344\273\213.md" "b/docs/php/\347\274\226\347\250\213\350\257\255\350\250\200\347\256\200\344\273\213.md"
new file mode 100644
index 0000000..3f3e4bb
--- /dev/null
+++ "b/docs/php/\347\274\226\347\250\213\350\257\255\350\250\200\347\256\200\344\273\213.md"
@@ -0,0 +1,76 @@
+# 编程语言简介
+
+> 本文主要是从编程语言的角度出发,介绍编程语言,帮你打消学习PHP这门编程语言的顾虑。全文共计2958字,作者:凌枫。如果文章对你有帮助,请关注和转发,谢谢!
+
+拥抱PHP,开启你的开发者之旅。当看到这段文字时,还记得为什么选择学PHP?是因为入门简单?好找工作?项目上手快?还是搞不定Java或C++?等等原因。
+
+## 一、学编程语言究竟在学什么
+
+### 1、编程语言的终极目标
+作为多年的编程语言实践者,在学习和运用编程语言的生涯里,要追求的是以下两点。一是不同的编程语言犹如不同的武功招式,不一定要学遍天下武功,掌握一门的诀窍,必要时可以快速上手另外一门;二是我们的目标是发明一门新的编程语言,而不是追求掌握每一门具体编程语言的细节。因此在你的整个生涯中,追求的终极目标都是以不变应万变。
+
+### 2、作为专业工程师
+
+学习一门编程语言,远不止于记忆语法,而是一个分层次、多维度的过程。首先,需要掌握特定的语法规则和熟练使用开发工具,从语法基础到核心概念再到高级特性;再则,能够理解普遍适合的核心编程概念和数据结构,培养编程思维,理解不同的编程范式;最后,锤炼自己的工程实践能力,并保持热爱以持续学习。
+
+语法规则和开发工具。语法规则就像学习外语的单词和语法一样,是写出合法程序的必备基础。包括关键字、运算符、语法结构、代码组织形式等。而开发工具(IDE)就像你要上网需要一台手机一样,需要掌握代码编辑、调试、代码执行、代码部署等。
+
+理解编程核心概念到培养编程思维。几乎所有的高级语言都在共享这些概念,只是不同语言写法有差异,包括:变量与数据结构、控制结构、函数和方法等。而怎么写到怎么想的转变,决定最后代码的高效性和易维护,包括:代码组织、模块化、错误处理、编程范式等。
+能落地的工程实践能力。代码就像建筑师建造的房子,最终是给人住的,而代码最终是要在项目运行的,也是要解决具体问题的。作为专业的工程师,先要通过了解各方信息,把要解决的问题定义清楚,再调出你的经验库,找到最适合、成本最低、能够落地的解决方案,再不断打磨最终上线。
+
+## 二、扒一下编程语言历史
+
+### 1、何为高级语言
+常见的编程语言均为高级语言。叫做高级语言,原因是比较接近人类的自然语言和数学表达,通过它能让普通人更容易的写出代码,更适合人类阅读的代码,也更容易维护的代码。相对高级语言而言,机器语言被视为低级语言,原因是它更接近机器本身,取悦的是机器而不是人,需要针对特定的硬件进行设计。
+
+### 2、高级语言有哪些
+在以往历史中出现的编程语言,一共可以分为5个世代。第一代语言是机器语言;第二代语言是汇编语言;第三代语言是高级语言,例如C;第四代语言是极高级语言,例如SQL;第五代语言是逻辑导向语言,又称自然语言。常见的高级语言均为第三代语言。高级语言包括:c语言、Java、C++、Go、PHP等。
+
+## 三、常见编程语言
+
+### 1、常见分类方法
+
+其实不同的编程语言都有在借鉴对方特性。常见的分类方法,包括:面向对象和面向过程、动态语言和静态语言等。更为常见的分类方法是根据特定应用场景,比如:C语言更适合嵌入式、Java适合跨平台和安卓开发、Python更擅长在数据分析和人工智能、JavaScript适合在Web前端、PHP适合在网站和中小型创新项目等。
+
+### 2、每年都在吵的事
+编程语言之间的争论,像是互联网技术圈里的武林大会。大家争吵的焦点一直都是:孰优孰劣、排行榜变化、就业前景和到底该学哪门语言,而争论的背后是对技术趋势和职业发展的顾虑。
+
+编程语言之间的鄙视链一直都存在,根源在于语言特性的差异和从业者的社会心理。Java、C++由于科班必修课,而且本身学习曲线也更高,让开发者不自觉认为高人一等。而JavaScript和PHP由于本身的动态灵活,再加上众多非科班的从业者涌入,一直被人吐槽可维护性,视为低人一等。
+
+排行榜的权威与局限。众多排行榜流行度,各有侧重,并不能完全代表实际情况。而作为从业者,关注排行榜一定要有一个正确的认识,要关注的是长期趋势,而非短期的波动。排名高的语言意味着生态系统更成熟,工作机会多,但不一定最适合你。
+
+### 3、PHP面临问题
+近年PHP排名和热度似乎一直在下滑,主要原因为互联网投资回归理性投资项目减少。就业岗位需求集中于现有系统的维护、更新和迭代。在新兴领域,如人工智能、大数据,由PHP作为主力技术栈的岗位相对较少。在一些中小企业成长成为大型企业的时候,往往因为PHP生态问题和从业者招聘原因,放弃将PHP作为主要技术栈,将公司技术栈转型为Java或者Go。综上原因,导致高阶PHP从业者薪资会比同水平的Java或者Go从业者低20%-30%,进一步加剧高阶开发者流向其他编程语言。
+
+对别人来说PHP是劣势,对你来说PHP或许是优势。第一类是新晋开发者,没有具体语言学习经验,也没有任何项目经验,或者尝试过其他语言觉得太难放弃的人;第二类是PHP从业者,已经掌握编程基础,也有一定的项目经验。对于前者,我的理由是唯快不破,能让你快速上手并找到工作,就是最重要的,嗯,先活下去,才有资格留在牌桌吐槽PHP。对于后者,深挖PHP的同事再学习其他语言,保持现状再求发展。
+
+## 四、最快的速成办法
+
+### 1、通过官网文档
+官方文档从来都是最好的学习材料。对于初学者先过一遍官方文档的语法参考部分,包括看和练习。完成这步之后,对PHP就已经有了初步的认识了。有人说,为什么我老是记不住,哪怕刚看完或者敲完代码,还是记不住呢。即使作为计算机科班,最后从事开发者工作了,也并没有多少,很多人都转行了。而你,现在要么选择PHP,要么还选择继续留在这个行业,肯定你没有更好的选择。所以只能逼自己一把吧,如果还是记不住,那只能说明PHP不适合你。
+
+### 2、实际项目开发
+接着你就可以把重心转向项目了,以练代学,以练促学。学习编程最终都是要做项目。而做项目也是最好的学习方式,在项目过程会遇到各种问题,遇到问题之后再回过去继续学。
+
+学习PHP其实是一件脑力活,也是一件苦力活。
+
+## 五、学习其他语言
+
+### 1、为什么要学习其他语言
+对于初学者而言,是强烈不建议着急掌握太多编程语言,也不太建议走所谓全栈工程师路线。对于有一定经验的开发者而言,为什么要学习其他语言呢?答案是生活或者工作所需,要么想涨薪,要么被领导安排。不然你说你兴趣广泛,你相信我也不相信。
+
+### 2、有什么好处
+延长职业生涯时间,东边不亮西边亮。学习其他语言最大的好处,就是可以极大的延长你的职业生涯。如果你一定要进大厂,又非PHP岗不干,那将完全没有机会。而你在学一门Go,那你将收获两种额外机会,一种是纯Go技术栈,还有一种是PHP转Go的技术栈。
+
+## 六、编程语言终极归宿
+
+### 1、PHP爱好者
+促使我们一直有愿意学习或者使用PHP,我大体分为以下几个阶段。第一阶段,兴趣使然让你跟PHP能够双向奔赴,它能够给你一份能填饱肚子的工作,这个是基础前提;第二阶段,突然有一天发现,继续学习或者使用PHP也没有新的突破,然后就陷入了瓶颈期;第三阶段,你开始重新评估自身,找到了新的突破点,就比如我在这里敲字给你一样。
+
+### 2、终极归属
+假如你还会留在PHP的就业市场里,大概会有一个最终的发展方向:深入研究业务、研究框架设计、转行。输入研究业务,指DDD这类;框架设计,指的是架构类,还有业务组件这些;而转行,指的是脱离一线编码,要么换行业,要么转管理。
+
+综上所述:无论你是刚刚踏入编程世界的新手,还是希望在PHP领域深耕的专业开发者,关键是要保持学习的热情和适应变化的能力。PHP可能不是完美的语言,但它绝对是一门实用的语言。
+
+如果文章对你有帮助,请关注和转发,谢谢!
+![qr](../assets/qr.png)
\ No newline at end of file
diff --git "a/docs/\345\217\202\350\200\203\350\265\204\346\226\231.md" "b/docs/\345\217\202\350\200\203\350\265\204\346\226\231.md"
index 413e88e..e5e274c 100644
--- "a/docs/\345\217\202\350\200\203\350\265\204\346\226\231.md"
+++ "b/docs/\345\217\202\350\200\203\350\265\204\346\226\231.md"
@@ -35,6 +35,8 @@
 - [IBM Developer](https://www.ibm.com/developerworks/cn/topics/)
 - [API 设计指南](https://cloud.google.com/apis/design/?hl=zh-cn)
 - [Microsoft REST API Guidelines](https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md)
+- [MySQL实战45讲](https://time.geekbang.org/column/intro/100020801)
+- [12步通关求职面试](https://kaiwu.lagou.com/course/courseInfo.htm?courseId=18#/sale)
 
 ## 职级参考
 
diff --git "a/docs/\345\255\230\345\202\250/MySQL\344\270\272\344\273\200\344\271\210\346\234\211\346\227\266\345\200\231\344\274\232\351\200\211\351\224\231\347\264\242\345\274\225.md" "b/docs/\345\255\230\345\202\250/MySQL\344\270\272\344\273\200\344\271\210\346\234\211\346\227\266\345\200\231\344\274\232\351\200\211\351\224\231\347\264\242\345\274\225.md"
new file mode 100644
index 0000000..111f927
--- /dev/null
+++ "b/docs/\345\255\230\345\202\250/MySQL\344\270\272\344\273\200\344\271\210\346\234\211\346\227\266\345\200\231\344\274\232\351\200\211\351\224\231\347\264\242\345\274\225.md"
@@ -0,0 +1 @@
+# MySQL为什么有时候会选错索引
\ No newline at end of file
diff --git "a/docs/\345\255\230\345\202\250/MySQL\346\230\257\346\200\216\344\271\210\344\277\235\350\257\201\344\270\273\345\244\207\344\270\200\350\207\264\347\232\204.md" "b/docs/\345\255\230\345\202\250/MySQL\346\230\257\346\200\216\344\271\210\344\277\235\350\257\201\344\270\273\345\244\207\344\270\200\350\207\264\347\232\204.md"
new file mode 100644
index 0000000..ed7df1d
--- /dev/null
+++ "b/docs/\345\255\230\345\202\250/MySQL\346\230\257\346\200\216\344\271\210\344\277\235\350\257\201\344\270\273\345\244\207\344\270\200\350\207\264\347\232\204.md"
@@ -0,0 +1 @@
+# MySQL是怎么保证主备一致的
\ No newline at end of file
diff --git "a/docs/\345\255\230\345\202\250/MySQL\346\230\257\346\200\216\344\271\210\344\277\235\350\257\201\346\225\260\346\215\256\344\270\215\344\270\242\347\232\204.md" "b/docs/\345\255\230\345\202\250/MySQL\346\230\257\346\200\216\344\271\210\344\277\235\350\257\201\346\225\260\346\215\256\344\270\215\344\270\242\347\232\204.md"
new file mode 100644
index 0000000..bc473c5
--- /dev/null
+++ "b/docs/\345\255\230\345\202\250/MySQL\346\230\257\346\200\216\344\271\210\344\277\235\350\257\201\346\225\260\346\215\256\344\270\215\344\270\242\347\232\204.md"
@@ -0,0 +1 @@
+# MySQL是怎么保证数据不丢的
\ No newline at end of file
diff --git "a/docs/\345\255\230\345\202\250/MySQL\346\230\257\346\200\216\344\271\210\344\277\235\350\257\201\351\253\230\345\217\257\347\224\250\347\232\204.md" "b/docs/\345\255\230\345\202\250/MySQL\346\230\257\346\200\216\344\271\210\344\277\235\350\257\201\351\253\230\345\217\257\347\224\250\347\232\204.md"
new file mode 100644
index 0000000..1f5b9a2
--- /dev/null
+++ "b/docs/\345\255\230\345\202\250/MySQL\346\230\257\346\200\216\344\271\210\344\277\235\350\257\201\351\253\230\345\217\257\347\224\250\347\232\204.md"
@@ -0,0 +1 @@
+# MySQL是怎么保证高可用的
\ No newline at end of file
diff --git "a/docs/\345\255\230\345\202\250/MySQL\346\234\211\345\223\252\344\272\233\351\245\256\351\270\251\346\255\242\346\270\264\346\217\220\351\253\230\346\200\247\350\203\275\347\232\204\346\226\271\346\263\225.md" "b/docs/\345\255\230\345\202\250/MySQL\346\234\211\345\223\252\344\272\233\351\245\256\351\270\251\346\255\242\346\270\264\346\217\220\351\253\230\346\200\247\350\203\275\347\232\204\346\226\271\346\263\225.md"
new file mode 100644
index 0000000..7289834
--- /dev/null
+++ "b/docs/\345\255\230\345\202\250/MySQL\346\234\211\345\223\252\344\272\233\351\245\256\351\270\251\346\255\242\346\270\264\346\217\220\351\253\230\346\200\247\350\203\275\347\232\204\346\226\271\346\263\225.md"
@@ -0,0 +1 @@
+# MySQL有哪些饮鸩止渴提高性能的方法
\ No newline at end of file
diff --git "a/docs/\345\255\230\345\202\250/assets/\344\270\200\346\235\241SQL\346\233\264\346\226\260\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204-update\350\257\255\345\217\245\346\211\247\350\241\214\346\265\201\347\250\213.png" "b/docs/\345\255\230\345\202\250/assets/\344\270\200\346\235\241SQL\346\233\264\346\226\260\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204-update\350\257\255\345\217\245\346\211\247\350\241\214\346\265\201\347\250\213.png"
new file mode 100644
index 0000000..39dc6e0
Binary files /dev/null and "b/docs/\345\255\230\345\202\250/assets/\344\270\200\346\235\241SQL\346\233\264\346\226\260\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204-update\350\257\255\345\217\245\346\211\247\350\241\214\346\265\201\347\250\213.png" differ
diff --git "a/docs/\345\255\230\345\202\250/assets/\344\270\200\346\235\241SQL\346\233\264\346\226\260\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204-write-pos.png" "b/docs/\345\255\230\345\202\250/assets/\344\270\200\346\235\241SQL\346\233\264\346\226\260\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204-write-pos.png"
new file mode 100644
index 0000000..ea7837d
Binary files /dev/null and "b/docs/\345\255\230\345\202\250/assets/\344\270\200\346\235\241SQL\346\233\264\346\226\260\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204-write-pos.png" differ
diff --git "a/docs/\345\255\230\345\202\250/assets/\344\270\200\346\235\241SQL\346\237\245\350\257\242\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204-\345\237\272\346\234\254\346\236\266\346\236\204\345\233\276.png" "b/docs/\345\255\230\345\202\250/assets/\344\270\200\346\235\241SQL\346\237\245\350\257\242\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204-\345\237\272\346\234\254\346\236\266\346\236\204\345\233\276.png"
new file mode 100644
index 0000000..9f6ba7f
Binary files /dev/null and "b/docs/\345\255\230\345\202\250/assets/\344\270\200\346\235\241SQL\346\237\245\350\257\242\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204-\345\237\272\346\234\254\346\236\266\346\236\204\345\233\276.png" differ
diff --git "a/docs/\345\255\230\345\202\250/assets/\344\272\213\345\212\241\351\232\224\347\246\273-\344\270\272\344\273\200\344\271\210\344\275\240\346\224\271\344\272\206\346\210\221\350\277\230\347\234\213\344\270\215\350\247\201-\344\272\213\345\212\241\346\217\220\344\272\244.png" "b/docs/\345\255\230\345\202\250/assets/\344\272\213\345\212\241\351\232\224\347\246\273-\344\270\272\344\273\200\344\271\210\344\275\240\346\224\271\344\272\206\346\210\221\350\277\230\347\234\213\344\270\215\350\247\201-\344\272\213\345\212\241\346\217\220\344\272\244.png"
new file mode 100644
index 0000000..2fb1164
Binary files /dev/null and "b/docs/\345\255\230\345\202\250/assets/\344\272\213\345\212\241\351\232\224\347\246\273-\344\270\272\344\273\200\344\271\210\344\275\240\346\224\271\344\272\206\346\210\221\350\277\230\347\234\213\344\270\215\350\247\201-\344\272\213\345\212\241\346\217\220\344\272\244.png" differ
diff --git "a/docs/\345\255\230\345\202\250/assets/\344\272\213\345\212\241\351\232\224\347\246\273-\344\270\272\344\273\200\344\271\210\344\275\240\346\224\271\344\272\206\346\210\221\350\277\230\347\234\213\344\270\215\350\247\201-\345\233\236\346\273\232\346\227\245\345\277\227\350\256\260\345\275\225.png" "b/docs/\345\255\230\345\202\250/assets/\344\272\213\345\212\241\351\232\224\347\246\273-\344\270\272\344\273\200\344\271\210\344\275\240\346\224\271\344\272\206\346\210\221\350\277\230\347\234\213\344\270\215\350\247\201-\345\233\236\346\273\232\346\227\245\345\277\227\350\256\260\345\275\225.png"
new file mode 100644
index 0000000..49b3233
Binary files /dev/null and "b/docs/\345\255\230\345\202\250/assets/\344\272\213\345\212\241\351\232\224\347\246\273-\344\270\272\344\273\200\344\271\210\344\275\240\346\224\271\344\272\206\346\210\221\350\277\230\347\234\213\344\270\215\350\247\201-\345\233\236\346\273\232\346\227\245\345\277\227\350\256\260\345\275\225.png" differ
diff --git "a/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-InnoDB\347\232\204\347\264\242\345\274\225\347\273\204\347\273\207\347\273\223\346\236\204.png" "b/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-InnoDB\347\232\204\347\264\242\345\274\225\347\273\204\347\273\207\347\273\223\346\236\204.png"
new file mode 100644
index 0000000..f43a9e4
Binary files /dev/null and "b/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-InnoDB\347\232\204\347\264\242\345\274\225\347\273\204\347\273\207\347\273\223\346\236\204.png" differ
diff --git "a/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.png" "b/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.png"
new file mode 100644
index 0000000..0193f97
Binary files /dev/null and "b/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.png" differ
diff --git "a/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-\345\223\210\345\270\214\347\264\242\345\274\225.png" "b/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-\345\223\210\345\270\214\347\264\242\345\274\225.png"
new file mode 100644
index 0000000..c4ee2c4
Binary files /dev/null and "b/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-\345\223\210\345\270\214\347\264\242\345\274\225.png" differ
diff --git "a/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-\346\234\211\345\272\217\346\225\260\347\273\204.png" "b/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-\346\234\211\345\272\217\346\225\260\347\273\204.png"
new file mode 100644
index 0000000..c566e9e
Binary files /dev/null and "b/docs/\345\255\230\345\202\250/assets/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212-\346\234\211\345\272\217\346\225\260\347\273\204.png" differ
diff --git "a/docs/\345\255\230\345\202\250/count()\350\277\231\344\271\210\346\205\242\357\274\214\346\210\221\350\257\245\346\200\216\344\271\210\345\212\236.md" "b/docs/\345\255\230\345\202\250/count()\350\277\231\344\271\210\346\205\242\357\274\214\346\210\221\350\257\245\346\200\216\344\271\210\345\212\236.md"
new file mode 100644
index 0000000..aaa9635
--- /dev/null
+++ "b/docs/\345\255\230\345\202\250/count()\350\277\231\344\271\210\346\205\242\357\274\214\346\210\221\350\257\245\346\200\216\344\271\210\345\212\236.md"
@@ -0,0 +1 @@
+# count()这么慢,我该怎么办
\ No newline at end of file
diff --git "a/docs/\345\255\230\345\202\250/order-by\346\230\257\346\200\216\344\271\210\345\267\245\344\275\234\347\232\204.md" "b/docs/\345\255\230\345\202\250/order-by\346\230\257\346\200\216\344\271\210\345\267\245\344\275\234\347\232\204.md"
new file mode 100644
index 0000000..e1c3845
--- /dev/null
+++ "b/docs/\345\255\230\345\202\250/order-by\346\230\257\346\200\216\344\271\210\345\267\245\344\275\234\347\232\204.md"
@@ -0,0 +1 @@
+# order by是怎么工作的
\ No newline at end of file
diff --git "a/docs/\345\255\230\345\202\250/\344\270\200\346\235\241SQL\346\233\264\346\226\260\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md" "b/docs/\345\255\230\345\202\250/\344\270\200\346\235\241SQL\346\233\264\346\226\260\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md"
new file mode 100644
index 0000000..7d45c3f
--- /dev/null
+++ "b/docs/\345\255\230\345\202\250/\344\270\200\346\235\241SQL\346\233\264\346\226\260\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md"
@@ -0,0 +1,124 @@
+# 一条SQL更新语句是如何执行的
+
+前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模块。相信你还记得,一条查询语句的执行过程一般是经过连接器、分析器、优化器、执行器等功能模块,最后到达存储引擎。
+
+那么,一条更新语句的执行流程又是怎样的呢?
+
+之前你可能经常听 DBA 同事说,MySQL 可以恢复到半个月内任意一秒的状态,惊叹的同时,你是不是心中也会不免会好奇,这是怎样做到的呢?
+
+我们还是从一个表的一条更新语句说起,下面是这个表的创建语句,这个表有一个主键 ID 和一个整型字段 c:
+
+> mysql> create table T(ID int primary key, c int);
+如果要将 ID=2 这一行的值加 1,SQL 语句就会这么写:
+
+> mysql> update T set c=c+1 where ID=2;
+
+前面我有跟你介绍过 SQL 语句基本的执行链路,这里我再把那张图拿过来,你也可以先简单看看这个图回顾下。首先,可以确定的说,查询语句的那一套流程,更新语句也是同样会走一遍。
+
+![基本架构示意图](./assets/一条SQL查询语句是如何执行的-基本架构图.png)
+
+你执行语句前要先连接数据库,这是连接器的工作。
+
+前面我们说过,在一个表上有更新的时候,跟这个表有关的查询缓存会失效,所以这条语句就会把表 T 上所有缓存结果都清空。这也就是我们一般不建议使用查询缓存的原因。
+
+接下来,分析器会通过词法和语法解析知道这是一条更新语句。优化器决定要使用 ID 这个索引。然后,执行器负责具体执行,找到这一行,然后更新。
+
+与查询流程不一样的是,更新流程还涉及两个重要的日志模块,它们正是我们今天要讨论的主角:redo log(重做日志)和 binlog(归档日志)。如果接触 MySQL,那这两个词肯定是绕不过的,我后面的内容里也会不断地和你强调。不过话说回来,redo log 和 binlog 在设计上有很多有意思的地方,这些设计思路也可以用到你自己的程序里。
+
+## 重要的日志模块:redo log
+
+不知道你还记不记得《孔乙己》这篇文章,酒店掌柜有一个粉板,专门用来记录客人的赊账记录。如果赊账的人不多,那么他可以把顾客名和账目写在板上。但如果赊账的人多了,粉板总会有记不下的时候,这个时候掌柜一定还有一个专门记录赊账的账本。
+
+如果有人要赊账或者还账的话,掌柜一般有两种做法:
+
+- 一种做法是直接把账本翻出来,把这次赊的账加上去或者扣除掉;
+- 另一种做法是先在粉板上记下这次的账,等打烊以后再把账本翻出来核算
+
+在生意红火柜台很忙时,掌柜一定会选择后者,因为前者操作实在是太麻烦了。首先,你得找到这个人的赊账总额那条记录。你想想,密密麻麻几十页,掌柜要找到那个名字,可能还得带上老花镜慢慢找,找到之后再拿出算盘计算,最后再将结果写回到账本上。
+
+这整个过程想想都麻烦。相比之下,还是先在粉板上记一下方便。你想想,如果掌柜没有粉板的帮助,每次记账都得翻账本,效率是不是低得让人难以忍受?
+
+同样,在 MySQL 里也有这个问题,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。为了解决这个问题,MySQL 的设计者就用了类似酒店掌柜粉板的思路来提升更新效率。
+
+而粉板和账本配合的整个过程,其实就是 MySQL 里经常说到的 WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘,也就是先写粉板,等不忙的时候再写账本。
+
+具体来说,当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log(粉板)里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做,这就像打烊以后掌柜做的事。
+
+如果今天赊账的不多,掌柜可以等打烊后再整理。但如果某天赊账的特别多,粉板写满了,又怎么办呢?这个时候掌柜只好放下手中的活儿,把粉板中的一部分赊账记录更新到账本中,然后把这些记录从粉板上擦掉,为记新账腾出空间。
+
+与此类似,InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。
+
+![write pos](./assets/一条SQL更新语句是如何执行的-write-pos.png)
+
+write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
+
+write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。
+
+有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
+
+要理解 crash-safe 这个概念,可以想想我们前面赊账记录的例子。只要赊账记录记在了粉板上或写在了账本上,之后即使掌柜忘记了,比如突然停业几天,恢复生意后依然可以通过账本和粉板上的数据明确赊账账目。
+
+## 重要的日志模块:binlog
+
+前面我们讲过,MySQL 整体来看,其实就有两块:一块是 Server 层,它主要做的是 MySQL 功能层面的事情;还有一块是引擎层,负责存储相关的具体事宜。上面我们聊到的粉板 redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。
+
+我想你肯定会问,为什么会有两份日志呢?
+
+因为最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。
+
+这两种日志有以下三点不同。
+
+1、redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
+
+2、redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
+redo log 是循环写的,空间固定会用完;
+
+3、binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
+
+有了对这两个日志的概念性理解,我们再来看执行器和 InnoDB 引擎在执行这个简单的 update 语句时的内部流程。
+
+1、执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
+
+2、执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
+
+3、引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
+
+4、执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
+
+5、执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。
+
+这里我给出这个 update 语句的执行流程图,图中浅色框表示是在 InnoDB 内部执行的,深色框表示是在执行器中执行的。
+
+![update语句执行流程](./assets/一条SQL更新语句是如何执行的-update语句执行流程.png)
+
+你可能注意到了,最后三步看上去有点“绕”,将 redo log 的写入拆成了两个步骤:prepare 和 commit,这就是”两阶段提交”。
+
+## 两阶段提交
+
+为什么必须有“两阶段提交”呢?这是为了让两份日志之间的逻辑一致。要说明这个问题,我们得从文章开头的那个问题说起:怎样让数据库恢复到半个月内任意一秒的状态?
+
+前面我们说过了,binlog 会记录所有的逻辑操作,并且是采用“追加写”的形式。如果你的 DBA 承诺说半个月内可以恢复,那么备份系统中一定会保存最近半个月的所有 binlog,同时系统会定期做整库备份。这里的“定期”取决于系统的重要性,可以是一天一备,也可以是一周一备。
+
+当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:
+
+- 首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库;
+- 然后,从备份的时间点开始,将备份的 binlog 依次取出来,重放到中午误删表之前的那个时刻。
+
+这样你的临时库就跟误删之前的线上库一样了,然后你可以把表数据从临时库取出来,按需要恢复到线上库去。
+
+好了,说完了数据恢复过程,我们回来说说,为什么日志需要“两阶段提交”。这里不妨用反证法来进行解释。
+
+由于 redo log 和 binlog 是两个独立的逻辑,如果不用两阶段提交,要么就是先写完 redo log 再写 binlog,或者采用反过来的顺序。我们看看这两种方式会有什么问题。
+
+仍然用前面的 update 语句来做例子。假设当前 ID=2 的行,字段 c 的值是 0,再假设执行 update 语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了 crash,会出现什么情况呢?
+
+1、先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。 但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。 然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。
+
+2、先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。
+可以看到,如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。
+
+你可能会说,这个概率是不是很低,平时也没有什么动不动就需要恢复临时库的场景呀?
+
+其实不是的,不只是误操作后需要用这个过程来恢复数据。当你需要扩容的时候,也就是需要再多搭建一些备库来增加系统的读能力的时候,现在常见的做法也是用全量备份加上应用 binlog 来实现的,这个“不一致”就会导致你的线上出现主从数据库不一致的情况。
+
+简单说,redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。
\ No newline at end of file
diff --git "a/docs/\345\255\230\345\202\250/\344\270\200\346\235\241SQL\346\237\245\350\257\242\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md" "b/docs/\345\255\230\345\202\250/\344\270\200\346\235\241SQL\346\237\245\350\257\242\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md"
new file mode 100644
index 0000000..556e161
--- /dev/null
+++ "b/docs/\345\255\230\345\202\250/\344\270\200\346\235\241SQL\346\237\245\350\257\242\350\257\255\345\217\245\346\230\257\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md"
@@ -0,0 +1,59 @@
+# 一条SQL查询语句是如何执行的
+
+## SQL内部执行过程
+
+假如你有一张表T,表里只有一个id字段,在执行下面这个查询语句,简述一下这条语句在MySQL内部的执行过程。
+
+> mysql > SELECT * FROM T WHERE id=10;
+
+## MySQL架构概览
+
+这是一张MySQL的基本架构示意图,通过图可以清晰的看到SQL语句在MySQL的各个模块中的执行过程。
+
+![基本架构示意图](./assets/一条SQL查询语句是如何执行的-基本架构图.png)
+
+- 大体来说,MySQL可以分为Server层和存储引擎层两部分。
+- Server层包括连接器、查询缓存、分析器、优化器、执行器等核心服务功能,以及所有内置函数,比如日期、时间和加密函数等;所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
+- 存储引擎层负责数据的存储和提取,其架构模式是插件式,支持InnoDB、MyISAM、Memory等多个存储引擎,其中InnoDB是最常用的存储引擎,从MySQL 5.5.5版本开始成为默认引擎。
+
+## 连接器
+
+- 连接器负责与客户端建立连接、获取权限、维持和管理连接。
+- 连接命令包括用户名、密码等认证信息。
+- 连接成功后,权限判断逻辑依赖于此时读到的权限,即使后续权限被修改,已建立的连接权限不变。
+
+> Q:在使用中减少连接动作尽量使用长连接,但全部使用长连接后,有些时候MySQL占用内存涨的特别快?
+
+## 查询缓存
+
+- 查询缓存用于存储之前执行过的语句及其结果,以提高查询效率。
+- 但由于查询缓存的频繁失效和命中率低,大多数情况下建议不使用查询缓存。
+- MySQL 8.0版本中,查询缓存功能被完全移除。
+
+> 查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。
+
+> mysql> SELECT SQL_CACHE * FROM T WHERE id=10;
+
+## 分析器
+
+- 分析器对SQL语句进行词法分析和语法分析,识别语句中的字符串和语法规则。
+- 如果SQL语句有语法错误,会提示错误信息。
+
+> 词法分析,识别出SQL语句各个字符分别是什么代表什么,比如把字符串T识别成表T,把字符串id识别成列ID。
+> 语法分析,根据词法分析的结果,语法分析器再根据语法规则,判断输入的SQL语句是否满足MySQL语法。 + +## 优化器 + +- 优化器决定使用哪个索引或表的连接顺序,以提高执行效率。 +- 优化器的选择可能影响查询性能。 + +> mysql> SELECT * FROM t1 JOIN t2 USING(id) WHERE t1.c=10 AND t2.d=20;
+> 既可以先从表 t1 里面取出 c=10 的记录的 ID 值,再根据 ID 值关联到表 t2,再判断 t2 里面 d 的值是否等于 20。
+> 也可以先从表 t2 里面取出 d=20 的记录的 ID 值,再根据 ID 值关联到 t1,再判断 t1 里面 c 的值是否等于 10。
+> 优化器的作用就是决定选择哪种方案,以便达到最佳效率。 + +## 执行器 + +- 执行器根据分析器和优化器的结果执行SQL语句。 +- 执行过程中,会检查权限,并根据表的存储引擎使用相应的接口。 +- 对于没有索引的表,执行器会遍历表中的每一行来查找满足条件的数据。 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\346\210\221\345\217\252\346\224\271\344\270\200\350\241\214\347\232\204\350\257\255\345\217\245\357\274\214\351\224\201\350\277\231\344\271\210\345\244\232.md" "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\346\210\221\345\217\252\346\224\271\344\270\200\350\241\214\347\232\204\350\257\255\345\217\245\357\274\214\351\224\201\350\277\231\344\271\210\345\244\232.md" new file mode 100644 index 0000000..96d0208 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\346\210\221\345\217\252\346\224\271\344\270\200\350\241\214\347\232\204\350\257\255\345\217\245\357\274\214\351\224\201\350\277\231\344\271\210\345\244\232.md" @@ -0,0 +1 @@ +# 为什么我只改一行的语句,锁这么多 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\346\210\221\345\217\252\346\237\245\344\270\200\350\241\214\347\232\204\350\257\255\345\217\245\357\274\214\344\271\237\346\211\247\350\241\214\350\277\231\344\271\210\346\205\242.md" "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\346\210\221\345\217\252\346\237\245\344\270\200\350\241\214\347\232\204\350\257\255\345\217\245\357\274\214\344\271\237\346\211\247\350\241\214\350\277\231\344\271\210\346\205\242.md" new file mode 100644 index 0000000..704f875 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\346\210\221\345\217\252\346\237\245\344\270\200\350\241\214\347\232\204\350\257\255\345\217\245\357\274\214\344\271\237\346\211\247\350\241\214\350\277\231\344\271\210\346\205\242.md" @@ -0,0 +1 @@ +# 为什么我只查一行的语句,也执行这么慢 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\346\210\221\347\232\204MySQL\344\274\232\346\212\226\344\270\200\344\270\213.md" "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\346\210\221\347\232\204MySQL\344\274\232\346\212\226\344\270\200\344\270\213.md" new file mode 100644 index 0000000..442d136 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\346\210\221\347\232\204MySQL\344\274\232\346\212\226\344\270\200\344\270\213.md" @@ -0,0 +1 @@ +# 为什么我的MySQL会抖一下 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\350\241\250\346\225\260\346\215\256\345\210\240\346\216\211\344\270\200\345\215\212\357\274\214\350\241\250\346\226\207\344\273\266\345\244\247\345\260\217\344\270\215\345\217\230.md" "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\350\241\250\346\225\260\346\215\256\345\210\240\346\216\211\344\270\200\345\215\212\357\274\214\350\241\250\346\226\207\344\273\266\345\244\247\345\260\217\344\270\215\345\217\230.md" new file mode 100644 index 0000000..6aa5c1a --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\350\241\250\346\225\260\346\215\256\345\210\240\346\216\211\344\270\200\345\215\212\357\274\214\350\241\250\346\226\207\344\273\266\345\244\247\345\260\217\344\270\215\345\217\230.md" @@ -0,0 +1 @@ +# 为什么表数据删掉一半,表文件大小不变 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\350\277\231\344\272\233SQL\350\257\255\345\217\245\351\200\273\350\276\221\347\233\270\345\220\214\357\274\214\346\200\247\350\203\275\345\215\264\345\267\256\345\274\202\345\267\250\345\244\247.md" "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\350\277\231\344\272\233SQL\350\257\255\345\217\245\351\200\273\350\276\221\347\233\270\345\220\214\357\274\214\346\200\247\350\203\275\345\215\264\345\267\256\345\274\202\345\267\250\345\244\247.md" new file mode 100644 index 0000000..c5abe3a --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\344\270\272\344\273\200\344\271\210\350\277\231\344\272\233SQL\350\257\255\345\217\245\351\200\273\350\276\221\347\233\270\345\220\214\357\274\214\346\200\247\350\203\275\345\215\264\345\267\256\345\274\202\345\267\250\345\244\247.md" @@ -0,0 +1 @@ +# 为什么这些SQL语句逻辑相同,性能却差异巨大 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\344\270\273\345\272\223\345\207\272\351\227\256\351\242\230\344\272\206\357\274\214\344\273\216\345\272\223\346\200\216\344\271\210\345\212\236.md" "b/docs/\345\255\230\345\202\250/\344\270\273\345\272\223\345\207\272\351\227\256\351\242\230\344\272\206\357\274\214\344\273\216\345\272\223\346\200\216\344\271\210\345\212\236.md" new file mode 100644 index 0000000..9fd3598 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\344\270\273\345\272\223\345\207\272\351\227\256\351\242\230\344\272\206\357\274\214\344\273\216\345\272\223\346\200\216\344\271\210\345\212\236.md" @@ -0,0 +1 @@ +# 主库出问题了,从库怎么办 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\344\272\213\345\212\241\345\210\260\345\272\225\346\230\257\351\232\224\347\246\273\347\232\204\350\277\230\346\230\257\344\270\215\351\232\224\347\246\273\347\232\204.md" "b/docs/\345\255\230\345\202\250/\344\272\213\345\212\241\345\210\260\345\272\225\346\230\257\351\232\224\347\246\273\347\232\204\350\277\230\346\230\257\344\270\215\351\232\224\347\246\273\347\232\204.md" new file mode 100644 index 0000000..12639a6 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\344\272\213\345\212\241\345\210\260\345\272\225\346\230\257\351\232\224\347\246\273\347\232\204\350\277\230\346\230\257\344\270\215\351\232\224\347\246\273\347\232\204.md" @@ -0,0 +1 @@ +# 事务到底是隔离的还是不隔离的 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\344\272\213\345\212\241\351\232\224\347\246\273-\344\270\272\344\273\200\344\271\210\344\275\240\346\224\271\344\272\206\346\210\221\350\277\230\347\234\213\344\270\215\350\247\201.md" "b/docs/\345\255\230\345\202\250/\344\272\213\345\212\241\351\232\224\347\246\273-\344\270\272\344\273\200\344\271\210\344\275\240\346\224\271\344\272\206\346\210\221\350\277\230\347\234\213\344\270\215\350\247\201.md" new file mode 100644 index 0000000..668afd5 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\344\272\213\345\212\241\351\232\224\347\246\273-\344\270\272\344\273\200\344\271\210\344\275\240\346\224\271\344\272\206\346\210\221\350\277\230\347\234\213\344\270\215\350\247\201.md" @@ -0,0 +1,107 @@ +# 事务隔离:为什么你改了我还看不见? + +提到事务,你肯定不陌生,和数据库打交道的时候,我们总是会用到事务。最经典的例子就是转账,你要给朋友小王转 100 块钱,而此时你的银行卡只有 100 块钱。 + +转账过程具体到程序里会有一系列的操作,比如查询余额、做加减法、更新余额等,这些操作必须保证是一体的,不然等程序查完之后,还没做减法之前,你这 100 块钱,完全可以借着这个时间差再查一次,然后再给另外一个朋友转账,如果银行这么整,不就乱了么?这时就要用到“事务”这个概念了。 + +简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在 MySQL 中,事务支持是在引擎层实现的。你现在知道,MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoDB 取代的重要原因之一。 + +今天的文章里,我将会以 InnoDB 为例,剖析 MySQL 在事务支持方面的特定实现,并基于原理给出相应的实践建议,希望这些案例能加深你对 MySQL 事务原理的理解。 + +## 隔离性与隔离级别 + +提到事务,你肯定会想到 ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性),今天我们就来说说其中 I,也就是“隔离性”。 + +当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。 + +在谈隔离级别之前,你首先要知道,你隔离得越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。SQL 标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )。下面我逐一为你解释: + +- 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。 +- 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。 +- 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。 +- 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。 + +其中“读提交”和“可重复读”比较难理解,所以我用一个例子说明这几种隔离级别。假设数据表 T 中只有一列,其中一行的值为 1,下面是按照时间顺序执行两个事务的行为。 + +> mysql> create table T(c int) engine=InnoDB;
+> mysql> insert into T(c) values(1); + +![事务提交](./assets/事务隔离-为什么你改了我还看不见-事务提交.png) + +我们来看看在不同的隔离级别下,事务 A 会有哪些不同的返回结果,也就是图里面 V1、V2、V3 的返回值分别是什么。 + +- 若隔离级别是“读未提交”, 则 V1 的值就是 2。这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。 +- 若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。 +- 若隔离级别是“可重复读”,则 V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。 +- 若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。 + +在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在“读提交”隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的。这里需要注意的是,“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;而“串行化”隔离级别下直接用加锁的方式来避免并行访问。 + +我们可以看到在不同的隔离级别下,数据库行为是有所不同的。Oracle 数据库的默认隔离级别其实就是“读提交”,因此对于一些从 Oracle 迁移到 MySQL 的应用,为保证数据库隔离级别的一致,你一定要记得将 MySQL 的隔离级别设置为“读提交”。 + +配置的方式是,将启动参数 transaction-isolation 的值设置成 READ-COMMITTED。你可以用 show variables 来查看当前的值。 + +```text +mysql> show variables like 'transaction_isolation'; + ++-----------------------+----------------+ + +| Variable_name | Value | + ++-----------------------+----------------+ + +| transaction_isolation | READ-COMMITTED | + ++-----------------------+----------------+ +``` + +总结来说,存在即合理,哪个隔离级别都有它自己的使用场景,你要根据自己的业务情况来定。我想你可能会问那什么时候需要“可重复读”的场景呢?我们来看一个数据校对逻辑的案例。 + +假设你在管理一个个人银行账户表。一个表存了每个月月底的余额,一个表存了账单明细。这时候你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。 + +这时候使用“可重复读”隔离级别就很方便。事务启动时的视图可以认为是静态的,不受其他事务更新的影响。 + +## 事务隔离的实现 + +理解了事务的隔离级别,我们再来看看事务隔离具体是怎么实现的。这里我们展开说明“可重复读”。 + +在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。 + +假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。 + +![回滚日志记录](./assets/事务隔离-为什么你改了我还看不见-回滚日志记录.png) + +当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。 + +同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。 + +你一定会问,回滚日志总不能一直保留吧,什么时候删除呢?答案是,在不需要的时候才删除。也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。 + +什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。 + +基于上面的说明,我们来讨论一下为什么建议你尽量不要使用长事务。 + +长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。 + +在 MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小。我见过数据只有 20GB,而回滚段有 200GB 的库。最终只好为了清理回滚段,重建整个库。 + +除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。 + +## 事务的启动方式 + +如前面所述,长事务有这些潜在风险,我当然是建议你尽量避免。其实很多时候业务开发同学并不是有意使用长事务,通常是由于误用所致。MySQL 的事务启动方式有以下几种: + +1、显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。 + +2、set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。 +有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。 + +因此,我会建议你总是使用 set autocommit=1, 通过显式语句的方式来启动事务。 + +但是有的开发同学会纠结“多一次交互”的问题。对于一个需要频繁使用事务的业务,第二种方式每个事务在开始时都不需要主动执行一次 “begin”,减少了语句的交互次数。如果你也有这个顾虑,我建议你使用 commit work and chain 语法。 + +在 autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。 + +你可以在 information_schema 库的 innodb_trx 这个表中查询长事务,比如下面这个语句,用于查找持续时间超过 60s 的事务。 + +> select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\345\205\250\345\261\200\351\224\201\345\222\214\350\241\250\351\224\201-\347\273\231\350\241\250\345\212\240\344\270\252\345\255\227\346\256\265\346\200\216\344\271\210\346\234\211\350\277\231\344\271\210\345\244\232\351\230\273\347\242\215.md" "b/docs/\345\255\230\345\202\250/\345\205\250\345\261\200\351\224\201\345\222\214\350\241\250\351\224\201-\347\273\231\350\241\250\345\212\240\344\270\252\345\255\227\346\256\265\346\200\216\344\271\210\346\234\211\350\277\231\344\271\210\345\244\232\351\230\273\347\242\215.md" new file mode 100644 index 0000000..4c70adb --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\345\205\250\345\261\200\351\224\201\345\222\214\350\241\250\351\224\201-\347\273\231\350\241\250\345\212\240\344\270\252\345\255\227\346\256\265\346\200\216\344\271\210\346\234\211\350\277\231\344\271\210\345\244\232\351\230\273\347\242\215.md" @@ -0,0 +1 @@ +# 全局锁和表锁:给表加个字段怎么有这么多阻碍 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\345\244\207\345\272\223\344\270\272\344\273\200\344\271\210\344\274\232\345\273\266\350\277\237\345\245\275\345\207\240\344\270\252\345\260\217\346\227\266.md" "b/docs/\345\255\230\345\202\250/\345\244\207\345\272\223\344\270\272\344\273\200\344\271\210\344\274\232\345\273\266\350\277\237\345\245\275\345\207\240\344\270\252\345\260\217\346\227\266.md" new file mode 100644 index 0000000..5374561 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\345\244\207\345\272\223\344\270\272\344\273\200\344\271\210\344\274\232\345\273\266\350\277\237\345\245\275\345\207\240\344\270\252\345\260\217\346\227\266.md" @@ -0,0 +1 @@ +# 备库为什么会延迟好几个小时 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\345\246\202\344\275\225\345\210\244\346\226\255\344\270\200\344\270\252\346\225\260\346\215\256\345\272\223\346\230\257\344\270\215\346\230\257\345\207\272\351\227\256\351\242\230\344\272\206.md" "b/docs/\345\255\230\345\202\250/\345\246\202\344\275\225\345\210\244\346\226\255\344\270\200\344\270\252\346\225\260\346\215\256\345\272\223\346\230\257\344\270\215\346\230\257\345\207\272\351\227\256\351\242\230\344\272\206.md" new file mode 100644 index 0000000..6a946bf --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\345\246\202\344\275\225\345\210\244\346\226\255\344\270\200\344\270\252\346\225\260\346\215\256\345\272\223\346\230\257\344\270\215\346\230\257\345\207\272\351\227\256\351\242\230\344\272\206.md" @@ -0,0 +1 @@ +# 如何判断一个数据库是不是出问题了 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\345\246\202\344\275\225\346\255\243\347\241\256\345\234\260\346\230\276\347\244\272\351\232\217\346\234\272\346\266\210\346\201\257.md" "b/docs/\345\255\230\345\202\250/\345\246\202\344\275\225\346\255\243\347\241\256\345\234\260\346\230\276\347\244\272\351\232\217\346\234\272\346\266\210\346\201\257.md" new file mode 100644 index 0000000..d0144b6 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\345\246\202\344\275\225\346\255\243\347\241\256\345\234\260\346\230\276\347\244\272\351\232\217\346\234\272\346\266\210\346\201\257.md" @@ -0,0 +1 @@ +# 如何正确地显示随机消息 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\345\271\273\350\257\273\346\230\257\344\273\200\344\271\210\357\274\214\345\271\273\350\257\273\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230.md" "b/docs/\345\255\230\345\202\250/\345\271\273\350\257\273\346\230\257\344\273\200\344\271\210\357\274\214\345\271\273\350\257\273\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230.md" new file mode 100644 index 0000000..0240110 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\345\271\273\350\257\273\346\230\257\344\273\200\344\271\210\357\274\214\345\271\273\350\257\273\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230.md" @@ -0,0 +1 @@ +# 幻读是什么,幻读有什么问题 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\346\200\216\344\271\210\347\273\231\345\255\227\347\254\246\344\270\262\345\255\227\346\256\265\345\212\240\347\264\242\345\274\225.md" "b/docs/\345\255\230\345\202\250/\346\200\216\344\271\210\347\273\231\345\255\227\347\254\246\344\270\262\345\255\227\346\256\265\345\212\240\347\264\242\345\274\225.md" new file mode 100644 index 0000000..bd51313 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\346\200\216\344\271\210\347\273\231\345\255\227\347\254\246\344\270\262\345\255\227\346\256\265\345\212\240\347\264\242\345\274\225.md" @@ -0,0 +1 @@ +# 怎么给字符串字段加索引 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\346\231\256\351\200\232\347\264\242\345\274\225\345\222\214\345\224\257\344\270\200\347\264\242\345\274\225\357\274\214\345\272\224\350\257\245\346\200\216\344\271\210\351\200\211\346\213\251.md" "b/docs/\345\255\230\345\202\250/\346\231\256\351\200\232\347\264\242\345\274\225\345\222\214\345\224\257\344\270\200\347\264\242\345\274\225\357\274\214\345\272\224\350\257\245\346\200\216\344\271\210\351\200\211\346\213\251.md" new file mode 100644 index 0000000..6fc4b2c --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\346\231\256\351\200\232\347\264\242\345\274\225\345\222\214\345\224\257\344\270\200\347\264\242\345\274\225\357\274\214\345\272\224\350\257\245\346\200\216\344\271\210\351\200\211\346\213\251.md" @@ -0,0 +1 @@ +# 普通索引和唯一索引,应该怎么选择 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212.md" "b/docs/\345\255\230\345\202\250/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212.md" new file mode 100644 index 0000000..142bd37 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\212.md" @@ -0,0 +1,144 @@ +# 深入浅出索引(上) + +提到数据库索引,我想你并不陌生,在日常工作中会经常接触到。比如某一个 SQL 查询比较慢,分析完原因之后,你可能就会说“给某个字段加个索引吧”之类的解决方案。但到底什么是索引,索引又是如何工作的呢?今天就让我们一起来聊聊这个话题吧。 + +数据库索引的内容比较多,我分成了上下两篇文章。索引是数据库系统里面最重要的概念之一,所以我希望你能够耐心看完。在后面的实战文章中,我也会经常引用这两篇文章中提到的知识点,加深你对数据库索引的理解。 + +一句话简单来说,索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。一本 500 页的书,如果你想快速找到其中的某一个知识点,在不借助目录的情况下,那我估计你可得找一会儿。同样,对于数据库的表而言,索引其实就是它的“目录”。 + +## 索引的常见模型 + +索引的出现是为了提高查询效率,但是实现索引的方式却有很多种,所以这里也就引入了索引模型的概念。可以用于提高读写效率的数据结构很多,这里我先给你介绍三种常见、也比较简单的数据结构,它们分别是哈希表、有序数组和搜索树。 + +下面我主要从使用的角度,为你简单分析一下这三种模型的区别。 + +哈希表是一种以键 - 值(key-value)存储数据的结构,我们只要输入待查找的值即 key,就可以找到其对应的值即 Value。哈希的思路很简单,把值放在数组里,用一个哈希函数把 key 换算成一个确定的位置,然后把 value 放在数组的这个位置。 + +不可避免地,多个 key 值经过哈希函数的换算,会出现同一个值的情况。处理这种情况的一种方法是,拉出一个链表。 + +假设,你现在维护着一个身份证信息和姓名的表,需要根据身份证号查找对应的名字,这时对应的哈希索引的示意图如下所示: + +![哈希索引](./assets/深入浅出索引-上-哈希索引.png) + +图中,User2 和 User4 根据身份证号算出来的值都是 N,但没关系,后面还跟了一个链表。假设,这时候你要查 ID_card_n2 对应的名字是什么,处理步骤就是:首先,将 ID_card_n2 通过哈希函数算出 N;然后,按顺序遍历,找到 User2。 + +需要注意的是,图中四个 ID_card_n 的值并不是递增的,这样做的好处是增加新的 User 时速度会很快,只需要往后追加。但缺点是,因为不是有序的,所以哈希索引做区间查询的速度是很慢的。 + +你可以设想下,如果你现在要找身份证号在 [ID_card_X, ID_card_Y] 这个区间的所有用户,就必须全部扫描一遍了。 + +所以,哈希表这种结构适用于只有等值查询的场景,比如 Memcached 及其他一些 NoSQL 引擎。 + +而有序数组在等值查询和范围查询场景中的性能就都非常优秀。还是上面这个根据身份证号查名字的例子,如果我们使用有序数组来实现的话,示意图如下所示: + +![有序数组](./assets/深入浅出索引-上-有序数组.png) + +这里我们假设身份证号没有重复,这个数组就是按照身份证号递增的顺序保存的。这时候如果你要查 ID_card_n2 对应的名字,用二分法就可以快速得到,这个时间复杂度是 O(log(N))。 + +同时很显然,这个索引结构支持范围查询。你要查身份证号在 [ID_card_X, ID_card_Y] 区间的 User,可以先用二分法找到 ID_card_X(如果不存在 ID_card_X,就找到大于 ID_card_X 的第一个 User),然后向右遍历,直到查到第一个大于 ID_card_Y 的身份证号,退出循环。 + +如果仅仅看查询效率,有序数组就是最好的数据结构了。但是,在需要更新数据的时候就麻烦了,你往中间插入一个记录就必须得挪动后面所有的记录,成本太高。 + +所以,有序数组索引只适用于静态存储引擎,比如你要保存的是 2017 年某个城市的所有人口信息,这类不会再修改的数据。 + +二叉搜索树也是课本里的经典数据结构了。还是上面根据身份证号查名字的例子,如果我们用二叉搜索树来实现的话,示意图如下所示: + +![二叉搜索树](./assets/深入浅出索引-上-二叉搜索树.png) + +二叉搜索树的特点是:每个节点的左儿子小于父节点,父节点又小于右儿子。这样如果你要查 ID_card_n2 的话,按照图中的搜索顺序就是按照 UserA -> UserC -> UserF -> User2 这个路径得到。这个时间复杂度是 O(log(N))。 + +当然为了维持 O(log(N)) 的查询复杂度,你就需要保持这棵树是平衡二叉树。为了做这个保证,更新的时间复杂度也是 O(log(N))。 + +树可以有二叉,也可以有多叉。多叉树就是每个节点有多个儿子,儿子之间的大小保证从左到右递增。二叉树是搜索效率最高的,但是实际上大多数的数据库存储却并不使用二叉树。其原因是,索引不止存在内存中,还要写到磁盘上。 + +你可以想象一下一棵 100 万节点的平衡二叉树,树高 20。一次查询可能需要访问 20 个数据块。在机械硬盘时代,从磁盘随机读一个数据块需要 10 ms 左右的寻址时间。也就是说,对于一个 100 万行的表,如果使用二叉树来存储,单独访问一个行可能需要 20 个 10 ms 的时间,这个查询可真够慢的。 + +为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。那么,我们就不应该使用二叉树,而是要使用“N 叉”树。这里,“N 叉”树中的“N”取决于数据块的大小。 + +以 InnoDB 的一个整数字段索引为例,这个 N 差不多是 1200。这棵树高是 4 的时候,就可以存 1200 的 3 次方个值,这已经 17 亿了。考虑到树根的数据块总是在内存中的,一个 10 亿行的表上一个整数字段的索引,查找一个值最多只需要访问 3 次磁盘。其实,树的第二层也有很大概率在内存中,那么访问磁盘的平均次数就更少了。 + +N 叉树由于在读写上的性能优点,以及适配磁盘的访问模式,已经被广泛应用在数据库引擎中了。 + +不管是哈希还是有序数组,或者 N 叉树,它们都是不断迭代、不断优化的产物或者解决方案。数据库技术发展到今天,跳表、LSM 树等数据结构也被用于引擎设计中,这里我就不再一一展开了。 + +你心里要有个概念,数据库底层存储的核心就是基于这些数据模型的。每碰到一个新数据库,我们需要先关注它的数据模型,这样才能从理论上分析出这个数据库的适用场景。 + +截止到这里,我用了半篇文章的篇幅和你介绍了不同的数据结构,以及它们的适用场景,你可能会觉得有些枯燥。但是,我建议你还是要多花一些时间来理解这部分内容,毕竟这是数据库处理数据的核心概念之一,在分析问题的时候会经常用到。当你理解了索引的模型后,就会发现在分析问题的时候会有一个更清晰的视角,体会到引擎设计的精妙之处。 + +现在,我们一起进入相对偏实战的内容吧。 + +在 MySQL 中,索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。而即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。由于 InnoDB 存储引擎在 MySQL 数据库中使用最为广泛,所以下面我就以 InnoDB 为例,和你分析一下其中的索引模型。 + +## InnoDB 的索引模型 + +在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。又因为前面我们提到的,InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。 + +每一个索引在 InnoDB 里面对应一棵 B+ 树。 + +假设,我们有一个主键列为 ID 的表,表中有字段 k,并且在 k 上有索引。 + +这个表的建表语句是: + +```text +mysql> create table T( +id int primary key, +k int not null, +name varchar(16), +index (k))engine=InnoDB; +``` + +表中 R1~R5 的 (ID,k) 值分别为 (100,1)、(200,2)、(300,3)、(500,5) 和 (600,6),两棵树的示例示意图如下。 + +![InnoDB的索引组织结构](./assets/深入浅出索引-上-InnoDB的索引组织结构.png) + +从图中不难看出,根据叶子节点的内容,索引类型分为主键索引和非主键索引。 + +主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。 + +非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。 + +根据上面的索引结构说明,我们来讨论一个问题:基于主键索引和普通索引的查询有什么区别? + +- 如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+ 树; +- 如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为回表。 + +也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询。 + +## 索引维护 + +B+ 树为了维护索引有序性,在插入新值的时候需要做必要的维护。以上面这个图为例,如果插入新的行 ID 值为 700,则只需要在 R5 的记录后面插入一个新记录。如果新插入的 ID 值为 400,就相对麻烦了,需要逻辑上挪动后面的数据,空出位置。 + +而更糟的情况是,如果 R5 所在的数据页已经满了,根据 B+ 树的算法,这时候需要申请一个新的数据页,然后挪动部分数据过去。这个过程称为页分裂。在这种情况下,性能自然会受影响。 + +除了性能外,页分裂操作还影响数据页的利用率。原本放在一个页的数据,现在分到两个页中,整体空间利用率降低大约 50%。 + +当然有分裂就有合并。当相邻两个页由于删除了数据,利用率很低之后,会将数据页做合并。合并的过程,可以认为是分裂过程的逆过程。 + +基于上面的索引维护过程说明,我们来讨论一个案例: + +> 你可能在一些建表规范里面见到过类似的描述,要求建表语句里一定要有自增主键。当然事无绝对,我们来分析一下哪些场景下应该使用自增主键,而哪些场景下不应该。 + +自增主键是指自增列上定义的主键,在建表语句中一般是这么定义的: NOT NULL PRIMARY KEY AUTO_INCREMENT。 + +插入新记录的时候可以不指定 ID 的值,系统会获取当前 ID 最大值加 1 作为下一条记录的 ID 值。 + +也就是说,自增主键的插入数据模式,正符合了我们前面提到的递增插入的场景。每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。 + +而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。 + +除了考虑性能外,我们还可以从存储空间的角度来看。假设你的表中确实有一个唯一字段,比如字符串类型的身份证号,那应该用身份证号做主键,还是用自增字段做主键呢? + +由于每个非主键索引的叶子节点上都是主键的值。如果用身份证号做主键,那么每个二级索引的叶子节点占用约 20 个字节,而如果用整型做主键,则只要 4 个字节,如果是长整型(bigint)则是 8 个字节。 + +显然,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。 + +所以,从性能和存储空间方面考量,自增主键往往是更合理的选择。 + +有没有什么场景适合用业务字段直接做主键的呢?还是有的。比如,有些业务的场景需求是这样的: + +只有一个索引; +该索引必须是唯一索引。 +你一定看出来了,这就是典型的 KV 场景。 + +由于没有其他索引,所以也就不用考虑其他索引的叶子节点大小的问题。 + +这时候我们就要优先考虑上一段提到的“尽量使用主键查询”原则,直接将这个索引设置为主键,可以避免每次查询需要搜索两棵树。 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\213.md" "b/docs/\345\255\230\345\202\250/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\213.md" new file mode 100644 index 0000000..b2e4f81 --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\346\267\261\345\205\245\346\265\205\345\207\272\347\264\242\345\274\225-\344\270\213.md" @@ -0,0 +1 @@ +# 深入浅出索引(下) \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\350\241\214\351\224\201\345\212\237\350\277\207-\346\200\216\344\271\210\345\207\217\345\260\221\350\241\214\351\224\201\345\257\271\346\200\247\350\203\275\347\232\204\345\275\261\345\223\215.md" "b/docs/\345\255\230\345\202\250/\350\241\214\351\224\201\345\212\237\350\277\207-\346\200\216\344\271\210\345\207\217\345\260\221\350\241\214\351\224\201\345\257\271\346\200\247\350\203\275\347\232\204\345\275\261\345\223\215.md" new file mode 100644 index 0000000..bdb219a --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\350\241\214\351\224\201\345\212\237\350\277\207-\346\200\216\344\271\210\345\207\217\345\260\221\350\241\214\351\224\201\345\257\271\346\200\247\350\203\275\347\232\204\345\275\261\345\223\215.md" @@ -0,0 +1 @@ +# 行锁功过:怎么减少行锁对性能的影响 \ No newline at end of file diff --git "a/docs/\345\255\230\345\202\250/\350\257\273\345\206\231\345\210\206\347\246\273\346\234\211\345\223\252\344\272\233\345\235\221.md" "b/docs/\345\255\230\345\202\250/\350\257\273\345\206\231\345\210\206\347\246\273\346\234\211\345\223\252\344\272\233\345\235\221.md" new file mode 100644 index 0000000..3ed3f7f --- /dev/null +++ "b/docs/\345\255\230\345\202\250/\350\257\273\345\206\231\345\210\206\347\246\273\346\234\211\345\223\252\344\272\233\345\235\221.md" @@ -0,0 +1 @@ +# 读写分离有哪些坑 \ No newline at end of file diff --git "a/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-01-tiobe-2025-dec.png" "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-01-tiobe-2025-dec.png" new file mode 100644 index 0000000..64ab6d4 Binary files /dev/null and "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-01-tiobe-2025-dec.png" differ diff --git "a/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-02-21\345\244\251\345\255\246\351\200\232PHP.png" "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-02-21\345\244\251\345\255\246\351\200\232PHP.png" new file mode 100644 index 0000000..e83d42c Binary files /dev/null and "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-02-21\345\244\251\345\255\246\351\200\232PHP.png" differ diff --git "a/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-03-\346\273\264\346\273\264PHP\345\262\227\344\275\215.png" "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-03-\346\273\264\346\273\264PHP\345\262\227\344\275\215.png" new file mode 100644 index 0000000..823dc3c Binary files /dev/null and "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-03-\346\273\264\346\273\264PHP\345\262\227\344\275\215.png" differ diff --git "a/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-04-\344\272\272\347\224\237\346\204\217\344\271\2111.jpg" "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-04-\344\272\272\347\224\237\346\204\217\344\271\2111.jpg" new file mode 100644 index 0000000..27ad8b6 Binary files /dev/null and "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-04-\344\272\272\347\224\237\346\204\217\344\271\2111.jpg" differ diff --git "a/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-05-\344\272\272\347\224\237\346\204\217\344\271\2112.jpg" "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-05-\344\272\272\347\224\237\346\204\217\344\271\2112.jpg" new file mode 100644 index 0000000..0a556f5 Binary files /dev/null and "b/docs/\347\225\252\345\244\226/assets/\347\274\226\347\240\201\347\203\255\346\203\205-05-\344\272\272\347\224\237\346\204\217\344\271\2112.jpg" differ diff --git "a/docs/\347\225\252\345\244\226/\344\275\240\347\232\204\347\274\226\347\240\201\347\203\255\346\203\205\346\230\257\345\246\202\344\275\225\346\266\210\351\200\200\347\232\204.md" "b/docs/\347\225\252\345\244\226/\344\275\240\347\232\204\347\274\226\347\240\201\347\203\255\346\203\205\346\230\257\345\246\202\344\275\225\346\266\210\351\200\200\347\232\204.md" new file mode 100644 index 0000000..6bd5d55 --- /dev/null +++ "b/docs/\347\225\252\345\244\226/\344\275\240\347\232\204\347\274\226\347\240\201\347\203\255\346\203\205\346\230\257\345\246\202\344\275\225\346\266\210\351\200\200\347\232\204.md" @@ -0,0 +1,131 @@ +# 你的编码热情是如何消退的? + +本文基于 PHP 从业者**编码热情会随时间逐渐消退**这是事实,探讨导致消退的原因,分享自己重拾热情的方式,抛砖引玉,**助你找到自己重拾热情的方式**。全文共计 **3163 字**,阅读时间约 **16 分钟**,作者:凌枫。如果文章对你有帮助,请关注和转发,谢谢! + +## 一、热情消退现象 + +作为一名 PHP 开发者,你是否察觉到曾经那种初学时的热情在逐渐消退?从渴望编写代码,到逐渐将编程视为例行公事,而简单归结为:**提不起劲?**这种转变在技术领域尤为常见。 + +**技术学习动力匮乏**是热情消退的首要信号。当发现自己对新框架和语言更新不再感兴趣,宁愿重复使用旧技术而不愿探索新方案时,就需要警惕了。**工作倦怠**则表现为将编程视为重复性劳动,创造力明显下降,只求完成功能而不追求代码优美。**创新意识减弱**则体现在满足于完成需求而非追求技术卓越,对代码质量和架构设计失去往日的热情。 + +许多 PHP 开发者都经历过这样的阶段:曾经乐在其中的代码实现变成了单调的复制粘贴,问题解决能力退化为机械式的功能堆砌。更令人担忧的是,PHP 在全球编程语言生态中的地位正在经历显著变化,TIOBE 指数显示 PHP 的排名已跌至历史最低点第 16 位(2025 年 12 月),另外 PHP 高端岗位均面临 Java 和 Go 的直接竞争,这些原因无疑加速了 PHP 开发者的热情消退。 + +![tiobe 排名—PHP 跌落至第 16 名](./assets/编码热情-01-tiobe-2025-dec.png) + +## 二、原因剖析 + +### 1、技术学习热情消退 + +PHP 开发者技术学习热情的消退,往往始于技术成长的“S型曲线”陷阱。**许多开发者在职业生涯初期经历了急速成长后,会进入一个漫长的平台期。**这个阶段最显著的特点是:缺乏有挑战性的任务,日常工作中充斥着重复的 CRUD(增删改查)业务逻辑开发,难以接触到高并发、分布式系统等更有深度的技术场景。 + +随着升职加薪节奏变缓,技术成长的经济回报边际效应递减,进一步削弱了学习动力。当技术能力达到一定水平后,单纯的薪资激励不再像早期那样有效,而 PHP 开发者在中小型企业的薪资上限通常不如中大型企业就职的 Java 或 Go 等语言。此外,家庭琐事的打扰也不容忽视——随着年龄增长,开发者需要投入更多时间在家庭责任上,用于深度学习和思考的完整时间块被碎片化。 +更深层次的问题是,部分 PHP 开发者陷入了“工具化”的舒适区,仅将编程视为谋生手段而非创造活动。当编码不再带来成就感,而沦为机械式的任务完成时,热情自然逐渐消退。 + +![21 天学通 PHP,畅销 16 万册](./assets/编码热情-02-21天学通PHP.png) + +### 2、语言局限性与生态劣势 + +在生态系统方面,PHP 的局限性尤为明显。相比 Java 在桌面软件、Android、大数据、金融系统、电商平台的成熟生态,Python 在 AI 和数据科学领域的主导地位,以及 Go 在云原生和基础服务器端软件方面的优势,PHP 的生态相对单一,主要集中在传统 Web 开发领域。这种生态劣势使得 PHP 开发者难以参与到技术发展的前沿领域中。语言本身的局限性。**相较 Java 语言,大厂成熟工程实践经验可直接复用到中小企业,进一步挤压 PHP 开发者的生存空间。** + +### 3、职业发展瓶颈与工作挑战 + +随着技术发展,PHP 开发者的职业路径面临显著挑战。许多 PHP 开发者长期局限于业务代码的实现,缺乏对系统架构、设计模式等深层技术原理的理解,这限制了职业发展空间。尤其是在小型软件公司或外包公司工作的开发者,很难有机会经历完整的大型项目开发。 +复杂项目经验的缺乏使不少 PHP 开发者难以胜任架构师等高级角色。随着移动互联网、云计算等新技术兴起,软件系统变得越来越复杂,大型网站对高并发、可用性的要求越来越高。而 PHP 在这些场景下的表现明显处于劣势。 +最终,**PHP 开发者为获得更高端岗位常常面临更换技术栈的艰难抉择。**大厂的核心高并发系统很少采用 PHP,而更多使用 Java 或 Go 等语言,这使得有志于进入一线互联网企业的 PHP 开发者不得不考虑转型。这种为职业发展而放弃擅长技术栈的抉择,本身就是热情消退的重要表现。 + +![滴滴,PHP 也只招聘到高级了](./assets/编码热情-03-滴滴PHP岗位.png) + +## 三、重拾方式:从消费者到生产者 + +面对热情消退的困境,探索出了一条转型之路:**从 PHP 消费者转变为 PHP 生产者。**这一转变不仅重燃对 PHP 开发的热情,更为职业发展开辟了新天地。 + +> PHP 消费者:依托现有游戏玩法,解决业务上的问题。PHP 生产者:质疑现有游戏玩法,设计游戏新玩法,解决业务上的问题。 + +### 1、深耕工程规范与最佳实践 + +突然意识到,提升工程规范是保持长期开发热情的关键。PHP 社区已形成了许多优秀实践,包括: + +* Composer:PHP 的依赖管理工具,可以快速安装、更新和管理项目依赖库。 + +* PSR 规范:社区制定的一系列 PHP 编码规范,涵盖自动加载、编码风格等方面。 + +* PHPStan:静态分析检查工具,帮助提前发现代码问题。 + +采用测试驱动开发(TDD) 模式后,代码质量显著提升,后续维护成本大大降低。同时,重视代码可读性,确保后续维护者都能轻松理解代码逻辑。 + +### 2、技术深度上的"蜕变" + +蜕变是深度优先的成长,是工匠精神的体现。先从简单的脚本开发转向注重软件设计原则,实践领域驱动设计(DDD)和整洁架构。这一转变使代码从"能用"升级到"优美且可维护"。 +参与开源项目是转变身份的重要一步。通过阅读高质量源代码并向资深开发者学习,逐渐从被动的代码使用者转变为积极的贡献者。当你的代码被广泛使用时,这种成就感极大地激发了进一步探索的动力。 + +### 3、技术广度上的"裂变" + +**裂变是广度优先的成长**,通过不断打破边界引发知识和影响力的链式反应。可行的裂变路径包括: + +* PHP + Vue/React(全栈开发)。 + +* PHP + Go(微服务架构)。 + +* DevOps 与容器化技术。 + +特别是通过 phpy 扩展,可以在 PHP 中直接调用 Python 的 AI 库,运行 AI 大模型推理和训练,这为 PHP 开发者打开了通向人工智能领域的大门。 + +### 4、打造个人生产链条 + +建立适合自己的开发环境是提升效率的基础。选择强大的 IDE(如 PhpStorm)可以显著提升编码体验,它们提供代码自动补全、语法检查、调试等功能。以及在工作中体验 VScode 并输出最佳实践;拥抱 cursor 等最新 AI 工具并输出 mdc 规则集。 +更重要的是,可以创建了个人工具集,开发专属的代码生成器、自动化测试脚本和部署工具。这些工具不仅解决日常开发中的痛点,还被团队其他成员采用,**真正实现了从代码"消费者"到价值"生产者"的转变**。 + +## 四、属于你的方式 + +**转型为生产者不是单一路径**,而是根据个人情况选择合适的成长模式。以下是几种可行的路径: + +### 1、"蜕变"优先路径 + +如果你感到迷茫和瓶颈,请优先选择 **"蜕变"** 。具体方式包括: + +* 深入理解 PHP 内核:学习 Zend 引擎、内存管理等底层原理。 + +* 研究优秀框架源码:阅读 Laravel、Symfony 等框架的源代码,理解设计模式应用。 + +* 参与开源项目:从解决 issue 开始,逐步参与代码审查和功能开发。 + +### 2、"裂变"优先路径 + +如果你感到厌倦和重复,可以尝试 **"裂变"** 。具体方向包括: + +* 全栈开发:学习 Vue.js、React 等前端技术,理解前后端协作。 + +* 微服务架构:探索 Go、Rust 等语言在特定场景下的应用。 + +* 技术管理:培养项目管理、团队协作能力,扩展职业边界。 + +### 3、螺旋式成长模型 + +理想的成长模型是螺旋式上升的,在"深度蜕变"与"广度裂变"之间不断循环。每个循环都让你站在更高维度: + +* 阶段一:专注蜕变,夯实 PHP 核心基础。 + +* 阶段二:触发裂变,拓展技术边界。 + +* 阶段三:在新领域再次蜕变,深耕新技术点。 + +* 阶段四:整合升华,形成 T 型或 π 型能力。 + +> T 型:有一项核心专长(深度),同时具备跨领域知识(广度),如:精通 PHP,又懂 Java / Go,帮助企业转型技术栈。 +> +> π 型:有至少两项专长(深度),同时具备跨领域知识(广度),如:精通 PHP,又懂其他语言,也懂业务,帮助企业在细分市场站稳脚跟。 + +## 五、结束语 + +PHP 开发者热情消退是一个复杂因素最终导致的现象,但并非不可逆转。**通过重新定义自己的技术定位,从被动的代码实现者转变为主动的价值创造者,我们完全可以重拾对 PHP 开发的热情。** + +"生产者"心态的核心在于,不再将 PHP 视为单纯的谋生工具,而是作为创造和表达的平台。当你开始构建自己的工具、参与开源项目、分享知识并探索新应用领域时,你会发现自己与 PHP 的关系发生了根本性变化。 + +从今天开始,选择一条适合自己的转型路径,迈出从消费者到生产者的第一步。**这不仅是技术层面的提升,更是职业态度和人生观的蜕变。** + +![请选择你的人生意义-图片1](./assets/编码热情-04-人生意义1.jpg) + +![请选择你的人生意义-图片2](./assets/编码热情-05-人生意义2.jpg) + +如果文章对你有帮助,请关注和转发,谢谢! +![qr](../assets/qr.png) diff --git "a/docs/\351\235\242\350\257\225/assets/\345\246\202\344\275\225\350\256\251\345\267\245\344\275\234\345\271\264\351\231\220\345\217\230\346\210\220\344\274\230\345\212\277-\345\276\267\351\233\267\347\246\217\346\226\257\346\250\241\345\236\213.png" "b/docs/\351\235\242\350\257\225/assets/\345\246\202\344\275\225\350\256\251\345\267\245\344\275\234\345\271\264\351\231\220\345\217\230\346\210\220\344\274\230\345\212\277-\345\276\267\351\233\267\347\246\217\346\226\257\346\250\241\345\236\213.png" new file mode 100644 index 0000000..3b37187 Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\345\246\202\344\275\225\350\256\251\345\267\245\344\275\234\345\271\264\351\231\220\345\217\230\346\210\220\344\274\230\345\212\277-\345\276\267\351\233\267\347\246\217\346\226\257\346\250\241\345\236\213.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\344\270\252\344\272\272\344\274\230\345\212\277.png" "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\344\270\252\344\272\272\344\274\230\345\212\277.png" new file mode 100644 index 0000000..d748247 Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\344\270\252\344\272\272\344\274\230\345\212\277.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\344\270\252\344\272\272\344\277\241\346\201\257\345\217\215\344\276\213.png" "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\344\270\252\344\272\272\344\277\241\346\201\257\345\217\215\344\276\213.png" new file mode 100644 index 0000000..432843a Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\344\270\252\344\272\272\344\277\241\346\201\257\345\217\215\344\276\213.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\344\270\252\344\272\272\344\277\241\346\201\257\347\244\272\344\276\213.png" "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\344\270\252\344\272\272\344\277\241\346\201\257\347\244\272\344\276\213.png" new file mode 100644 index 0000000..8eab36c Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\344\270\252\344\272\272\344\277\241\346\201\257\347\244\272\344\276\213.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\345\267\245\344\275\234\347\273\217\351\252\214.png" "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\345\267\245\344\275\234\347\273\217\351\252\214.png" new file mode 100644 index 0000000..9ca2938 Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\345\267\245\344\275\234\347\273\217\351\252\214.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\346\225\231\350\202\262\347\273\217\345\216\206.png" "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\346\225\231\350\202\262\347\273\217\345\216\206.png" new file mode 100644 index 0000000..c1fec36 Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\346\225\231\350\202\262\347\273\217\345\216\206.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\346\225\231\350\202\262\347\273\217\345\216\2062.png" "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\346\225\231\350\202\262\347\273\217\345\216\2062.png" new file mode 100644 index 0000000..4dc3fcf Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\346\225\231\350\202\262\347\273\217\345\216\2062.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\346\225\231\350\202\262\350\203\214\346\231\257.png" "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\346\225\231\350\202\262\350\203\214\346\231\257.png" new file mode 100644 index 0000000..73179f0 Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\346\225\231\350\202\262\350\203\214\346\231\257.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\347\256\200\345\216\206\346\250\241\347\211\210.png" "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\347\256\200\345\216\206\346\250\241\347\211\210.png" new file mode 100644 index 0000000..864311a Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206-\347\256\200\345\216\206\346\250\241\347\211\210.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\350\257\273\346\207\202\345\262\227\344\275\215\347\262\276\345\207\206\346\212\225\351\200\222-pm\346\213\233\350\201\230\350\246\201\346\261\202.png" "b/docs/\351\235\242\350\257\225/assets/\350\257\273\346\207\202\345\262\227\344\275\215\347\262\276\345\207\206\346\212\225\351\200\222-pm\346\213\233\350\201\230\350\246\201\346\261\202.png" new file mode 100644 index 0000000..ea04a2c Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\350\257\273\346\207\202\345\262\227\344\275\215\347\262\276\345\207\206\346\212\225\351\200\222-pm\346\213\233\350\201\230\350\246\201\346\261\202.png" differ diff --git "a/docs/\351\235\242\350\257\225/assets/\351\235\242\350\257\225\346\275\234\350\247\204\345\210\231-12\346\255\245\351\200\232\345\205\263\346\261\202\350\201\214\351\235\242\350\257\225.jpg" "b/docs/\351\235\242\350\257\225/assets/\351\235\242\350\257\225\346\275\234\350\247\204\345\210\231-12\346\255\245\351\200\232\345\205\263\346\261\202\350\201\214\351\235\242\350\257\225.jpg" new file mode 100644 index 0000000..5e5046e Binary files /dev/null and "b/docs/\351\235\242\350\257\225/assets/\351\235\242\350\257\225\346\275\234\350\247\204\345\210\231-12\346\255\245\351\200\232\345\205\263\346\261\202\350\201\214\351\235\242\350\257\225.jpg" differ diff --git "a/docs/\351\235\242\350\257\225/\344\272\206\350\247\243\350\241\214\344\270\232\350\226\252\350\265\204\357\274\214\346\270\205\346\231\260\346\211\276\345\207\206\345\256\232\344\275\215.md" "b/docs/\351\235\242\350\257\225/\344\272\206\350\247\243\350\241\214\344\270\232\350\226\252\350\265\204\357\274\214\346\270\205\346\231\260\346\211\276\345\207\206\345\256\232\344\275\215.md" new file mode 100644 index 0000000..069b9b8 --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\344\272\206\350\247\243\350\241\214\344\270\232\350\226\252\350\265\204\357\274\214\346\270\205\346\231\260\346\211\276\345\207\206\345\256\232\344\275\215.md" @@ -0,0 +1,19 @@ +# 了解行业薪资,清晰找准定位 + +## 清晰找准自己的定位 + +相信面试到这里你已经在谈薪资了,此时的你是否会有这样的疑惑:我所期望的薪资是否能给到呢?在这里我建议你一定要对自己有清晰的定位,比如可根据你的工作年限、工作经验以及对市场行情等全方位的了解后,才能拿到合理的薪资,也会让企业认为你物有所值。 那如何才能争取比较合适的薪资呢?很多小伙伴会根据身边的朋友来判断自己的薪资是否合理。但是很多人没有考虑到,大家的学历不同、做过的项目不同、所应聘的公司也不同,那么薪资水平也很可能会有较大的差距,所以一定要明确自己的情况是怎么样的。 + +### 如果你是应届生 + +如果你是一个刚刚毕业的小伙伴,如果从事的是基础的岗位一般薪资基本在 4 ~ 8K,但如果选择做程序员,假如学校背景还不错的话,薪资可在 10 ~ 15 K。 不过,不用太在意薪资这一块,毕竟找一个有前景的工作会更重要,建议对自己有一个短期(1~2 年)的职业规划,相信在不久的将来,薪资也会翻倍的。 + +### 如果你有工作经验 + +如果你已经是一个在专业领域工作多年的候选人,行业经验也非常丰富,相信丰富的经验可以为你创造比较高的收入,你可以比对行业的知名公司职级的薪资结构去判断自己的薪资情况。 + +### 如果你有项目经验 + +当然也可能有小伙伴会问,如果我前一家公司的薪资高于市场行情,换一家公司是否需要继续要求增加薪资,还是考虑降薪? 这个问题我认为可以根据你的项目经验来考虑。如果你的项目经验是行业非常急需而且比较难得的,同时你又做得比较突出,你要求一个合理涨幅,很多公司也是愿意的。但是如果你的工作表现一般,只是一个负责副线项目的人,我认为对你来说很难争取到新的提成,所以不如脚踏实地地去做一个比较稳定的项目,为自己多积累一些相关的经验,也为后面的涨薪做铺垫。 + +## 一些行业的薪酬报告 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\345\201\232\345\245\275\345\205\205\345\210\206\347\232\204\345\207\206\345\244\207\345\216\273\351\235\242\350\257\225.md" "b/docs/\351\235\242\350\257\225/\345\201\232\345\245\275\345\205\205\345\210\206\347\232\204\345\207\206\345\244\207\345\216\273\351\235\242\350\257\225.md" new file mode 100644 index 0000000..8e53ded --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\345\201\232\345\245\275\345\205\205\345\210\206\347\232\204\345\207\206\345\244\207\345\216\273\351\235\242\350\257\225.md" @@ -0,0 +1,69 @@ +# 做好充分的准备去面试 + +## 日常HR的工作流程解析 + +可能你会问:HR 不就是每天守在电脑前等着投简历,然后第一时间去筛选简历吗?为什么还会有“投递黄金时间段”这样的说法?不应该是随时投递简历都可以吗?有这样疑惑的你就太误解 HR 的工作了,接收简历只是他们手上工作的一部分。HR 每天还需要处理其他很多的工作,就像很多不同岗位的小伙伴同时负责很多工作一样。 + +首先,电话沟通是 HR 核心工作内容的一部分,电话沟通并不是简单的通知候选人何时何地来面试。可是,大部分人反馈说:我每次接到电话就是通知面试,并没有说其他的。其实在电话沟通之前,HR 就已经仔细阅读过简历内容了,有疑问的地方会标记出来,在电话沟通过程中与你确认具体的问题点在哪里。 + +所以,电话沟通分为两种:一种是电话预约;另外一种是电话确认。 + +电话预约已经很清晰了,就是确认面试的时间。 + +电话确认一般发生在 HR 不确认你对招聘的岗位是否有兴趣或者简历存在比较大的问题的情况下。也就是说在接到电话沟通时不要表现的太过随意,因为你在电话另外一端说的任何一句话 HR 都会有一个潜意识的判断,这个判断很有可能会影响到你的面试环节。 + +如何在电话沟通时表现良好呢? + +一定要对投递的公司有所了解,也正如前面说过的,很多人都是随便投递简历,最终也不记得都投递了哪些公司,所以在电话沟通时,招聘人员很不喜欢听到“请问你们公司是做什么的?”、“这个岗位的职位 JD 可以发给我看看吗?”、……,类似的声音,他们会认为你都没有认真对待这个岗位。 + +电话沟通时语气非常冷漠,明显表现出非常不高兴的情绪。其实 HR 在电话另外一端可以很清晰地听出对方的性格和表现。例如,有的人声音很消极,给 HR 的感觉是这个人对工作没有热情;有的人声音非常强硬,很容易给 HR 对方不是很好相处的印象。 + +因此,一定要记住自己投递过的公司和岗位信息,给 HR 一种你已经做好准备的感觉;然后掌握沟通的技巧,清晰地表达自己的想法和观点,语气尽量亲切一些,这样 HR 应该很愿意和你沟通,还能留下不错的印象。 + +其次,面试安排其实也会占据 HR 的很多工作时间。这个环节和电话沟通一样,你的表现也很容易被 HR 作出初步的判断。如何在面试安排时表现良好呢? + +要有时间观念,尽量提前 5 ~ 10 分钟到达面试地点。很多招聘企业非常喜欢遵守时间的人,而且 HR 每天都会安排很多场面试,可能每个候选人的面试时间都是相连的,如果迟到很有可能会影响面试官的时间安排。如果有特殊原因,最好提前 0.5 ~ 1 个小时通知 HR,争取能调整面试的时间。 + +要保持个人卫生,相信不论到任何场合,个人卫生非常差的话很难受到欢迎。虽然不需要你穿着有多奢华,只要保持衣着整洁、干净,会给 HR 留下不错的印象。 + +保持礼貌,中国有句古话“礼多人不怪”,虽然 HR 只是帮你协调面试,但也起到了你是否可以入职的关键因素。所以保持礼貌和谦虚的心态和行为,可以得到不少的加分。 + +当然,除了上面提到的工作内容,很多 HR 还会负责面试、发放 Offer 以及办理入职相关的手续,这些内容将在后面详细讲解。 + +## 其他投递渠道分析 + +目前 HR 收取简历的主要渠道有招聘网站、猎头、内推、校园招聘这几类。接下来说说这些渠道都适合什么样的求职者,去帮助你准确地判断通过哪种渠道可以更好的找到工作以及心仪的公司。 + +### 招聘网站 + +不用过多介绍,目前很多求职者都在使用,比如拉勾网,其是一家专注垂直互联网领域的公司,如果有研发、产品、运营等互联网方向的岗位需求,可以到该网站上发布岗位或寻找人才。 + +我在面试的时候经常有人问:常年累月挂着招聘是真的有招人的需求吗?企业究竟是真的想要招人,还是只挂在网上做做样子? + +你可能经常也会有这样的疑惑,其实很多企业挂出去的岗位目前都是在持续招聘的,可能招聘的紧急度并没有那么高,也有可能是一个储备的岗位,HR 会先将岗位挂出去,对投递来的简历会仔细审阅,直到选出最合适的人选才会安排面试。 + +### 猎头 + +猎头渠道对候选人的要求可能会更高,一般是针对高级管理者或者行业专家使用的渠道。如果你只有 1 ~ 3 年的工作经验,可能现在还用不到这个渠道。 + +此时的你是否有过这样的疑惑:看岗位是合适的,为什么最后得到的反馈就是不适合? + +这个时候就要审视一下自己的简历,正如第01讲所分析的,是不是真的将自己的优势都凸显出来了?招聘岗位所说的内容是不是都在简历中有所体现?工作年限真的都留意到了吗?自己的项目经验是不是也都体现出来了? + +### 内推 + +内部推荐是一个非常好的渠道,成功率非常高。如果你身边有不错关系的同学或者朋友在心仪的公司,请他内推给 HR,可能反馈非常快,不过面试结果还要看个人的努力了。 + +那为什么同一家公司自己投简历就没后续了,猎头推荐或内推就有面试机会呢? + +其实猎头的工作并没有那么神奇,每位猎头顾问在推荐你简历的时候都会与你进行沟通,在沟通的过程中会提炼出你简历里面没有展现的优势,然后猎头会对你的简历进行修改,以突出工作中的重点,以便让 HR 清晰地捕捉到想要的信息。所以说将自己的简历写出价值是有多么重要。 + +### 校园招聘 + +校园招聘相信都经历过,这个渠道主要是针对应届毕业生的,每年都会在 3 月或者 10 月份进行。如果你已经离开校园了,也就没有办法参与这个渠道的招聘了。 + +那互联网企业招聘现在更看重是学历?还是本身具备的技能? + +可能有的人比较忐忑:我学历背景并不是很好,企业在招聘的时候更看重学历还是看中我本身的技能呢?当然很多人会告诉你学历是非常重要的,同时企业也会注意你的学习能力。如果你在后期努力进修了更好的学历,其实可以充分的展现学习主动性,也会很获得企业的欢迎。当然如果你的项目经验非常完善,在过去的工作经历中负责过比较好的项目,那完全可以掩盖你学历上的差距。 + +希望通过对以上渠道的分析,能准确地使用它并获得邀约的机会和面试的流程。 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\345\210\244\346\226\255\345\205\254\345\217\270\350\203\214\346\231\257\357\274\214\345\201\232\345\207\272\345\220\210\347\220\206\351\200\211\346\213\251.md" "b/docs/\351\235\242\350\257\225/\345\210\244\346\226\255\345\205\254\345\217\270\350\203\214\346\231\257\357\274\214\345\201\232\345\207\272\345\220\210\347\220\206\351\200\211\346\213\251.md" new file mode 100644 index 0000000..7af41ab --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\345\210\244\346\226\255\345\205\254\345\217\270\350\203\214\346\231\257\357\274\214\345\201\232\345\207\272\345\220\210\347\220\206\351\200\211\346\213\251.md" @@ -0,0 +1,25 @@ +# 判断公司背景,做出合理选择 + +## 通过网站上展示的相关信息来判断 + +互联网对于大家来说已经不是那么陌生了,所以很多企业的信息都可以在互联网上获得。我们可以通过这家公司的官网去了解它的基础业务和公司发展信息;也可以通过拉勾招聘网站上的企业界面介绍页去了解这家公司的基础信息。如果还不放心的话,还可以到“天眼查”或者“企查查”官网上查一下这家公司是否是一家正常运营的公司。 当查到这些信息后该如何判断公司的背景呢? + +(1)公司的主营业务。可以查看目前这个主营业务是不是你希望从事的,是行业内的蓝海还是一个比较传统的业务。比如可以通过传播媒体的报道来了解;或者如果一家公司在一年内迅速扩张,那这家公司必然是蓝海企业。 + +(2)公司的融资情况。可以通过了解公司的融资轮次以及投资的金融机构来验证是否是一个成熟的公司,因为优秀的金融机构对公司进行投资会非常谨慎。 + +(3)公司的人员规模和办公地点。这些也可以判断一家公司的情况,假如公司人员比较多,则说明业务可能更为稳定一些,办公地点在比较正规的办公大厦里也代表了一种稳定的因素。当然不是说好的办公地点和公司人员数量多就是最好的公司,也有不少几个人的初创公司也是很值得考虑的。 + +(4)公司的创办时间。除了以上因素以外,肯定要考虑公司的创办时间,可能刚刚开始创办的公司不具备那么好的条件。如果你比较喜欢初创业的公司,那么可以通过公司经营的业务和投资机构的名气去判断;但是如果是一个创办时间比较久的公司,可能就要考虑得稍微多一些了。 (5)收集创始人在网上的演讲稿。从他们的演讲稿中获得一些信息,通过对这个人的看法对比是否和你期望的企业形式一致,也可以判断出他是否具有行业的眼光,但是尽量避免跳入公关稿的坑里去。 当然不排除一些经营很好,但是企业规模和融资情况一般的公司,这个也需要通过其他方面的信息去判断。 + +## 通过来自内部人员的信息判断 + +以上是通过拉勾招聘网站上的公司介绍页以及企业官网来判断公司的背景,但只是了解这个公司的表面信息,想了解这家公司真实的具体情况和管理风格,通过以下的方式可能会更清晰一些。 + +(1)如果这家公司中有熟悉的朋友(或前同事)或者学长学姐的话就比较方便了,可以问一下他们关于公司内部的团队、部门、上下班时间、福利等情况,甚至可以问一下团队领导的风格是怎样的。 + +(2)如果你没有这样的朋友关系,也可以通过职言或者论坛的讨论去看看这个公司内部的员工是怎么评价公司的。 + +## 面试时通过面试官的言行举止判断 + +在面试的过程中,还可以通过与面试官的交流去判断这家公司的情况,如果面试官在面试过程中表现的比较有条理、沟通比较有素质,则可以说明这家公司还是不错的选择;也可以通过对公司环境的观察来判断这家公司的情况。当然你也可以看看拉勾网企业界面的面试评价区,去了解一下其他面试者对这家公司的评价。 相信通过以上几种方法,可以比较清晰地了解一家公司的情况,也可以帮助你更好的去判断这家公司是否值得去。 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\345\246\202\344\275\225\350\256\251\345\267\245\344\275\234\345\271\264\351\231\220\345\217\230\346\210\220\344\274\230\345\212\277.md" "b/docs/\351\235\242\350\257\225/\345\246\202\344\275\225\350\256\251\345\267\245\344\275\234\345\271\264\351\231\220\345\217\230\346\210\220\344\274\230\345\212\277.md" new file mode 100644 index 0000000..023744a --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\345\246\202\344\275\225\350\256\251\345\267\245\344\275\234\345\271\264\351\231\220\345\217\230\346\210\220\344\274\230\345\212\277.md" @@ -0,0 +1,75 @@ +# 如何让工作年限变成优势 + +在软件开发招聘中,“有多少年工作经验”是一个重要的招聘指标。但实际上,技术能力和工作年限并不是正相关的,特别是工作三五年以后,很多人的技术能力进步就几乎停滞了。但是招聘面试的时候,面试官是期待他有着和工作年限相匹配的技术能力的。 + +如果一个人空有十几年工作经验,却没有相应的技术能力,那么这十几年的工作经验甚至可能会成为他的劣势,至少反映了他已经没有成长空间了。反而是工作年限不如他,但是技术能力和他相当的其他候选人更有优势,因为这个人可能还有进步的空间。 + +事实上,就我这些年的面试经验而言,空有十几年工作经验而没有相应技术能力的人大有人在。其实从简历上就能看的出来:最近几年的时间他承担的工作职责几乎没有变化,使用的技术、开发的项目几乎和头几年一样,那么很难相信这些年他的技术会有什么进步。 + +那么如何保持技术能力持续进步,使工作年限成为自己的优势而不是缺点呢? + +## 德雷福斯模型 + +我们先看一个德雷福斯模型。德雷福斯是一个专业人员能力成长模型,这个模型认为所有专业人员都需要经历5个成长阶段,不管是医生还是律师,或者是软件开发,任何专业技能的从业者都需要经历新手、高级新手、胜任者、精通者、专家5个阶段。 + +![德雷福斯模型](./assets/如何让工作年限变成优势-德雷福斯模型.png) + +通常一个人进入专业的技能领域,即使在学校已经系统学习过这个专业的相关知识,但依然无法独立完成工作,必须在有经验的同事指导下,学习相关的技能。这里主要学习的是有关工作的规则和套路。比如用什么工具、什么框架,如何开发程序,如何开会、写周报,如何和同事合作,业务领域的名词术语是什么意思等等这些各种各样和工作有关的大小事情。这个阶段叫做新手阶段。 + +通常说来,一个人大约工作两三年后,就差不多掌握了工作的各种套路,可以摆脱新手阶段,独立完成一些基本的工作了。通过新手阶段的人,少部分会直接进入胜任者阶段,而大多数则进入高级新手阶段。 + +高级新手其实是新手的自然延续,他不需要别人指导工作,也不需要学习工作的规则和套路,因为高级新手已经在新手阶段掌握了这些套路,他可以熟练应用这些规则套路完成他的工作。但是高级新手的能力也仅限于此,他不明白这些规则是如何制定出来的,为什么使用这个框架开发而不是另一个框架,也不明白这个框架是如何开发出来的。 + +因此,一旦需要解决的问题和过往的问题有很大不同,以前的规则套路无法解决这些新问题的时候,高级新手就抓瞎了,不知道该怎么办。 + +一个悲观的事实是,新手会自然进入高级新手阶段,而高级新手却无法自然进入其后的其他等级阶段。实际上,在各个专业领域中,超过半数的人终其一生都停留在高级新手阶段,也就是说,大多数人一生的工作就是基于其专业领域的规则在进行重复性的劳动。他们不了解这些规则背后的原理,也无法在面对新的问题时,开创出新的方法和规则。那些简历上十多年如一日使用相同的技术方案、开发类似软件项目的资深工程师大部分都是高级新手。 + +导致一个人终身停留在高级新手阶段的原因有很多,其中一个重要的原因是:高级新手不知道自己是高级新手。高级新手觉得自己在这个专业领域混得很不错,做事熟练,经验丰富。 + +事实上,这种熟练只是对既有规则的熟练,如果岁月静好,一切都循规蹈矩,也没什么问题。而一旦行业出现技术变革或者工作出现新情况,高级新手就会遇到巨大的工作困难。事实上,各行各业都存在大量的高级新手,只是软件开发领域的技术变革更加频繁,问题变化也更加快速,使高级新手问题更加突出。 + +少部分新手和高级新手会在工作中学习、领悟规则背后的原理,当需要解决的问题变化,或者行业出现技术革新时,能够尝试学习新技术,解决新问题,这样的人就进入胜任者阶段。胜任者工作的一个显著特点是,做事具有主动性。他们在遇到新问题时,会积极寻求新的解决方案去解决问题,而不是像高级新手那样,要么束手无策,要么还是用老办法解决新问题,使问题更加恶化。 + +胜任者能够解决新问题,但他们通常只会见招拆招,局限于解决问题本身,而缺乏反思精神以及全局思维:为什么会出现这样的问题?如何避免类似问题再发生?这个问题在更宏大的背景下处于什么位置?还有哪些类似的问题? + +而拥有反思精神和全局思维,即使没有新问题也能够进行自我突破、寻求新的出路的人,就进入了精通者阶段。精通者需要通过主动学习进行提升,主动进行大量的阅读和培训,而不是仅仅依靠工作中的经验和实践。他们在完成一个工作后会反思:哪些地方可以改进,下次怎么做会更好? + +精通者拥有了自我改进的能力。 + +高级新手会把规则当做普适性的真理而使用,甚至引以为豪;而精通者则会明白所有的规则都只在特定的场景中才会有效,工作中最重要的不是规则,而是对场景的理解。 + +而最终,各行各业大约只有1%的人会进入专家阶段,专家把过往的经验都融汇贯通,然后形成一种直觉,他们直觉地知道事情应该怎么做,然后用最直接、最简单的方法把问题解决。专家通常也是他所在领域的权威,精通者和胜任者会学习、研究专家是如何解决问题的,然后把这种解决方案形成套路,成为行业做事的规则。 + +## 如何在工作中成长 + +德雷福斯模型告诉我们,人的专业能力不会随着工作年限的增加而自然增长,多数人会终身停留在高级新手阶段。那么如何在工作不断成长,提升自我,最终成为专家呢?以下三个建议供你参考。 + +### 勇于承担责任 + +好的技术都是经过现实锤炼的,能够真正解决现实问题的,得到大多数人拥护的。所以自己去学习各种各样的新技术固然重要,但是更重要的是要将这些技术应用到实践中,去领悟技术背后的原理和思想。 + +而所有真正的领悟都是痛的领悟,只有你对自己工作的结果承担责任和后果,在出现问题或者可能出现问题的时候,倒逼自己思考技术的关键点,技术的缺陷与优势,才能真正地理解这项技术。 + +如果你只是去遵循别人的指令,按别人的规则去做事情,你永远不会知道事物的真相是什么。只有你对结果负责的时候,在压力之下,你才会看透事物的本质,才会抓住技术的核心和关键,才能够让你去学好技术,用好技术,在团队中承担核心的技术职责和产生自己的技术影响,并巩固自己的技术地位。 + +### 在实践中保持技能 + +有个说法叫做1万小时定律,是说要想成为某个领域的专家,必须经过1万小时高强度的训练才可以,对软件开发这样更强调技术的领域来说,这一点尤其明显。我们必须要经过长时间的编程实践,从持续的编程实践中提升技术认知,才能够理解技术的精髓,感悟到技术的真谛。 + +但是1万小时的编程时间并不是说你重复的编程1万小时就能够自动提升成为专家的。真正对你有帮助的是不断超越自我,挑战自我的工作。也就是说,每一次在完成一个工作以后,下一次的工作都要比上一次的工作难度再增加一点点,不断地让自己去挑战更高难度的工作,从而拥有更高的技术能力和技术认知。 + +通俗说来,就是要摘那些跳起来才能够得着的苹果,不要摘那些伸手就能够得着的苹果。但是如果难度太高,注定要失败的任务,其实对技术提升也没有什么帮助。所以最好是选择那些跳起来能够摘得到的苹果,你要努力再进步一点点,才能够完成。通过这样持续的工作训练和挑战,在实践中持续地获得进步,你就可以不断从新手向专家这个方向前进。 + +### 关注问题场景 + +现实中,很多人觉得,学好某一个技术就大功告成了。但事实上是,即使你熟练掌握了强大的技术,但如果对问题不了解,对上下文缺乏感知,也不会真正地用好技术,也就无法去解决真正的问题。试图用自己擅长的技术去解决所有问题,就好像是拿着锤子去找钉子,敲敲打打大半天,才发现打的根本就不是一个钉子。 + +所谓的专家其实是善于根据问题场景发现解决方法的那个人,如果你关注场景,根据场景去寻找解决办法,也许你会发现解决问题的办法可能会非常简单,也许并不需要多么高深的工具和方法就能够解决,这时候你才能成为真正的专家。也就是在这个时候你会意识到方法、技术、工具这些都不是最复杂的,而真正复杂的是问题的场景,是如何真正地理解问题。 + +这个世界没有万能的方法,没有一劳永逸的银弹。每一种方法都有适用的场景,每一种技术都有优点和缺点,你必须要理解问题的关键细节、上下文场景,才能够选择出最合适的技术方案,真正地解决问题 + +## 结束语 + +如果你是一个新手,刚刚工作不久,那么不要被所谓的工作经验和所谓的资深工程师的说教局限住,你要去思考规则背后的原理,主动发现新问题然后去解决问题,越过高级新手阶段,直接向着胜任者、精通者和专家前进吧。 + +如果你是一个有多年经验的资深工程师,那么忘了你的工作年限吧,去问自己,我拥有和工作年限相匹配的工作技能吗?我在德雷福斯模型的哪个阶段?我该如何超越当前阶段,成为一个专家? \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\345\267\245\344\275\234\344\272\244\346\216\245\346\265\201\347\250\213\347\246\217\345\210\251\350\241\224\346\216\245.md" "b/docs/\351\235\242\350\257\225/\345\267\245\344\275\234\344\272\244\346\216\245\346\265\201\347\250\213\347\246\217\345\210\251\350\241\224\346\216\245.md" new file mode 100644 index 0000000..cd873d9 --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\345\267\245\344\275\234\344\272\244\346\216\245\346\265\201\347\250\213\347\246\217\345\210\251\350\241\224\346\216\245.md" @@ -0,0 +1,53 @@ +# 工作交接流程福利衔接 + +## 工作交接流程 + +### 如何不伤和气的提出辞呈 + +终于拿到了自己心仪公司的 Offer 了,可能有很多小伙伴又开始发愁了:如何与领导顺利提出辞呈,又不伤和气呢?这个时候一定要做好最坏的打算,你要明白,心软拖着不说会更伤害自己与前公司的关系,不如直截了当、当机立断。 一般提出离职的方式分为两种: + +通过邮件的形式提出辞呈; + +直接找直属 leader 沟通。 具体采用哪种方式,可根据自己的个性来判断,比如不太擅长沟通、偏内向的可以通过邮件的方式;如果已经想好了怎么和上级沟通,也可以直接找 leader 阐明心意。那在写邮件或直接沟通时需要注意哪些呢?img首先,可以先表达出对公司和领导在工作中的指导和帮助的感激,以及这段时间在公司的工作和成长的开心,同时说明一下做出辞职的决定对自己来说是多么难的一次选择。相信这样的表达可以让领导对你有个不错的印象。 其次,不论你的离职原因是不满意薪资、不适应团队的管理风格还是发展空间到达了上限等,都不要在这里抱怨出来,因为每个公司的 leader 都清楚公司里的问题,与其这样,不如直接告诉 leader,辞职的原因是希望可以有更好的发展,或者是让自己有更好的学习成长的空间。相信你的决心加上这样的理由,leader 一定会领会里面的意思。 + +如果这时 leader 突然问:找到下家了么?该怎么回答?建议这样委婉地回答:手里有好几个 Offer,还没确定好去哪家…… 最不建议的离职理由:经常会有小伙伴为了避免双方尴尬,会选择“家人生病需要较长的时间照顾”、“家人要求我回老家工作”等类似这样的理由,如果是真实的当然不会有问题,如果是虚构的,以后万一被发现,则会给前公司留下一个不诚信的印象,以后再相见时会更尴尬。 当然也有小伙伴提出离职是为了通过拿到的 Offer 要求涨薪,这样的“小聪明”玩不好可能就把自己“玩”进去了,不但在拿到 Offer 的公司名声坏了,也不会被现在的公司重用的。 + +最后,可以和前司表示一下,自己一定会负责任地把手里的工作交接清楚,站好最后一班岗,这样也可以给前司 leader 留下一个让人踏实的印象。毕竟你的面试背调还在人家手里,总不希望闹得不可开交,拿不到一个好的背调反馈吧。 + +### 合理安排交接工作 + +一般来说,如果你是一位已经转正的全职员工,那么交接的时间为一个月,所以公司也会要求你在这一个月里正常工作,那么,如何清晰地在这一个月里合理安排交接工作呢? + +先和直属 leader 协商找到一个靠谱的工作交接人; + +把自己以往的项目文档整理好,分类发给交接人; + +如果你手里还有未结束的项目,可以带着交接人熟悉一下,一起对这个项目做收尾工作; + +通知同事或者项目对接人自己已经离职,接下来的项目由被交接人负责; + +空出两周的时间,协助交接人熟悉你手里的工作内容,在旁做好支持工作。 + +如果新的公司期望你能尽快入职的话,多数情况下会担心你拒绝入职,此时建议你诚恳地向新公司解释,并和新公司同步交接工作的进度。 交接文档有以下注意事项,比如: + +清晰的文档归类,发现问题可以马上与你沟通; + +尽可能将相关的文档都涉及到,让你的交接文档更容易查找; + +记得文档转出时抄送给领导,这个很重要,一定要记得; 我相信这样的交接流程不会让自己手忙脚乱,也可以给前司留下不错的印象。 离职最后一天走的时候,记得和同事们一一打招呼,感谢大家以往的照顾和帮助,以后要常保持联系。更重要的一点是,一定要拿到“离职证明”文件或“解除 / 终止劳动合同报告书”。 + +### 福利衔接 + +交接工作都做完了,很多小伙伴会问:我的社保、公积金怎么办?下面来讲讲 3 种常用的福利交接事项。 + +### 社保公积金 + +各个公司的社保、公积金都是以每个月的 15 日作为分界点,如果你是在 15 号前入职的新公司,那么就会帮你交当月的社保和公积金,如果你是在 15 号后从前公司离职,社保、公积金会由前公司承担。当然也会有特殊情况,要看人才局的具体安排。 如果你正好是 15 号前离职,中间休息了一段时间,15 号后入职新公司的,可能需要你自己找第三方保险代缴公司自行缴纳社保公积金了。img + +### 年假 + +通常,公司会按照你出勤的月份帮你做年假的换算,然后与你协商安排延后几天离职,或结算成工资,或者按照公司的规定有其他操作。img + +### 工作居住证 + +如果在前司有工作居住证的话,需要问问新公司是否可以接收,如果可以当然就直接转出,如果不可以,需要问问是否有第三方机构接收。 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\345\267\247\345\246\231\346\216\250\351\224\200\350\207\252\345\267\261\347\232\2043\344\270\252\346\212\200\345\267\247.md" "b/docs/\351\235\242\350\257\225/\345\267\247\345\246\231\346\216\250\351\224\200\350\207\252\345\267\261\347\232\2043\344\270\252\346\212\200\345\267\247.md" new file mode 100644 index 0000000..807f5de --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\345\267\247\345\246\231\346\216\250\351\224\200\350\207\252\345\267\261\347\232\2043\344\270\252\346\212\200\345\267\247.md" @@ -0,0 +1,37 @@ +# 巧妙推销自己的3个技巧 + +## 平和的心态,展现你的热情 + +对于任何一家企业在选择合适的人选时,一般都会从这几个方面进行筛选:首先是否具有相关的项目经验;其次是否聪明或者是否具有独自解决问题的能力;最后判断能否融入到团队的氛围中,以及是否对企业或者行业具有热情的态度来面对。 + +前两个方面所说的是面试者的行业经验和智商,对于一家公司在招聘时当然重要,但这并不是一个团队可以获得成功的关键因素。相信很多企业在选择五年经验以内的候选人时,这两个方面的考察一定会低于最后性格部分的考察。 + +相信很多小伙伴会有疑问,性格(工作热情)相比情商或工作经验对于一家企业真有那么重要吗?这个我可以肯定的告诉你,面试官非常喜欢性格开朗、积极主动、乐于挑战的候选人;同样也很排斥在乎个人得失、把责任都推给前司的候选人。相信你在工作中也会遇到过类似的同事,可以回忆一下,当时你是不是也很排斥呢? + +在一个团队里如果存在一位性格比较消极的员工,很容易将其他员工传染,从而导致整个团队产出效率低下。也就是我们常说的“酒与污水定律”:是指把一勺酒倒入到一桶污水里,得到是一桶污水;如果把一勺污水倒入倒一桶酒中,得到的还是一桶污水。所以,对于面试官来说,候选人的性格和态度是至关重要的。 + +如何在面试官面前表现出积极正向、乐观的心态呢? + +首先,需要表现出对应聘岗位和企业的认同感,也让面试官看到你为了这份工作做了很充分的准备,或者积极的介绍之前做过的项目与应聘企业项目的相似度。这些表现都可以让面试官感受到你的热情和积极正向的输出,非常不建议面试时问什么答什么的做法。 + +比如,当面试官问「你为什么选择目前的这份工作」时?如果只是单纯地回答「我喜欢这份工作」且没有任何的解释,那面试官无法判断你所说的真实性。此时建议这样回答:“因为目前这份工作和我之前做过的 xxx 项目非常相像。我在参与上一份项目时学习到了 xxx 技能,找到了一个新的发展方向,从而喜欢上了这样的一份职业。”,相信这样的表述面试官才能感受到你的热情和积极正向的态度。 + +其次,可以和面试官介绍一下,你在上一家公司与同事和领导相处融洽的案例,让面试官感受到你是一个积极融入团队中的人。比如「在前司获得的成长有哪些,与前 leader 的身上都学习到了哪些工作思路和成长思路等」,相信面试官会认为你是一个非常值得培养和积极主动学习的人。 + +## 了解行业发展,清晰表达你的见解 + +除了表现出积极和热情以外,如果在面试的过程中可以介绍一些你对行业以及对自己所从事工作的理解或见解,相信面试官一定会被你的表述深深吸引,也同样加强了希望可以录取你的信心。 + +比如,当面试官问「你怎么看对目前所从事的工作价值」时,如果这样回答「我觉得这是一份收入,并没有太多的感受,也不知道自己未来的发展是什么样的」,面试官会判断你是一个没有任何思考的人,应该也不会在自己的岗位上有什么作为。 + +此时建议这样回答:“我非常喜欢我的工作,我感觉我的岗位在目前行业的发展中起到了非常重要的作用,我们所做的几个项目都在推动公司的发展,也帮助公司的业绩从 XX% 提升到了 XX%(在这里举一些自己做过的项目经验),而且我也希望可以继续从事这样的工作,因为它可以让我获得更多的成就感。相信这个行业的发展是 xxxxxxxxx,我的职业规划也会跟随这个行业的发展而得到很大的提升。” + +当面试官听到这样的介绍时,会非常清晰地了解你对自己的工作已经有了深入的思考,同时也能感受到你不止局限在自己的工作领域中,还在通过行业的变化和了解,来规划自己的职业,是一个很有潜力的候选人。 + +## 真诚的对待每一次面试 + +当然除了积极的态度以及清晰的定位以外,还必须是一个真诚、正直的人。如果在面试的过程中表现良好、思考很全面,但是与实际工作的内容只有 50% 的真实度,这样会很容易在面试官面前露馅,然后给你打上一个不真诚的标签,自然而然,面试也就到此结束了。 + +因此,很多企业非常在乎候选人是否诚实或者真诚。也许你的职业经历并不是很丰富、项目内容并没有那么完美,但如果将自己所做的内容真实、完整地呈现给面试官,同时加上自己的思考,相信很多面试官会参考你的工作年限然后给你一个非常公平的反馈。 + +通过以上三个方面的讲述,可以了解到面试官在面试的时候更看重的是品德,然后是性格,最后才是工作经验和学历。希望你听完这一讲的内容后,可以积极主动地面对自己的岗位,更好的去思考如何巧妙的推销自己。 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\346\212\200\346\234\257\345\262\227\351\235\242\350\257\225\346\275\234\350\247\204\345\210\231.md" "b/docs/\351\235\242\350\257\225/\346\212\200\346\234\257\345\262\227\351\235\242\350\257\225\346\275\234\350\247\204\345\210\231.md" new file mode 100644 index 0000000..252b2b4 --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\346\212\200\346\234\257\345\262\227\351\235\242\350\257\225\346\275\234\350\247\204\345\210\231.md" @@ -0,0 +1,71 @@ +# 技术岗面试潜规则 + +开启上帝视角你会发现,在技术岗面试,竟有如此多不为人知的潜规则。 + +## 一、岗位如何诞生 + +### 1、岗位招聘指标 + +岗位招聘指标哪里来的呢?首先肯定不是无中生有,也不是领导异想天开而来。那公司或部门什么时候会进行岗位招聘呢?即产生岗位招聘指标。根据个人以往经验,常常会产生在这两种情况:公司业务扩展期和为解决特定问题。公司业务扩展期,往往需要提前进行人员储备,会逐层递进下发岗位指标,最终抵达各部门和小组;另一种情况则是在发生棘手问题,而问题又是非解决不可,为解决问题先经过问题深层次的分析,恰好人力又是解决问题的关键路径,岗位招聘指标就顺应而生。 + +### 2、挂上招聘页面前 + +岗位指标定好就挂上招聘页面?其实还有很多我们看不到的过程。假设是因棘手问题产生的岗位招聘指标,技术总监需要向CTO汇报该问题解决方案和进展,技术总监的解决方案有一个关键路径是新增某类岗位人力若干。请问技术总监如何说服人事部门新增这笔预算?或者技术总监是否有这个权限决定这笔新支出?即使技术总监有这个权限新增(超强话语),也需要在流程上获取人事部门的支持,因为在招聘过程中也是需要人事部门的工作支持。 + +因此在岗位指标制定后,还有个我们看不到的关键点:`新增人力预算需要说服人事部门`。 + +可见招聘指标看起来有了,实际上还需要来回确认。包括薪资预算再逐步拆分,招聘多少人,什么级别,薪资范围,期望到位时间。接着技术部门和人事部门再临时组成一个虚拟工作组,明确分工职责,如简历筛选,制定面试流程。在这一系列的操作后,最终才有我们在招聘页面上看到的岗位信息。 + +## 二、精准的岗位描述 + +### 1、精准的岗位描述 + +招聘页面上的岗位描述,一般都是HR挂上去的。但是也有特例,公司员工为了赚内推费用,随意写的挂上去收简历内推挣台iPhone。比较好的方式是HR事先跟用人方沟通,用人方根据岗位实际工作内容,罗列工作内容和技能要求。但是用人方,不了解现实市场行情,也不知道岗位预算(级别和薪资范围),所以在浏览招聘岗位时,常看到精通某某的描述语句。 + +### 2、流水线岗位描述 + +招聘页面上的岗位描述,一般常看到的都是流水线式。要么HR拿到一段岗位描述,统一挂到招聘页面,要么HR找用人方,用人方从别处复制简单修改下。你很难看到用心写的岗位描述,所以常常会遇到投的岗位描述跟实际面试官介绍的不一致。这里我称为流水线岗位描述,即招聘页面看到的岗位描述也仅供参考。 + +## 三、流畅的面试流程 + +### 1、标准化面试流程 + +你遇到专业标准化面试流传概率非常低。在从业十年,面试百余场,只遇到一家公司有专门培训面试官,即面试你的人都没有经过上岗培训。面试流程包括岗位制定和岗位招聘两大部分。岗位制定指为什么要招聘这个岗位,解决什么问题,大概花费多少预算,岗位的职业发展通道;岗位招聘指发岗位到招聘网站,收简历进行面试,发放offer,新人入职到转正。理论上从公司角度普遍认为自己的这套流程很专业,但是从参与者角度看这套流程其实是很不专业的。面试官没有上岗培训,求职者无法从招聘页面上看到真实的岗位描述,新人导师只是一份额外临时工作。 + +### 2、临时拼凑面试流程 + +作为求职者需要很清楚明白遇到的面试流程只是临时拼凑的。你会遇到面试官打听你的薪资期望,因为他想了解市场行情。你会遇到明明简历上写了介绍,但是似乎面试官没看过,因为他只是5分钟前被临时安排面试你。 + +## 四、最终面试结果 + +### 1、面试通过发放offer + +终面后很久才发放offer。常常遇到有些公司面试次数特别多,而且终面后很久才收到offer通知。大公司一般审核流程很长,面试本身有流程,每次面试后都要填写面试结论。面试结束后综合考虑录用,也要发起定级定薪审核,需要部门/公司/人事审核,所以审核本身就很慢了,如果遇到领导出差,审核人处理更紧急的事情,那耗费时间就更长了。 + +一直没消息突然打电话。已经入职新一个月公司,突然接到HR电话,说邀请你入职让你考虑一下。其中原因终面通过那批人中你的综合条件没那么优秀,简单说你是备胎。先发放offer给其他人,但是其他人拿到offer推迟入职,最后又去了更好的公司,然后备胎转正想起你了。 + +### 2、面试不通过无音讯 + +预算不够没通过。明明各轮面试下来感觉不错,甚至终面还问你住的远不远,但是还是没有等来offer。面试官只是考核你岗位匹配度,出发点肯定是越优秀越好。但是每个岗位都有相应的硬指标—预算,岗位预算不够,那怕很想要你,公司最终审批也是没法通过的。 + +岗位突然没了。大公司信息常常不太透明,组织架构调整往往是高层的决策,底层常常是不知道的,所以就会造成招聘这拨人和组织架构调整这波人信息不互通, 组织架构调整公告时候才告诉你需要停止招聘。 + +## 五、其他潜规则 + +### 面试参与人员 + +在面试环节,参与人员常常有很多,包括岗位用人方、招聘HR、各轮面试官、公司管理层。岗位用人方:最终招聘人员使用方,一般会充当第一次面试官,或者加一轮面试,也有可能不参与面试环节,但是肯定要充当新人导师,帮助新人快速上手;招聘HR:寻找适合的候选人,初次筛选简历,邀约候选人,各个环节润滑加速流程。各轮面试官:包括N轮面试,先考察技术基本功,再考察综合实力,有适合也会有跨部门面试,级别不同流程往往也不同。公司管理层:往往岗位级别达到一定才需要管理层参与,薪资审批也只是人事部门审核。 + +### 551定律 + +你知道招聘市场的“551 定律”吗? 551 定律:每一层筛选环节都会有百分之十的折损率。一个岗位从接收简历到发下offer至少要筛选500份左右的简历、面试50人左右、只有5人左右通过面试,最终也只有1位候选人可以顺利入职。 + +对于企业内的招聘人员平均每天至少在一个岗位上要收到几百份简历,至少要面试十几个候选人,每周至少会发出4-5个offer;一周内至少同时会招聘5-7个不同的岗位。因此,可以看出,招聘人员的工作强度是非常巨大的,面试者需要把控好每个环节的节奏并表现优秀,才能得到最好的结果。 所以说你的简历是否对自己的工作内容和项目经历描述清晰,你的面试表达是否可以直击面试官的问题要点,你的Offer沟通是表现的完美无缺,都对你能否赢得心仪的岗位至关重要。 + +### 面试的那些事 + +简历已被阅读却迟迟没有回应 相信大部分人都会有这样的疑惑:每个求职的早上都会迫不及待的打开手机查看是否收到昨天投递简历的回复,发现都显示为“您的简历已经被阅读”的状态,带着兴奋的心情等待着心仪企业的面试邀请电话,上午过去了没有接到电话,午饭时间过去了没有接到电话,下班了仍然没有接到电话,第二天、第三天……一直没有收到任何的信息,一度以为自己的手机是否坏掉了或者怀疑招聘人员是不是忘记了拨打电话… + +面试很顺利却迟迟没有收到录用通知 相信你也会有这样的困惑:好不容易收到面试电话,兴奋的不能自已,提前和公司请了假,穿上非常体面的衣服,吃一个元气满满的早餐,做好充分的准备去面试。面试时费尽心力地展示自己,把从事过的工作内容和听说过的项目经验全部展示在面试官面前,这时的你侃侃而谈,表现得非常自信且积极正能量。但是,不知是否留意过面试官有时候针会对一个问题进行深入的提问或者偶尔出现锁眉的动作? + +面试了很多公司,难道能力已被透支 相信你有过这样的经历:面试了很久,也面试过 N 家公司,最后一份录用通知书也没有收到,此时是否开始对自己的工作能力表示深深的怀疑,真的是自己不够努力吗?之前的工作能力真的那么水吗?有没有想过也许就是与招聘人员沟通时的语气,与部门负责人的一次错误的意见表达等,导致公司对你的看法完全改变了,进而错过了接受心仪公司录用通知的机会。 这时候的你是不是感觉面试的道路上充满了坎坷,对自己的能力也产生了深深的质疑? 其实求职之路并没有想象的那么复杂,接下来我会从一个面试官的角度来告诉你在简历上需要注意的细节点以及哪些内容是必须要提到的;在面对面试官时如何张弛有度地展示自己,在沟通中如何让面试官感觉到你就是那个公司一直在寻找的人选。 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\346\212\212\346\217\241\351\235\242\350\257\225\346\227\266\347\232\204\345\205\263\351\224\256\347\202\271.md" "b/docs/\351\235\242\350\257\225/\346\212\212\346\217\241\351\235\242\350\257\225\346\227\266\347\232\204\345\205\263\351\224\256\347\202\271.md" new file mode 100644 index 0000000..ba1f004 --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\346\212\212\346\217\241\351\235\242\350\257\225\346\227\266\347\232\204\345\205\263\351\224\256\347\202\271.md" @@ -0,0 +1,101 @@ +# 把握面试时的关键点 + +## 面试前的准备工作 + +先说说面试前的准备吧。常规的准备相信你一定知道,比如制作一份吸引 HR 的简历、穿一身体面的衣服、整理一下自己的发型等。简历相关的准备前面已经详细讲过,这里就不多介绍了。 + +下面说说穿着相关的准备,很多小伙伴认为面试时的穿着并不是很重要,面试官肯定更看重个人魅力和知识的储备。当然这么说是没错的,但如果你和面试官首次见面,在还没有开始正式聊天之前,他是无法感知你的个人魅力或者知识储备的。 + +假如第一次见面就看到邋遢的外表或者奇怪的着装,面试官会怎么给你贴标签呢?首先他一定会认为你并不尊重这次面试,给他造成一种没有礼貌的印象;然后就是被你身上的味道熏倒无法和你多交流;最后根本来不及了解你的个人魅力和知识储备就草草地结束了这次面试。相信这个结果一定不是你想碰到的吧?所以,干净得体的着装是面试非常重要的一个环节。 + +面试官也会通过你的着装去判断你的性格,以及判断与公司的文化、团队的气氛是否匹配。这时可能你会问:我也没有进入到这家公司和团队,该如何判断面试当天穿什么衣服才符合这个公司的文化或者符合这个团队的气氛呢?当然,我们没有办法做到“把面试官的感受照顾到很细”的层面。 + +但是不同的穿着一定会表现出你的性格,有些表现出来的性格可能不会被大众所接受的,希望可以回避一下。下面简单说说几种可以表现性格的穿着: + +喜欢穿简单朴素衣服的人,往往给人的印象是性格比较沉着稳重、为人比较真诚和随和,无论是在工作或学习上,还是在生活中,会给人一种勤奋好学、诚实肯干的感觉; + +喜欢穿样式繁杂、颜色多样、花里胡哨的衣服的人,多是虚荣心比较强,爱表现自己而且又是乐于炫耀的人,会给人一种性格有些飞扬跋扈的感觉; + +喜欢穿浅色衣服的人,性格比较活泼好动,十分健谈,会给人一种喜欢交朋友的感觉; + +喜欢穿深色衣服的人,性格比较稳重,显得城府很深,会给人一种比较沉默,做人做事深谋远虑的感觉。 + +如果你希望在面试中表现的不是那么具有攻击力或者给人比较亲和、稳重性格的话,建议穿简单、朴素、纯色的衣服,会显得整个人比较清爽,且比较容易亲近,相信面试官也愿意和你多聊几句。当然不仅穿着干净,而且一定要注意个人卫生,最好不要让自己身上的体味过重或者使用太重味道的香水。化妆时,不建议浓妆艳抹,自然的淡妆让自己看起来很精神就可以。 + +## 如何全面的介绍自己 + +接下来就是面试的过程了,首先面试官会说:“请简单介绍一下自己。” + +面试官有两个目的:(1)希望通过你的简单描述可以和简历上的经历做校对;(2)通过简单地介绍来看看你的逻辑和总结能力如何。所以自我介绍也是非常重要的一个环节,好的自我介绍一定要做到以下几点。 + +### 面试时的自我介绍 + +一定要把握住时间。面试时的自我介绍一般控制3~5分钟最合适,尽量不要超过10分钟。时间过短说明你根本没有清晰的介绍自己,这时面试官很难了解你到底做了什么;时间过长可能很多内容不是面试官需要的信息,这时大部分的面试官会主动打断你,从而留下了不太好的印象。 + +那如何把握好时间呢?建议在介绍时包含以下几个部分就好:(1)情况介绍,包括教育经历;(2)工作经验的介绍;(3)介绍最有价值的经历。这样的一个自我介绍应该可以很好的控制在5分钟左右了,既可以让面试官清晰的了解你的情况,也能表现出你的优势。 + +### 面试过程中需突出的几个点 + +在面试过程中一定要突出以下几个点:做过什么、有哪些工作业绩、优势是什么,这样可以很好的突出自己。 + +做过什么:介绍自己,把自己曾经做过的事情说清楚,每段工作对应时间节点的公司名称、担任职务、工作内容等,尤其是对最近两份工作做过的事情要重点说说,较早之前的工作经验,或者学习的经验可以一带而过,要把握“重点突出”的原则。 + +有哪些工作业绩:把自己在不同阶段做成的有代表性的项目经验介绍清楚,但是一定要注意:(1) 应与应聘岗位需要的能力相关的业绩多介绍,不相关的一笔带过或不介绍,因为面试官关注的是对用人单位有用的业绩;(2)要注意介绍你个人的业绩而不是团队业绩,要把自己最精彩的一两段业绩加以重点呈现。当然也要做好充足的准备,可以迎接面试官的提问。 + +突出自己的优势:注意介绍自己的优势一定要与应聘的岗位密切相关,主要是围绕自己专业特长来介绍。除专业特长以外的特长,特别突出可以介绍,但要点到为止。 + +举个例子:你好,我是某某,2018年3月加入XXX公司,担任产品经理一职,主要负责公司核心产品的规划和设计工作;在这段期间,我独立完成过XX项目的产品跟进和上线的工作,将产品的数据提升了30%,业绩突出,获得了公司的认可。在项目中,我通过学习和与外部专家的沟通,获许了XXX新策略的信息,并积极尝试,达成了我的目标。 + +### 每段工作的离职原因 + +在面试的过程中一定要突出自己职业规划的逻辑性,也就是说需要让面试官感受到你的每次工作变动都是为了个人成长以及有规划的进行变动。所以在表述的时候最好可以清晰地说出你在每段工作中的收获和成长点,当然如果在陈述这些内容时可以体现出你的个人思考,就更是画龙点睛了。 + +## 如何回答面试中的问题 + +相信你经常会碰到面试官问以下的问题,这些问题也是面试官给你的一些考验,如果更好地回答这些问题可能会成为你入职心仪公司的敲门砖。 + +### 你为什么选择我们公司? + +这个问题相信不少小伙伴遇到过,可能你的原因是随便投递、公司离自己住的地方近、工资给的高、公司不加班、公司有各种补助等。如果这些答案出现在你的面试回答中,那 HR 会重新考虑是否要录用你了。 + +所以在回答这个问题时需要有一些准备: + +可以先描述一下自己的能力与岗位要求的契合度,表现出在公司提供的岗位上有机会可以一展所长; + +说出几个被企业所吸引的优点,这些优点能为以后的工作带来什么好处; + +自己的职业发展与公司前景作出总结。 + +相信这些回答可以很容易抓住面试官的心,不过前期也是需要你对这家企业,以及所招聘的岗位做了一定的功课。 + +### 你为什么从上家公司离职? + +也许你在前公司受到了委屈、也许前公司人事关系复杂所以离职,但无论前公司有多么的糟糕,都千万不能在面试时说出来。因为你在上家公司离职的原因,会使面试官联想到你会不会因为在新公司受到委屈而轻易离职?再者,面试官其实并不关心你为什么要离职,所以面试时只需要给在场所有的人一个都可以接受的答案就可以了。 + +例如,可以这样回答:为了更好的发展,所以选择离职。切记在回答这个问题的时候,不能贬低前公司、不要损害前领导的形象。 + +### 你的优点和缺点是什么? + +相信很多小伙伴对这个问题都很头疼,自己的优点说的太多会让面试官感觉过于自大,可在面试的过程中又有谁愿意说自己的缺点呢?下面列举几个简单的方向,希望可以帮助你解决这个尴尬的困境。 + +优点:可以结合过往的工作经历和工作业绩等讲述一下自己的优势。例如,我曾经参加过某某项目,相信我的这个工作经验可以很好的帮助到公司解决什么方面的问题等。当然也可以通过一些例子说明自己的人品或性格方面的优势,哪家企业可以拒绝一位性格和能力都很好的候选人呢? + +缺点:金无足赤、人无完人,要勇敢的面对自己的缺点,可以向面试官说明,你针对自己的缺点做了哪些改变,以此来说明你正在积极地改变自己去成为更优秀的人。 + +### 未来3年或5年,你的职业规划是什么? + +当面试官问到这个问题时,是希望看到你的自我学习力和未来牵引你的职业动力是什么。对职业规划不清晰的人,很难获得成功,也不会在一个岗位上待很久,所以也不是公司最合适的人选。 + +当被问到你的职业规划是什么的时候,此时可以设定一个短期就能实现的规划和一个未来希望实现的目标。 + +例如,我希望可以在未来的 1 ~ 2 年内,梳理和参与到几个完整的项目中,从中学习和看到整个项目进度是什么样的,从而提升自己的工作能力和项目经验。在未来的 3 ~ 5 年内我希望可以独立承担项目,做一个可以让大家都能使用并且体验良好的产品出来。 + +这样的回答,在短期规划上会让面试官认为你是一个脚踏实地,希望可以通过学习而成长的人,而且也在积极的改变自己;在长期规划上也能让面试官感受到你对这份工作的热情,具有很强的成就动机。 + +### 在选工作中更看重的是什么? + +很多小伙伴反馈,这个问题很难回答,其实也能想到面试官肯定更看重你的是个人成长和发展空间。当然也许你的内心想的是涨薪或者培训,虽然薪资是一定的,但是如果让面试官认为你是一个物质的人,并没有长久的培养空间,那面试的结果就可想而知了。 + +### 你还有什么问题吗? + +这是面试结束前的最后一个问题,也可以认为是个形式问题或走个流程,此时可根据前面面试过程中的表现程度来适当的提问,比如公司福利、上下班时间、团队氛围、个人岗位发展等,但尽量不要问从网上就能查到公司信息的问题。 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\346\215\225\346\215\211\351\235\242\350\257\225\345\256\230\345\276\256\350\241\250\346\203\205\357\274\214\345\201\232\345\207\272\345\272\224\345\257\271\347\255\226\347\225\245.md" "b/docs/\351\235\242\350\257\225/\346\215\225\346\215\211\351\235\242\350\257\225\345\256\230\345\276\256\350\241\250\346\203\205\357\274\214\345\201\232\345\207\272\345\272\224\345\257\271\347\255\226\347\225\245.md" new file mode 100644 index 0000000..ead10c2 --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\346\215\225\346\215\211\351\235\242\350\257\225\345\256\230\345\276\256\350\241\250\346\203\205\357\274\214\345\201\232\345\207\272\345\272\224\345\257\271\347\255\226\347\225\245.md" @@ -0,0 +1,63 @@ +# 捕捉面试官微表情,做出应对策略 + +在面试的过程中,如何判断自己所陈述的信息是面试官感兴趣的呢?怎么才能在恰当的时间更好地展示自己擅长的内容呢?同样,如何在不恰当的时候适可而止,更好地转换话题呢?此时需要精准地捕捉到面试官的微表情,以便在合适的时间突出自己。 + +## 沟通时需注意的小细节 + +你有没有经历过这样的窘境:当在描述项目经验时,突然被面试官打断了,虽然此时你正在兴头上,但也请你马上停止,面试官的打断说明他对你的这段经历比较了解,或者刚才的这段描述有了自己的判断,所以要想想刚才的描述是否有漏洞,如果有机会建议再重新解释一下。 + +有时候也能发现面试官重复提问同样类型的问题,说明他对你之前回答的问题有质疑,希望可以通过重复提问的方式,再次确定这件事情的真实性,此时需要你给出不同的答案或挑选重点内容来回答,如果没有察觉,很有可能就错失了这次机会。 + +甚至有时候面试官针对某个项目经验进行深入提问,不断地细化你所做的项目数据。这时一定要提高警惕,因为面试官对你的这段经历比较感兴趣,需要通过非常细致地提问,才能了解你在这个项目中真实参与的程度和担任的角色。如果你的回答不够细化或者给出的数据不够精细,那么很容易被误解为并没有参与这个项目的核心内容。 + +## 面试时需留意的微表情 + +观察面部微表情可以解读很多信息,进而可以判断面试官是否真的对你所说的内容感兴趣,下面来讲讲面试过程中常见的几个微表情。 + +当看到面试官的下嘴唇往前撇时,说明他对接收到的信息持有怀疑的态度, 此时需要转化角度或思路来陈述。 + +当看到用牙齿咬嘴唇的时候,说明面试官正在仔细听你的介绍,同时也在默默的思考你所表达的另一层含义是什么。 + +当看到面试官调整自己的坐姿时,比如身体向前移动,很有可能对你所讲的内容很感兴趣;如果发现面试官身体逐渐的后退,说明很有可能对你的这段介绍没有兴趣聆听,此时要及时的调整陈述的思路。 + +当发现面试官双臂交叉时,这是防卫的一种表现。很有可能你说的内容与他的认知完全不相符,也表现出面试官对你表述的观点完全不认同或者完全没有听懂你所表达的意思。 + +上面简单讲了一些面试过程中可能碰到的情况,希望你在以后的面试过程中及时捕捉到面试官的微表情,以做出应对的策略。 + +其实很多时候面试官也在捕捉你的面部表情。当你在阐述的过程中,面试官在倾听是否有漏洞,也许不经意的某个动作或者某句话,也能让面试官察觉到你的问题点,然后做出不一样的决定。所以管理好自己的微表情也是非常有必要的,下面我们来说说在面试时做出的一些不经意的错误微表情有哪些。 + +## 面试时需改掉不好的习惯 + +有时候一个沟通时的习惯,也能透露出一些问题,比如: + +当习惯说“啊”、“呀”、“这个”、“那个”、“嗯”等口头语时,一般给人留下词汇量小或者思维慢的印象,在说话时需要利用间歇的方式让自己思考; + +沟通时喜欢使用中英搭配,这样很容易给人一种虚荣心比较强、好表现或夸耀自己的错觉; + +如果口头禅出现频率过高的话,很容易给人一种办事不干练、意志不坚定的印象。 + +当然说话声音的大小或者语速的快慢等这些信息也能让面试官初步判断你是一个什么性格的人。 + +说话声音的大小和一个人的性格联系非常紧密,喜欢大声说话的人,其性格比较以自我为中心,积极主动、行动力和支配欲强,也就是富有攻击性的一类人;说话声音小的人其性格比较偏内向,考虑的因素比较多,很压制自己的情感。 + +语速快慢和声音大小一样,一般语速快的人性格比较外向,有冲劲且有活力,但是常常给人一种紧张和压迫感,让人有种焦躁、混乱甚至有些粗鲁的感觉;但是语速慢的人容易让人感觉比较木讷,容易犹豫不决,甚至有时候有消极悲观的想法。 + +所以说需要根据你所从事的工作或者要应聘的岗位来调整自己说话的方式,才更能获得面试官的青睐。假如你是一位声音小而且语速慢的人,去面试一家公司的销售岗位,相信这家公司不会对你抛出橄榄枝,因为他们很难从你的沟通中看出你的销售潜力。 + +## 面试中透露出的动作,也需要多多留意 + +很多小伙伴可能没有留意在面试过程中做的一些小动作,也许就是这些小动作导致面试官对你的印象减分。下面简单说几个常见的小动作,希望可以帮到你。 + +吐舌头:一般在感受到有压力时,舌头不自觉地做出舔嘴唇或者看似是在舔嘴唇的动作,说明是对自我的一种安慰。如果做了这个动作说明当时你备感尴尬,希望可以缓解一下气氛。 + +用手捂住嘴巴:这个动作一般表示自己对刚刚说的话已经意识到了错误,下意识的去捂住自己的嘴巴。 + +十指交叉:这个动作很可能是自信的表现,也有可能是在掩盖你的紧张。如果你的十指无意识的交叉在一起,而且眼神也在躲避面试官,很有可能是怕面试官发现你的紧张。 + +抚摸颈部:这个动作说明你并不是很自信,当然也有可能是你正在释放压力,这是一种普遍有力的信号,说明大脑正在积极处理某种消极的情绪。 + +眼神躲避:很多候选人在面试的过程中,经常左顾右看,躲避面试官的眼神,给面试官一种心虚的表现。 + +当然还有一些其他的小动作,比如揉鼻子、挠头或者摸耳朵、翘二郎腿或抖腿、常扶眼镜、玩弄随身小物件、咬指甲等,这些小动作也都说明你比较紧张或者不够自信。 + +希望你可以通过这一课时的学习,合理地控制一下自己的微表情,在面试时更好地表现自己。 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\347\233\256\346\240\207\346\230\216\347\241\256\357\274\214\351\230\220\346\230\216\346\262\237\351\200\232.md" "b/docs/\351\235\242\350\257\225/\347\233\256\346\240\207\346\230\216\347\241\256\357\274\214\351\230\220\346\230\216\346\262\237\351\200\232.md" new file mode 100644 index 0000000..6104b98 --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\347\233\256\346\240\207\346\230\216\347\241\256\357\274\214\351\230\220\346\230\216\346\262\237\351\200\232.md" @@ -0,0 +1,31 @@ +# 目标明确,阐明沟通 + +## 知道自己想要什么 + +在开始谈薪资之前,需要明确自己到底想要什么,希望在这次的工作变动中有什么收获,比如想在团队氛围很和谐的公司里工作、希望积累更多的项目经验、还是仅仅为了涨薪等。 当你明确自己想要的是什么,同时清晰的表达出来以后,HR 会根据你的需求去匹配这个职位是否可以给到你所期望的东西,或者你也可以直接询问 HR 来判断是否能得到你想要的。 + +很多小伙伴会说,如果我确实不知道自己这次换工作想要得到什么,该怎么办? 你可以参考在之前的工作过程中,是不是有让自己感觉不舒服或者有挫败感的时候,同时想想是什么原因造成的,然后把它们整理出来写在纸上,标出来哪些是你希望可以得到改善和需要得到成长的。 + +通过这样的方式,再去想想在面试的过程中或者在和面试官沟通的过程中,这家企业是否可以给到你想要的东西。 + +## 明确自己的优势 + +明确了自己想要的,也要知道你能给企业创造出什么样的价值,这样才可以在薪资上做更有利的争取。面试结束后,HR 已经对你的表现做了公正的评价,相信这些评价一定都是你对企业有价值的地方。 那怎么评估自己优势的价值呢? 首先要明确,你的优势是软性的优势还是硬性的优势: + +软性的优势是指性格方面(比如性格好、踏实),相信很多人都具备,所以这个优势的价值可能就没有那么大的竞争力; + +硬性的优势是指之前的工作经历给到的优势(比如项目经历、专业经历),这样的优势相比软性优势要更有竞争力 + +其次如果你的硬性优势又是比较稀缺的项目经验或者专业经历的话,那就更有竞争力了。 + +## 不要敌化与HR的关系 + +在谈 Offer 的时候,最终肯定会落在谈薪资的问题上,你是不是也会有这样的感觉:明明招聘网站上写的很高,HR 却说给不了这么高? 因为招聘网站上显示的薪资范围,为了吸引用户会稍微提高一点薪酬水平。但是主要确定你薪资的并不是网站上的薪酬范围,而是你的真实能力。因为 HR 会根据你的真实能力去判断你在什么薪酬档位上,然后给出你合适的薪资。所以并不是 HR 不愿意给你高薪,要判断自己是否具有拿到高薪的能力。 + +如果 HR 问你期望的薪资是多少,该怎么回答呢? 如果你不清楚企业的薪酬结构,可以考虑给 HR 说一个年薪的的范围。但是建议提出的薪资涨幅不要超过你之前薪资的 20%,当然如果你特别优秀或者岗位是非常紧俏的岗位可以考虑多要一些涨幅,但是也不要太离谱。 + +因此,不管怎么谈薪资,建议你都要明确心态,尽量不要把 HR 当作自己的敌人,因为 HR 也有指标,也希望优秀的候选人可以顺利接受 Offer。 把 HR 当做朋友,先以平和的心态和他确认一下自己的各种疑惑或者不清晰的信息,然后再清晰地表达出期望得到什么,或者也可以让 HR 说一下他们可以给到你的都有什么,然后给彼此一些时间考虑一下(最好不要超过一周,不然会错失这个机会),相信这样的沟通方式大家都比较愉快 + +## 坦诚地沟通 + +前面经历了多轮的面试和沟通后,最后在沟通 Offer 的时候同样要表现出你的真诚,不要在最后的关键时刻给到 HR 比较滑头的感觉, 不然结果会前功尽弃。 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206.md" "b/docs/\351\235\242\350\257\225/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206.md" new file mode 100644 index 0000000..e1a30e7 --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\350\256\276\350\256\241\344\270\200\344\273\275\345\220\270\345\274\225\351\235\242\350\257\225\345\256\230\347\232\204\347\256\200\345\216\206.md" @@ -0,0 +1,91 @@ +# 设计一份吸引面试官的简历 + +> 简历是什么?可以理解成:如何花两分钟初步了解你? + +## 简历模板 + +简洁干净的纯色底简历模版可能是最好的选择。面试官也会把更多的注意力放在简历内容上,从而可以更好地判断你的经历是否与工作内容相符。 + +下面是一个比较清晰的简历模版,可供参考。整体简历的排版清晰;文字描述段落清晰和明确;重点比较突出。 + +![简历模版](./assets/设计一份吸引面试官的简历-简历模版.png) + +## 组成部分 + +分为这几部分进行阐述:个人信息、教育情况、工作经历、项目经历、自我评价、其他信息。 + +### 个人信息 + +很多小伙伴在构思自己个人信息的时候,很喜欢把自己完全展示出来,如身高、体重、性别、民族、婚姻情况、政治面貌、家庭住址、籍贯等,统统都写在简历上。 + +但有没有思考过:这些信息是面试官所需要的吗?当然,特殊岗位可能需要提供身高、体重、籍贯、民族或者政治面貌。但是在如今这个互联网公司占据了很大就业市场的情形下,并没有太多的互联网公司在乎这些比较表面的信息,面试官往往更在乎的是姓名有没有写完整、电话号码和邮箱地址是否填写正确等,只有这些信息完整,面试官才可以快速地联系到你。其次面试官也会留意你的求职意向是否与应聘的职位相符,以及期望薪资是否在应聘岗位的范围内等。 + +![个人信息反例](./assets/设计一份吸引面试官的简历-个人信息反例.png) + +上图的简历过于繁琐,罗列的很多个人信息不是 HR 所需要的,而且很难快速地找到个人的联系方式和邮件信息,这样会给招聘人员带来很大的困扰。简单的突出自己的关键信息既可以让招聘人员轻松的获取,也能让招聘人员认为这是一个逻辑清晰的候选人。 + +![个人信息示例](./assets/设计一份吸引面试官的简历-个人信息示例.png) + +### 教育情况 + +首先,建议求职的小伙伴在教育情况的部分只需要体现出正规被认证的学习经历即可,比如本科、研究生、博士生等的经历。很多小伙伴会将自己的高中,甚至初中的学习经历都要写上,也许你的初中、高中学校在家乡非常有名,但是对于企业来说很难判定和考究。因为学信网可查的学校只有大学院校,所以只需要填写高中毕业后的教育经历就好。 + +其次,在填写学校经历的时候只需要突出就学时间(入学时间—毕业时间)、学校名称、专业名称、学历(统招或非统招)即可。对于企业方可以很容易通过就学专业和学校判定出来你所学的内容,而不太需要你做过多的说明和解释。经常看到一些刚刚踏入职场的小伙伴在自己学校和专业信息下面会长篇大论地介绍自己的专业以及学习的科目,往往是非常徒劳无功的表现。 + +最后,部分小伙伴也会分享在校期间获得的所有证书和参与的活动,相信很多面试官在这里的停留不会超过5秒,所以,即使展示自己学习有多好也不会吸引太多的注意。倒不如在这里保持一个整洁干净的排版,将自己获得的证书或者参与自己擅长的活动信息挑一些重要的放在最后的其他信息中,这样也能给简历加分。如果觉得这里是否有些单调,可以放一些真正具有含金量的奖项。 + +![教育经历](./assets/设计一份吸引面试官的简历-教育经历.png) + +![教育背景](./assets/设计一份吸引面试官的简历-教育背景.png) + +![教育经历2](./assets/设计一份吸引面试官的简历-教育经历2.png) + +### 工作经历 + +工作经历往往是企业方非常在乎的一部分,这一块也是招聘方停留时间最长的地方。在写工作经历的时候一定要清晰标注这几个内容:公司名称、工作时间(开始时间—结束时间)、职位名称、工作内容、业绩成果。 + +a. 公司名称。目前很多互联网公司的对外名称和他们的注册名称完全不同,所以在书写公司注册名称的时候建议在后面也标注一下公司的对外名称,也是帮助面试官判定你上一家所在的公司行业和领域,因为很多互联网公司的注册名称普及度并不是很高。 + +b. 职位名称。公司名称后面建议标注所在公司的职位名称,职位名称可以写上职位的层级 + 具体做的事情,如招聘专员、招聘主管、招聘经理、招聘总监。往往面试官会根据岗位层级看你简历内容负责的事情是否与层级相符,进而来判定你的能力。目前大多数公司内部都有岗位职级,可以在职位名称后面标注上自己目前的职级,以方便面试官清晰的判定。 + +c. 工作时间。建议具体到年、月,如2016.07~2019.03,这样的书写方式,面试官可以很清晰的了解你的工作年限。 + +提示:在写工作时间的时候一定要注意每段工作时间之间的结合期是否连贯。如果没有连贯,且中间相隔的时间周期比较长,最好增加一个说明,为什么会有那么长的空白期、这段时间在做什么等。如果连续几段经历都出现这样的断档,需要慎重思考一下是不是要改变已有的工作习惯。 + +如果有很多段经历,建议优先写最近的经历,然后采用倒叙的顺序来写比较合适,因为大部分企业方习惯从最近的一份工作去了解。 + +d. 工作内容。接下来我们聊一下简历中最重要的一个部分,也就是工作内容的描述。我知道很多小伙伴在写职位描述的时候,习惯的做法是借鉴与自己相同岗位的职位 JD,甚至有的小伙伴直接把大公司的职位 JD 粘贴到自己的简历中,表示自己做过相关的工作。但有没有仔细阅读过这些岗位描述?往往企业在发布岗位描述的时候希望招的都是按照最全面的要求去撰写的,但是候选人的能力很少具备全部的要求,如果复制粘贴,很容易被 HR 怀疑简历的真实性。所以最好的做法是在设计简历之前,先认真的思考一下自己上一段的工作内容是什么,然后把这些工作内容按照重要程度依次精简描述出来,写到简历中。 + +当然,也有小伙伴说:我不太擅长文字的书写怎么办?没关系,如果你已经清晰知道自己的日常工作是什么了,也可以借鉴企业招聘职位中的描述。假设你是一位销售助理,日常的工作为: A.拜访客户与客户进行销售产品的沟通 B.收集客户的资料 C.日常的文档整理、合同的归档。对于 HR 来说,A 和 B 的内容更为重要,所以放在前两条来展现自己日常工作的重要性,两件或多件内容中间要做好分段,描述完一件工作内容后记得另起一段来描述下一个工作职责。 + +如果有多份工作经历,且工作内容都比较相似的话,最好有一个递进的关系,每一段突出一个工作重点。 + +来看下下面的一个简历截图,能看出里面的问题吗? + +![工作经验](./assets/设计一份吸引面试官的简历-工作经验.png) + +有以下几个问题点:工作时间倒序,并非HR的日常阅读习惯;两段工作经历之间留有1年时间的空档期,很容易让HR表示怀疑;工作描述过于简单,几乎没有突出自己工作的亮点。 + +e. 业绩成果。如果工作中有过一些公司认可的奖状或者某一期的绩效非常优异的话,可以在“工作内容描述”后面增加一栏“业绩成果的展示”,但是这一栏的内容不易过多,把最重要或者很有价值的公司奖项或绩效按照重要程度精简出 1 ~ 3 条即可,如「公司年度优秀员工奖」、「上季度绩效为 A」等。 + +### 项目经历 + +项目经历其实和工作内容描述表现的形式类似,只不过项目经历不用把每段都写在简历里,只需要选出一些自己作为主要参与人或者由自己负责的项目添加在简历里就好了。 + +项目经历的展示也需要体现出项目名称、项目时间、项目中担任的职务、项目职责和项目业绩。如果补充项目经历的介绍,则会给简历加分,也能更容易打动面试官。 + +### 自我评价 + +不要小看自我评价哦,这一部分是上述简历整体的一个总结,大部分面试官很希望从自我评价中整体了解候选人的情况。 + +所以,建议从两个方面整体的评价自己:首先先对自己过去的工作内容或者过去的学习经历做个总结;然后对自己的个性以及工作态度做一个工作展示。 + +比如下面的「个人优势」其思路比较清晰: + +![个人优势](./assets/设计一份吸引面试官的简历-个人优势.png) + +上面的自我评价是一个可参考的模板:候选人不但突出了自己的优势,而且还清晰地展示了自己的学习主动性和学习能力,进而会给招聘人员留下很好的印象。 + +### 其他信息 + +这里如果真的写不出来,可以忽略,当然如果有一些非常值得或者有帮助的奖项亦或培训内容也可以在这里展示,比如某某专栏的发表、CPA的认证、司法考试的证书等。 \ No newline at end of file diff --git "a/docs/\351\235\242\350\257\225/\350\257\273\346\207\202\345\262\227\344\275\215\347\262\276\345\207\206\346\212\225\351\200\222.md" "b/docs/\351\235\242\350\257\225/\350\257\273\346\207\202\345\262\227\344\275\215\347\262\276\345\207\206\346\212\225\351\200\222.md" new file mode 100644 index 0000000..16ec34c --- /dev/null +++ "b/docs/\351\235\242\350\257\225/\350\257\273\346\207\202\345\262\227\344\275\215\347\262\276\345\207\206\346\212\225\351\200\222.md" @@ -0,0 +1,39 @@ +# 读懂岗位精准投递 + +## 清晰的了解用人部门的招聘要求 + +上一讲是关于如何设计简历,相信大部分小伙伴可以很轻松的掌握,但这只是投递简历前的一个开始,明确工作方向才是真正的关键点。 很多小伙伴在投递简历的时候,很少仔细阅读招聘公司的招聘简章,只是投递出去了,甚至一个岗位投递多次或者一个公司投递多个岗位,尤其在刚刚毕业的小伙伴身上表现非常明显。可能大部分人会认为多投递几次会被面试官看中的几率更高,但往往是相反的,如果你自己的工作内容与职位的内容完全没有关联,只会被 HR 淘汰掉,然后放进简历库,而且目前很多公司在简历库里都会对投递来不合适的简历做备注,下次使用简历的时候面试官也会查看上次的评价。 + +Tips:如果不想在心仪的公司里面留下污点,建议谨慎地投递简历。 + +需要注意,投递的内容除了职位描述以外,还需要留意公司对人才的工作年限要求和工作的职级。例如,经常看到一些刚刚工作 1 年左右的小伙伴,直接投递公司需要 8 年以上要求的总监岗位。这样会给 HR 留下非常不好的印象,会认为这份简历的候选人对自己的定位非常不清晰,甚至有时候会觉得比较浮躁。 + +第一印象一旦形成是非常难改变的,所以建议各位小伙伴在投递简历时一定要做好两个准备: + +要对投递的公司做好充分的了解,清晰地知道自己投递公司的业务是否和自己的发展方向一致; +投递的岗位是否与你之前的工作内容相符合。 +一份满足以上条件的简历才会获得用人公司的邀请。 + +下面是一个某公司“产品经理”的招聘要求: + +![pm招聘要求](./assets/读懂岗位精准投递-pm招聘要求.png) + +通过上面的职位描述,有几点可以注意一下: + +在职位描述中,不断提到需要具有“独立负责…”、“负责具体…”等字样,说明这个岗位需要你能独立完成一些项目。如果你还是一个需要别人带着干活的话,最好不要盲目投递哦。 + +职位要求年限,这是一个要求有工作经验的岗位,比如需要具备 3 ~ 5 年的工作经验。往往 HR 希望求职者最好有 5 年左右的工作经验,具有 3 年工作经验是最低的要求。如果你的工作经验低于 3 年的话,就不要考虑尝试了,即使投递了也会被刷掉。 + +任职要求中的第4点,说明这份工作的强度非常大,成长性也很高。如果你是一个不希望太大工作强度或者想找一份轻松工作的小伙伴,也要好好的思考一下哦。 + +建议:在投递简历的时候一定要注意 HR 在职位描述中的用词和一些细节,这样才能更准确地投递到心仪的岗位,进而获得一份满意的录用通知书。 + +## 最合理的助攻来自自己内心的力量 + +好的简历内容并不是通过简单地编写就可以实现了,一定是通过每天的努力工作和不断地反思才实现的。所以,即使简历构思再完美、逻辑再缜密,如果不是自己亲身经历和努力付出过的项目经验都会很容易的被发现漏洞。 + +一定要认真的对待自己的工作,每次的工作变动也要对自己的职业规划做好充分思考,这样才会拥有一份完美的简历和一份完整的职业规划。 + +可能很多刚刚毕业的小伙伴会问:“我没有工作经历,也不知道自己做什么。如果我不去尝试怎么可能知道自己适合什么呢?”其实很多小伙伴在大学读的专业也就已经明确了自己可以从事的方向,当然如果你认为自己读的专业不是你喜欢的,也许可以选择管培生的岗位,用 1 年的时间去体会各个岗位也许会对你有所帮助。 + +而对于工作 2 ~ 5 年的小伙伴,相信你们已经在一个岗位上至少工作了 2 年以上,如果这个时候还在反复,此时需要慎重的思考一下自己的规划了。 \ No newline at end of file