MySQL 实战 45 讲

MySQL 实战 45 讲 | 林晓斌 01 | 基础架构:一条 SQL 查询语句是如何执行的? 请求自上而下经过 Server 层 → Engine 层。Server 层承担连接、解析、优化、执行,所有引擎共享;Engine 层负责数据存取,可插拔。 组件 职责 备注 连接器 建立 / 鉴权 / 维持连接 长短连接由客户端控制,服务端 wait_timeout 兜底 查询缓存 命中即直接返回 8.0 已移除;表一更新整张缓存失效,不建议用 分析器 词法 + 语法 + 预处理;列 / 表名校验在此发生 报 Unknown column 'k' in 'field list' 就在此 优化器 选索引、决定 join 顺序 执行器 鉴权后调引擎接口;返回结果集 引擎扫描行数 ≠ rows_examined(见 §05) 常用查询 SHOW PROCESSLIST; -- 当前连接 SELECT @@wait_timeout; -- 28800s = 8h SHOW VARIABLES LIKE 'wait_timeout'; SELECT @@query_cache_type; -- 8.0 之前可观察是否开启 清理连接占用的资源但保留连接:调接口函数 mysql_reset_connection(),不是 SQL 语句。 ...

August 14, 2022 · 5 min · 991 words · Me, LLM

回顾 2021

直到对的人来 · 追星族乐队 我想跟他说:那家餐厅太美了,我一定要和你去一次。但是那个菜其实很一般的,那个老板娘戴的耳环特别漂亮。 – EOF –

December 31, 2021 · 1 min · 7 words · Me

设计模式之美

引言 学习设计模式要回答两个问题: 什么是好代码? —— 见 §02。 怎样写出好代码? —— 面向对象、设计原则、设计模式、编程规范、代码重构这五件套各管一段,见 §03。 KISS 原则人人会背,难的是判断"多简单算简单"。后面所有内容都在给这类判断提供尺子。 02 代码质量:把 30 个形容词压成 2 个问题 书里列了 30 多个评价代码的英文词。它们都可以归到三层: 层面 关心的问题 代表属性 编码层 当下能不能读懂 readability、simplicity、clean 模块层 改动能不能控制住 maintainability、testability、reusability 系统层 长期能不能演进 extensibility、flexibility、scalability 剥到最里面,只剩两个问题: 改一行,要扫几个文件? —— 决定可维护性、可测试性。 加一个新需求,是插一段,还是重写? —— 决定可扩展性、灵活性。 其余术语的定位: flexibility 是结果不是属性:易扩展 / 易复用 / 易用三者满足任一即可称"灵活"。 elegant / good / clean 偏主观,不构成评价维度,适合表态不适合度量。 robustness / reliability / scalability 描述运行时行为,属于架构而非代码质量。 DRY 是手段不是目标:复用是结果,过早抽象反而降低可维护性。 边界:可读性和可维护性常被混用。可读性是"读"的成本,可维护性是"改"的成本。一段代码可以读得懂但改不动(逻辑分散在 10 个文件),反之少见。 03 五件套:面向对象 / 设计原则 / 设计模式 / 编程规范 / 代码重构 工具 解决什么 抽象层次 关键内容 面向对象 复杂程序的组织方式 最高 封装、抽象、继承、多态(§04~§10) 设计原则 写代码时的判断依据 高 SOLID、DRY、KISS、YAGNI、LOD 设计模式 反复出现的设计问题 中 23 种,分创建型 / 结构型 / 行为型 编程规范 可读性 低 命名、注释、函数长度、模块划分 代码重构 代码质量不下降的兜底 低 单元测试为前提,分大重构 / 小重构两种规模 依赖关系: ...

February 5, 2021 · 5 min · 984 words · Me, LLM

PHP 月份加减问题

看现象 var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2020-07-31")))); // string(10) "2020-08-31" 符合预期 var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2020-05-31")))); // string(10) "2020-07-01" 不符合预期,预期 2020-06-30 var_dump(date("Y-m-d", strtotime("-1 month", strtotime("2020-02-29")))); // string(10) "2020-01-29" 符合预期 var_dump(date("Y-m-d", strtotime("-1 month", strtotime("2020-03-31")))); // string(10) "2020-03-02" 不符合预期,预期 2020-02-29 // Carbon\Carbon Carbon::parse("2020-07-31")->addMonth()->toDateString(); // "2020-08-31" Carbon::parse("2020-05-31")->addMonth()->toDateString(); // "2020-07-01" Carbon::parse("2020-02-29")->subMonth()->toDateString(); // "2020-01-29" Carbon::parse("2020-03-31")->subMonth()->toDateString(); // "2020-03-02" // 结果与 strtotime 一致。 原因 var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2020-05-31")))); // string(10) "2020-07-01" date 内部的处理逻辑: 2020-05-31 做 +1 month 也就是 2020-06-31。 再做日期规范化,因为没有 06-31,所以 06-31 就等于了 07-01。 var_dump(date("Y-m-d", strtotime("2020-06-31"))); // string(10) "2017-07-01" var_dump(date("Y-m-d", strtotime("next month", strtotime("2017-01-31")))); // string(10) "2017-03-03" var_dump(date("Y-m-d", strtotime("last month", strtotime("2017-03-31")))); // string(10) "2017-03-03" 解决方案 var_dump(date("Y-m-d", strtotime("last day of -1 month", strtotime("2017-03-31")))); // string(10) "2017-02-28" var_dump(date("Y-m-d", strtotime("first day of +1 month", strtotime("2017-08-31")))); // string(10) "2017-09-01" // 但要注意短语的含义: var_dump(date("Y-m-d", strtotime("last day of -1 month", strtotime("2017-03-01")))); // string(10) "2017-02-28" 如果使用 Carbon\Carbon 可以用 subMonthNoOverflow 与 addMonthNoOverflow 防止进位: ...

