IP层只管把数据包尽力送到目标机器,不保证送达、不乱序、不丢包;可靠的所有保障(确认、重传、排序、去重)全部是TCP在操作系统内核里实现的。PHP写MySQL查询,就是典型跑在TCP+IP之上。
沿用前面写信比喻:IP(邮局)只管尽力送信封,丢了、顺序乱邮局不管;TCP(信纸里的页码+回执约定)负责兜底保障。
场景:PHP执行一条MySQL查询
<?php$pdo=newPDO('mysql:host=192.168.1.100;dbname=test','user','pass');$stmt=$pdo->query("SELECT name FROM user WHERE id=1");var_dump($stmt->fetch());PHP本身不直接操作IP、TCP报文。
PHP调用PDO → PDO调用操作系统socket接口 →操作系统内核的TCP模块 + IP模块完成网络传输。
整个请求拆解,区分IP做什么、TCP做什么
- PHP把SQL字符串交给操作系统Socket,请求发给MySQL服务器
192.168.1.100:3306 - TCP层(内核)
- 先和MySQL的3306端口完成三次握手,建立连接
- 把SQL数据切成分段,给每一段加上TCP头部:序列号、源端口、目标端口、校验和
- 交给下层IP
- IP层(内核)
IP给TCP数据加上IP头部:源IP、目标IP,组装成IP数据包。
IP的工作仅此而已:查找路由,把包扔到网络上,尽力转发。IP完全不知道里面装的是SQL查询,分不清这是第几段数据,发送出去之后,IP就不再管这个包了。
为什么IP不可靠?(4种故障,IP全部视而不见)
- 数据包丢失:网络拥堵,路由器直接丢弃这个IP包。邮局把信封弄丢了,邮局不会通知寄件人。IP没有重试机制。
- 数据包乱序:IP包可以走不同路由。比如SQL被拆成包A、包B,B先到达MySQL服务器,A晚到。IP不排序。
- 数据包重复:网络链路异常,同一个IP包被重复投递两次。IP不会识别重复。
- 数据损坏:传输中比特出错。IP只做简单校验,校验失败直接丢弃,不会通知发送方重发。
IP的设计哲学:尽力交付(best-effort),它不保存任何发送记录,没有回执,不知道对方收没收到包。IP只关心「主机到主机」的投递。
TCP如何在IP之上实现可靠(同样4种故障,TCP全部处理)
IP只是底层运输,TCP在内核盯着每一段数据:
- 序列号:每个TCP分段带序号。包B先到MySQL,TCP会先缓存,等包A抵达,再按顺序拼接完整SQL,再交给MySQL程序。
- ACK确认回执:MySQL收到TCP分段,立刻回一个ACK包告诉客户端TCP:“我收到到第N号字节了”。
- 超时重传:客户端TCP发送分段后,等待ACK。超时没收到回执 → 自动重新发送这段SQL数据。PHP代码完全感知不到丢包和重传,内核悄悄处理。
- 去重:如果重复的包到达,TCP依靠序列号识别,丢弃重复副本,不会把同一条SQL重复发给MySQL。
- 校验和:TCP检测数据损坏,损坏的包直接丢弃,等待重传。
当MySQL执行完SQL,返回结果集,同样走这套反向流程:MySQL端TCP分段,IP负责投递,客户端TCP重组完整结果,再交给PDO,PHP拿到数组。
通信结束,TCP四次挥手断开连接。
关键边界(非常容易踩坑,后端面试高频)
✅ TCP的可靠,只保证:数据完整、有序,交付到目标机器操作系统的TCP缓冲区
❌TCP不保证MySQL服务一定读取这条数据
举个极端例子:数据包成功送到MySQL服务器内核TCP缓冲区(TCP回执已经发回给PHP客户端),但MySQL进程瞬间崩溃。
此时:TCP层面已经确认送达成功,但是MySQL程序还没来得及读取、执行SQL。
TCP可靠 = 内核之间字节流可靠交付;不等于应用业务层面一定执行成功。业务层面的事务、重试,需要PHP+MySQL自己来做。
一句话浓缩这个PHP查询例子
IP负责把承载SQL的数据包送到MySQL服务器这台机器,路上丢包、乱序IP一概不管;
TCP在操作系统内核,利用序列号、ACK、超时重传,保证这条SQL字节流完整有序到达MySQL的TCP缓冲区,PHP写业务代码完全看不到底层丢包重传的细节。