咸亨酒店的账房玄机—从孔乙己赊账窥MySQL查询之道

咸亨酒店的账房玄机---从孔乙己赊账窥MySQL查询之道

📖 开篇:咸亨酒店开业

在鲁迅笔下的咸亨酒店里,掌柜对孔乙己"赊账多少"的查询应答,恰似MySQL在内存与磁盘间的数据流转。让我们透过长衫客赊酒的场景,揭开数据库存储架构的神秘面纱。这日刚开业,孔乙己踱着方步来到柜台前:"温两碗酒,要一碟茴香豆。"说着排出九文大钱,忽然压低声音:"敢问掌柜的,我总共赊的账,可还...记得清?"

掌柜的闻言一惊,看着眼前这个穿着长衫站着喝酒的人,突然意识到:要快速查清陈年旧账,看来要费一番周转了。这场景像极了MySQL在内存与磁盘间的数据查询之旅...

让我们查看掌柜的账本信息

sql 复制代码
CREATE TABLE bill_record (
    bill_id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
    custormer VARCHAR(255) comment '顾客',
    city VARCHAR(100) comment '顾客来源地',
    bill_date DATE comment '日期',
    bill_money DECIMAL(10,2) comment '金额',
    bill_remark text comment '备注信息'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 comment '账单';
​
insert into bill_record values(null,'阿Q','绍兴','2023-01-05',3,'茴香豆一碟');
insert into bill_record values(null,'孔乙己','绍兴','2023-12-19',3,'茴香豆一碟');
insert into bill_record values(null,'孔乙己','绍兴','2024-02-02',3,'茴香豆一碟');
insert into bill_record values(null,'祥林嫂','南通','2023-07-12',10,'一打酒');
​
select * from bill_record;
+---------+-----------+--------+------------+------------+-----------------+
| bill_id | custormer | city   | bill_date  | bill_money | bill_remark     |
+---------+-----------+--------+------------+------------+-----------------+
|       1 | 阿Q       | 绍兴   | 2023-01-05 |       3.00 | 茴香豆一碟      |
|       2 | 孔乙己     | 绍兴   | 2023-12-19 |       3.00 | 茴香豆一碟      |
|       3 | 孔乙己     | 绍兴   | 2024-02-02 |       3.00 | 茴香豆一碟      |
|       4 | 祥林嫂     | 南城   | 2023-07-12 |      10.00 | 一打酒          |
+---------+-----------+--------+------------+------------+-----------------+

🏮 第一幕:全表扫描的慢镜头(掌柜翻账本)

场景还原:

掌柜的抓耳捞腮,不断调用大脑记忆(Buffer Pool内存 ),他也无法记清具体赊账金额,只得让孔乙己稍等片刻随即转身后向柜台,很熟练的拿起那本泛黄的牛皮账本(磁盘数据文件),这本账册用蝇头小楷记录着:账册

sql 复制代码
mysql> select * from bill_record where custormer='孔乙己';
+---------+-----------+--------+------------+------------+-----------------+
| bill_id | custormer | city   | bill_date  | bill_money | bill_remark     |
+---------+-----------+--------+------------+------------+-----------------+
|       2 | 孔乙己    | 绍兴   | 2023-12-19 |         3.00 | 茴香豆一碟        |
|       3 | 孔乙己    | 绍兴   | 2024-02-02 |         3.00 | 茴香豆一碟        |
+---------+-----------+--------+------------+------------+-----------------+
​
mysql> explain select * from bill_record where custormer='孔乙己'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: bill_record
   partitions: NULL
         type: ref
possible_keys: idx_custormer_bill_date
          key: idx_custormer_bill_date
      key_len: 768
          ref: const
         rows: 2
     filtered: 100.00
        Extra: NULLtype: ALL -- 代表全表扫描
        rows: 4 -- 代表扫描所有行
​
-- 查看慢日志
mysql> set global slow_query_log=on;
​
-- 查看服务器慢日志
# Time: 2025-03-06T03:36:52.169226Z
# User@Host: root[root] @ localhost []  Id:     2
# Query_time: 0.000232  Lock_time: 0.000099 Rows_sent: 2  Rows_examined: 4
SET timestamp=1741232212;
select * from bill_record where custormer='孔乙己';
Rows_sent: 2 -- 代表返回的行数
Rows_examined: 4 -- 代表检查的行数

查账三部曲:

  1. 掌柜思考 :掌柜遍历内存记忆看是否有孔乙己赊账信息(Buffer Pool),发现找不到相关记忆信息
  2. 逐页翻找 :掌柜拿账本到柜台,手指沾着唾沫从第一页翻到末页(磁盘I/O,全表扫描
  3. 柜台对账 :一旦找到孔乙己赊账记录,掌柜并默记赊账金额(Buffer Pool),然后将数字打到算盘上,如此反复,直到翻到末页,算珠被打的劈里啪啦的响(CPU计算**)
  4. 告知结果 :掌柜的将最后算盘计算的数字告知孔乙己(返回结果

账本太重太厚,账页从正头翻到尾,掌柜的鼻尖沁出汗珠子,忽地两眼放光:"终于找全了!共计六文!"(全表扫描耗费资源),话音未落,却见孔乙己倚着榆木柜台笑得前仰后合,长衫下摆扫落了三四粒茴香豆。掌柜若这穷酸书生日日来翻旧账,怕是要把柜台磨穿,看来务必想个办法了。

存储架构对照表

咸亨元素 MySQL组件 协作规则
掌柜的记忆 Buffer Pool 缓存最近访问的账页,默认占内存80%
牛皮账本 磁盘 数据最终存储地,按.ibd文件分册
算盘 CPU资源 CPU计算

🏮 第二幕:二级索引显神通(朱红目录册的秘密)

掌柜的妙招:

昨日查账可让掌柜费了老大劲,掌柜在柜台边挂起一本朱红目录(二级索引),该朱红目录册小巧而精致。

sql 复制代码
ALTER TABLE bill_record ADD INDEX idx_custormer_bill_date(custormer,bill_date);

索引结构揭秘:

这本目录示意:

css 复制代码
阿Q
    └─2023-01-05 → 主账册第23页
孔乙己
    ├─2023-12-19 → 主账册第21页
    └─2024-02-02 → 主账册第89页
祥林嫂
    └─2023-07-12 → 主账册第156页

查询过程:

sql 复制代码
mysql> explain select * from bill_record where custormer='孔乙己'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: bill_record
   partitions: NULL
         type: ref
possible_keys: idx_custormer_bill_date
          key: idx_custormer_bill_date
      key_len: 768
          ref: const
         rows: 2
     filtered: 100.00
        Extra: NULL
type: ref -- 代表等值查询
key: idx_custormer_bill_date -- 代表使用的索引名称
key_len: 768 -- custormer(255[字段长度]*3[UTF8占用3字节] + 1[字段是否为空]+2[字段是否变长])  代表使用的索引部分长度即前缀索引
​
    
# Time: 2025-03-06T03:41:24.764760Z
# User@Host: root[root] @ localhost []  Id:     2
# Query_time: 0.000218  Lock_time: 0.000094 Rows_sent: 2  Rows_examined: 2
SET timestamp=1741232484;
select * from bill_record where custormer='孔乙己';
Rows_sent: 2 -- 代表返回的行数
Rows_examined: 2 -- 代表检查的行数,证明通过索引锁定了所在的数据行

高效查账四步舞:

翌日,孔乙己又懒洋洋的跑过来,对掌柜说到"敢问掌柜的,我赊的账,可还...记得清?"

  1. 掌柜思考 :掌柜遍历自己大脑看是否有孔乙己赊账信息(Buffer Pool
  2. 目录寻踪 :掌柜的实在记不起来,转身翻朱红目录(索引扫描
  3. 精准定位 :"孔乙己-腊月"直指189页(索引命中
  4. 取账验证 :只搬对应账页到柜台(回表查询1)
  5. 循环遍历 :掌柜的回到朱红目录册找到189页指向的下一条记录第245页(数据页指针
  6. 取账验证 :只搬对应账页到柜台,如此反复,直到翻到末页,算盘也打的劈里啪啦的响(CPU计算
  7. 当场确认 :"您赊了六文钱"(返回结果

存储架构对照表

咸亨元素 MySQL组件 协作规则
掌柜的记忆 Buffer Pool 缓存最近访问的账页,默认占内存80%
牛皮账本 磁盘 数据最终存储地,按.ibd文件分册
朱红目录册 二级索引 加速查询
算盘 CPU资源 CPU计算

掌柜这次掏出个巴掌大的小本子,不多时,并直接对孔乙己说到"共欠了六文!",孔乙己一脸愕然,掌柜敲了敲朱红目录册说到:"玄机在这里呢!"

🏮 第三幕:覆盖索引避回表(朱红目录册的玄机)

腊月的穿堂风卷着酒客的吆喝,查询账单的人越来越多,带着老花镜的掌柜一遍遍翻看这账册,这也累的够呛。掌故心想,这伙人只管自己赊账多少,我为何不将这个赊账信息直接放入到朱红目录册中,如此并无需翻账本了。

sql 复制代码
alter table bill_record add index idx_custormer_bill_money(custormer,bill_money);
​
mysql> explain select sum(bill_money) from bill_record where custormer='孔乙己'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: bill_record
   partitions: NULL
         type: ref
possible_keys: idx_custormer_bill_date,idx_custormer_bill_money
          key: idx_custormer_bill_money
      key_len: 768
          ref: const
         rows: 2
     filtered: 100.00
        Extra: Using index
Extra: Using index -- 代表直接走索引,无需回表
​
# Time: 2025-03-06T06:15:33.006049Z
# User@Host: root[root] @ localhost []  Id:     2
# Query_time: 0.000240  Lock_time: 0.000097 Rows_sent: 1  Rows_examined: 0
SET timestamp=1741241733;
explain select sum(bill_money) from bill_record where custormer='孔乙己';

高效查账四步舞:

翌日,孔乙己又懒洋洋的跑过来,对掌柜说到"敢问掌柜的,我赊的账,可还...记得清?"

  1. 掌柜思考 :掌柜遍历自己大脑看是否有孔乙己赊账信息(Buffer Pool
  2. 目录寻踪 :掌柜先翻朱红目录(索引扫描
  3. 精准定位 :"孔乙己-腊月"直指189页(索引命中 ),获取本次赊账3文,手指灵活的拨动算盘的算珠(CPU计算)。
  4. 循环遍历:掌柜的回到朱红目录册找到189页指向的下一条记录第245页,获取本次赊账3文,手指灵活的拨动算盘的算珠。
  5. 当场确认 :"您共赊了六文钱"(返回结果

存储架构对照表

咸亨元素 MySQL组件 协作规则
掌柜的记忆 Buffer Pool 缓存最近访问的账页,默认占内存80%
朱红目录册 二级索引 加速查询
算盘 CPU资源 CPU计算

掌柜这次掏出个巴掌大的小本子,还没等算盘珠子拨响,张嘴就报数:"共欠了六文!"孔乙己吓得酒碗差点摔了:"您这新账本会算命吗?"掌柜敲了敲朱红目录册说到:"玄机还在这里呢!"

🏮 第四幕:掌柜的烦恼

掌柜看此朱红目录册如此甚好,根据不同的用途,一口气制作了十几分目录册,正当掌柜对此洋洋得意之时,弊端也暴露出来了。

  1. 索引不是万能的:就像给每粒茴香豆都做目录(过度索引)反而占地方
  2. 内存有限:柜台最多摆十本账(缓冲池大小限制)
  3. 索引实时更新:修改账本数据的时候也需要修改对应的朱红目录册数据(加重改数据代价)

💼 终章:孔乙己的顿悟

暮色中的咸亨酒店,孔乙己攥着刚结清的账本,忽然拍案道:"原来这查账的学问,不在茴字的四种写法,而在索引的精妙!"。掌柜笑着递过温好的黄酒:"客官可曾听过B+树?这索引目录的编排方式..." 话音未落,店内外充满了快活的空气。

相关推荐
炬火初现32 分钟前
Etcd的安装与使用
数据库·etcd
IT猿手40 分钟前
2025最新群智能优化算法:云漂移优化(Cloud Drift Optimization,CDO)算法求解23个经典函数测试集,MATLAB
开发语言·数据库·算法·数学建模·matlab·机器人
雷渊1 小时前
深入分析理解mysql的MVCC
java·数据库·面试
Paparazi灬1 小时前
RocksDB写流程各种场景下的处理逻辑和线程交互时序
数据库
神经星星1 小时前
【vLLM 教程】使用 TPU 安装
数据库·人工智能·机器学习
hjehheje2 小时前
clickhouse查询效率低
数据库·人工智能
七七powerful2 小时前
ClickHouse 中出现 DB::Exception: Too many parts 错误
java·前端·数据库
Linux运维老纪3 小时前
Python实战项目(‌Hands-on Python Project)
开发语言·数据库·python·sql·mysql·云计算·运维开发
小林熬夜学编程3 小时前
【MySQL】第十五弹---全面解析事务:定义、起源、版本支持与提交方式
android·linux·服务器·开发语言·数据库·mysql
明矾java10 小时前
MySQL进阶-关联查询优化
数据库·mysql