January 27, 2021 · 2 min · 271 words · Me

回顾 2020

过年就在石家庄 lwl 走学 肺炎在家办公 PR 剪辑 表情包制作 自己染发剪发 – EOF –

December 31, 2020 · 1 min · 11 words · Me

Composer 文档笔记

命令行 composer dump 能跑,因为 symfony/console 允许 unambiguous prefix —— 任何 composer 子命令都可缩到能唯一匹配的最短前缀: composer dump # = dump-autoload composer i # = install composer show --platform # 列本机 platform package(php / ext-* / lib-*) --prefer-install=source|dist|auto 是 install / update 的同名 flag;--prefer-source / --prefer-dist 是它的快捷写法,三者底层同一开关。 发包瘦身 dist tarball 默认把 repo 里所有文件打进去(demo / test / CI 配置)。.gitattributes 加 export-ignore 把它们排除出 dist: /demo export-ignore phpunit.xml.dist export-ignore /.github/ export-ignore 本地 git archive 预览实际 dist 包内容: git archive HEAD --format zip -o preview.zip 几个反直觉默认 Config 默认 坑 process-timeout 300s 装大包(satis 全量索引、monorepo)容易 5 分钟撞 timeout platform-check php-only 只查 PHP 版本,不查扩展。缺扩展要等运行时 Call to undefined function 才暴露 discard-changes false source 装的包本地有改动时 update 会卡 prompt;CI 设 true 或 stash preferred-install auto 可以 per-package 配,自家 fork 走 source 方便 patch,其余走 dist { "config": { "process-timeout": 900, "preferred-install": { "myorg/*": "source", "*": "dist" } } } 生产环境 autoload 优化 composer install --no-dev 是基操,再加: ...

October 28, 2020 · 2 min · 279 words · Me, LLM

PHP float 精度

实例 1 $a = 1.1; var_dump(gettype($a)); // string(6) "double" var_dump($a); // float(1.1) 实例 2 $a = "123456789.1100110011"; $a = (float) $a; var_dump($a); // float(123456789.11001) var_dump(sprintf('%.11f', $a)); // string(21) "123456789.11001099646" $b = 123456789.11001; var_dump($b); // float(123456789.11001) var_dump(sprintf('%.11f', $b)); // string(21) "123456789.11000999808" $c = '123456789.1100110011'; $c = (float) $c; var_dump($c); // float(123456789.11001) $c = (string) $c; var_dump($c); // string(15) "123456789.11001" $c = (float) $c; var_dump($c); // float(123456789.11001) var_dump(sprintf('%.11f', $c)); // string(21) "123456789.11000999808" var_dump($a === $b); // bool(false) - 说明 $a 还是携带着 float 的精度 var_dump($b === $c); // bool(true) 实例 3 // # 1 var_dump(120085 === 1200.85 * 100); // bool(false) // # 2 var_dump(120085 == 1200.85 * 100); // bool(false) // # 3 var_dump(120081 == 1200.81 * 100); // bool(true) // # 4 var_dump(120085 - 1200.85 * 100); // float(1.4551915228367E-11) 实例 4 $a = 0.1; $b = 0.9; $c = 1; var_dump(($a + $b) == $c); // bool(true) var_dump(($c - $b) == $a); // bool(false) var_dump(sprintf('%.20f', $a + $b)); // string(22) "1.00000000000000000000" var_dump(sprintf('%.20f', $c - $b)); // string(22) "0.09999999999999997780" var_dump((0.5 - 0.25) === 0.25); // bool(true) 0.5 二进制 0.1,0.25 二进制 0.01 var_dump((0.25 + 0.25) === 0.5); // bool(true) 实例 5 $n = 19.99; var_dump($n * 100); // float(1999) var_dump((int) ($n * 100)); // int(1998) !!! var_dump((string) ($n * 100)); // string(4) "1999" var_dump((int) (string) ($n * 100)); // int(1999) var_dump(round($n * 100)); // float(1999) var_dump((int) round($n * 100)); // int(1999) 19.99 * 100 实际结果是 1998.9999999999998…,(int) 走的是向零截断而非四舍五入,所以掉到 1998。打印时 PHP 默认精度只显示到 1999,肉眼上看不出来这个陷阱。 ...

May 9, 2020 · 3 min · 449 words · Me

回顾 2019

新裤子 · 夏日终曲 2019 年的第一天从三亚开始,在 第一市场海鲜市场 买了海味,然后直接到旁边的 林姐香味海鲜 加工,迟到了超级好吃的香辣蟹。飞回北京就是滑雪团建,年后双双得到了最佳合作奖,感谢我们每周的火锅,让大家多了些感情。 – EOF – 年后买了烤箱烹饪技术再次升级,烤鸡翅、披萨、蛋挞甚至烤鱼,都已不在话下。受 shilei 推荐也喜欢玩德州扑克。看 B 站 UP 主自驾游,疯狂迷恋上了房车一段时间,还去看了一次房山的房车展(够远的),应该会结下不解之缘。 3 月 29 日公司 7 周年,第一次庆祝公司周年,吃蛋糕前大家合了张影。这张照片我挺感动的,说不好具体的原因,可能是因为大家的笑容吧。清明小假在奥森玩了个露营,是真的有点冷。涛爷来北京面试,转头就成了科大博士,给劲儿。 4 月中的一个双休日和爸妈南下,和比比定了亲 🎉🎉🎉。 5 月乐队的夏天,是今年开过最好的综艺,刺猬、新裤子很棒。 2019 年过去了,我很怀念它。

December 31, 2019 · 1 min · 32 words · Me

Lonicera Framework

项目代码:imzyf/lonicera | GitHub Lonicera Framework - Every French soldier carries a marshal’s baton in his knapsack. MVC MVC 模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。 Lonicera 0.1 bootstrap index.php 单一入口模式。 启动 PHP 内置 Web 服务器: php -S localhost:7070 路由器层 更偏向于使用 PATH_INFO 方式来访问。 从传统 URL 参数模式的访问地址进行解析,提取里面的 group、controller、action、param 4 个参数,随后交给 bootstrap 进行 dispatch 处理。 数据模型 用 PDO 来实现连接数据库。 ORM Object Relational Mapping 对象与数据库的映射叫作对象关系映射 PO Persistent Object 把一个数据库中的表的一行记录对应的对象称为持久对象 BO Business Object 业务对象 把业务逻辑封装为一个对象 VO Value Object 值对象 界面显示的数据对象 DTO Data Transfer Object 用在热呵呵需要数据传输的地方 DAO Data Access Object 指代 Active Record 模式中的数据对象 传统的 ORM 模式提倡数据对象和负责持久化的代码的分开,但是这并没有坚持数据操作的工作量。还有一种 ORM 模式叫作 Active Record。在 Active Record 中,模型层集成了 ORM 的功能,他们及代表实体,包含因为业务逻辑,又是数据对象,并负责把自己存储到数据库中。 ...

December 19, 2019 · 2 min · 275 words · Me

最左前缀原理与相关优化

MySQL 中的索引可以按一定顺序引用多个列,这种索引叫做联合索引。形式上,一个联合索引是一个有序元组 <a1, a2, …, an>,其中每个元素是表中的一列;单列索引可视为元素数为 1 的特例。 下文以 MySQL 8.0 + Employees Sample Database 为实验环境。MySQL 5.7 的历史情况会在相关章节穿插说明 —— 5.7 已结束官方支持,但很多旧项目仍在用,行为差异值得标出来。 以 employees.titles 为例,查看其索引。注意 8.0 比 5.7 多了 Visible 与 Expression 两列: SHOW INDEX FROM employees.titles; +--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression | +--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+ | titles | 0 | PRIMARY | 1 | emp_no | A | 301292 | NULL | NULL | | BTREE | | | YES | NULL | | titles | 0 | PRIMARY | 2 | title | A | 442605 | NULL | NULL | | BTREE | | | YES | NULL | | titles | 0 | PRIMARY | 3 | from_date | A | 442605 | NULL | NULL | | BTREE | | | YES | NULL | +--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+ Visible:8.0 引入的 Invisible Index。ALTER TABLE ... ALTER INDEX idx INVISIBLE 让索引对优化器不可见但物理结构仍在,适合「灰度删除索引」的场景 —— 先标为不可见观察一段时间,没出问题再真删。 Expression:8.0.13+ 的 Functional Key Parts(函数索引)会在这一列展示表达式,后文会用到。 全列匹配 EXPLAIN SELECT * FROM employees.titles WHERE emp_no = '10009' AND title = 'Senior Engineer' AND from_date = '1995-02-18'; +----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+ | 1 | SIMPLE | titles | NULL | const | PRIMARY | PRIMARY | 159 | const,const,const | 1 | 100.00 | NULL | +----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+ 对索引所有列进行精确匹配(这里精确匹配指 = 或 IN)时,索引被完整用到。 ...

May 25, 2019 · 6 min · 1155 words · Me, LLM