swoole相关

swoole与php-fpm的区别

最直观的区别是长连接和短连接,php-fpm每次访问要重新加载代码到内存中,并且按照请求-响应单向通信,swoole可以保持长连接,并且通过进程的方式将代码常驻在内存中,并且支持多线程、异步处理,可以在游戏业务中担任socket服务,能够轻松处理高并发请求。
小杜的个人图床

面试官问“你对 Swoole 框架的理解”,怎么答?

你可以按照这个逻辑去“套路”面试官:

定义: “Swoole 是一套 C 语言编写的 PHP 异步网络通信引擎。它让 PHP 不再局限于 Web 领域,而能胜任网络服务器、即时通信等高并发场景。

核心优势(背下来): “它最强大的地方在于协程 (Coroutine)。它能在同步编写代码的同时,实现异步非阻塞的性能,极大降低了开发高并发系统的难度。”

连接 TP 经验: “虽然我之前主要用 TP,但我了解到 TP6 也可以通过 think-swoole 扩展来运行。我理解在这种模式下,我们需要注意单例模式的变量污染问题和连接池的使用。”

几个 Swoole 术语

如果面试官想考考你是否真的懂,可能会抛出这几个词,你只要听懂了就不慌:

协程 (Coroutine): 比线程更轻量。可以理解为在 PHP 内部模拟的多任务切换。

连接池 (Connection Pool): 以前 TP 每次请求都连数据库。在 Swoole 里,数据库连接是提前连好放在池子里的,拿来即用,速度极快。

热更新: 因为 Swoole 常驻内存,修改代码后不会立即生效。通常需要重启 Worker 进程(面试时可以提一下这一点,显得你有实践经验)。

协程相关

协程的优点

首先是协程相对于进程更加轻量化,并且协程由Swoole进行调度,而进程由OS调度。

通过协程可以异步的处理高并发的任务,如果通过php-fpm进行大量的数据库操作,它是以类似队列的方式进行,每次处理一个操作,在一个操作没有完成之后队列就一直堵塞, 而协程是Swoole进行高效率调度的方式,swoole通过在一个进程中开启多个协程异步处理任务,当一个协程开启的时候,CPU会转而处理其他的任务,直到这个协程完成,再回头处理。

通过使用协程,一个进程就能轻松处理成千上万个高频请求,提高任务效率。

伪代码:

use Swoole\Coroutine;

// 同时开启两个协程
Coroutine\run(function () {
    Coroutine::go(function () {
        Coroutine::sleep(1); // 模拟 IO
        echo "A 完成\n";
    });
    Coroutine::go(function () {
        Coroutine::sleep(1); // 模拟 IO
        echo "B 完成\n";
    });
});
// 结果:总共只花了 1 秒多一点

注意事项

请注意,协程容易产生数据污染的问题,协程在同一个进程空间中进行,因此需要避免使用容易混淆的全局变量,但是可以通过Swoole\Coroutine::getContext() 获取当前协程的上下文空间来使用上下文的局部变量来避免污染。

另一方面,禁止直接使用阻塞函数 如果在协程中使用原生的阻塞函数sleep会失去携程的意义,因为原生的阻塞函数是同步的,如果需要使用阻塞函数,应当通过Swoole\Runtime::enableCoroutine(); 开启协程化,Swoole会将阻塞函数变成异步的。

对数据库的操作应当使用连接池而非重新创建连接,由于Swoole的协程是用于高频场景的,因此对数据库的查询应当使用连接池即提前建立大量的数据库连接,协程需要用到时拿来用,用完归还,如果重新创建连接会导致大量不必的开销。

一句话:

协程是对并发处理能力的革命性提升
在游戏场景中,比如玩家移动或释放技能,会产生极其频繁的数据库或 Redis 读写。如果用传统的 FPM 模式,进程会大量阻塞在 IO 等待上。
而使用 Swoole 协程,我们可以通过 一键协程化 让原生的数据库操作变为非阻塞。配合 连接池 技术,我们可以在维持数万个 WebSocket 长连接的同时,依然保持极低的响应延迟。这在处理用户基数巨大的游戏平台时,是系统稳定性的核心保障。

laravel相关

1. 服务容器

这里Laravel与tp最大的不同,在tp的习惯上通过use加载引入类或者通过助手函数调用相关的模块,但是laravel几乎所有功能都是注册在容器里面的,通过依赖注入只需要在类的构造函数中写明需要使用的类名,laravel就会自动实例化把对象给你。把各种功能绑定到容器中是通过service provider

2. eloquent orm 的潜规则

相比tp比较宽松的表个模型设计规则,laravel对数据表的规定较为严格:模型名可以是单位,但是表名必须是复数,并且必须有create_at和update_at的时间戳,否则会报错。

另一个是N+1问题,例如查询10个用户的头像,用户表和头像表通过外键关联,这时候如果先一次查10个用户,再循环查头像,需要执行11次查询,但是通过with方法指定关联就可以只产生两次查询,这就是预加载。

3. 四架马车:中间件、模型迁移、命令行、模板

主要就是中间件,模型迁移、命令行、blade模版。

模板

小杜的个人图床

blade模板最核心的一点是他会被编译成原生的php缓存,这样能够得到最大的性能。

同时还支持布局继承,即通过@yield在父模板中定义插槽,在子模版通过@extend继承父模板,并且通过@section 指定在父模板中留空的部分。

同时还可以通过php artisan make:component xxx 定义组件

定义的组件存放在 app\View\Components\xxx 对应视图 resources\view\components\xxx

在组件的构造函数指定组件的参数,并且在引用组件的时候通过

<x-组件名 :参数="值" />

模板还支持通过@auth@endauth来鉴权和通过@guest与@endguest判断是否为游客。

@foreach@endforeach进行循环,可以通过$loop->first$loop->last判断头尾。

中间件

关于中间件,就是在每次请求在执行实际的业务代码进行响应之前,先执行中间件来进行一些必要的检查,例如鉴权、日志、处理跨域等。也可以在业务代码执行之后在响应之前再通过中间件进行一些操作。

中间件分为全局中间件和路由中间件,全局中间件在app\http\Kernel中登记之后全局生效,路由中间件在路由定义的时候通过->middleware指定使用的中间件

可以通过 php artisan make:middleware来快速创建一个中间件,在创建的中间件中在handle方法中编写业务逻辑,一般是先写前置操作,然后需要通过 $response = next(next(next(request); 来结束前置运行,将请求传递给控制器,在$resposne 后编写的代码会在控制器响应之后作为后置操作来执行。

一般可以用中间件完成登录鉴权、请求频率限制、数据格式化、日志追踪、参数加解密等。

命令行

关于命令行,类似django和thinkphp的命令行,laravel的命令行被称为artisan 对应tp的 php think命令,可以用来自动生成代码、管理数据库、运行计划任务、调试系统等。

主要语法是 php artisan [command] [option] [argument]

基础创建:
  • php artisan make:controller 创建控制器

  • php artisan make:model User 创建模型

  • php artisan make:middleware xxx  创建中间件

  • php artisan make:migration create_order_table  创建迁移

迁移操作

laravel的数据库管理非常依赖迁移,通过迁移工具即可在命令行中同步代码与数据库描述的结构。

  • php artisan migrate   将代码中描述的结构同步到数据库
  • php artisan migrate:rollback  撤销最后一次操作

  • php artisan migrate:refresh  清空并创建表

  • php artisan db:seed   生成测试用的假数据

应用管理与调试
  • php artisan route:list 列出所有路由 可以用来检查路由是否挂载成功
  • php artisan config:cache / php artisan cache:clear   清空缓存,例如修改了.env文件要用

  • php artisan tinker 进入一个php的交互shell来调试php代码。

自定义命令

通过php artisan make:command xxx 可以用于创建自定义命令行程序,在生成的代app/Console/Commands/xxx.php 来编辑命令行程序,需要修改$signature 来定义命令名, 例如名称为game:check 然后再handle方法中编写具体实现,最后通过php artisan game:check来执行命令行程序。

计划任务

原先我们通过corntab来管理计划任务,laravel将这个管理收回到了框架代码中,只需要执行一次php artisan schdule:run 即可通过laravel自动管理计划任务,在 app\console\kernel.php中编写 $schedule->command(命令)->周期即可挂载计划任务。

如何管理数据库变更或者定时任务?

在我的开发流程中,我严格遵守 Laravel 的 Migration 规范。所有数据库表结构的变更都通过 php artisan make:migration 生成,这样可以确保团队成员之间的数据结构高度同步,也方便在部署时一键更新。

对于定时任务,我不直接修改系统的 Crontab,而是利用 Laravel 的 Task Scheduler。在 Console/Kernel 中统一定义任务计划。这样做的核心好处是任务逻辑随代码版本走,不需要去服务器上手动配置,运维起来非常优雅。”

迁移

1. 迁移的本质:数据库的“Git”

你可以把每一个迁移文件看作是 Git 的一个 Commit

  • Git 管理的是代码的增删改。

  • Migration 管理的是数据库表结构的增删改。

为什么需要它? 在团队开发中,如果你的同事建了一个表,他不需要发给你一个 .sql 文件,你只需要执行 git pull 拿到迁移文件,然后运行 php artisan migrate,你的数据库就和他的一模一样了。


2. 迁移文件的结构解析

一个典型的迁移文件(由 php artisan make:migration 生成)包含两个核心方法:

up():执行变更

当你运行 php artisan migrate 时,系统会执行这个方法。

PHP

public function up() {
    Schema::create('orders', function (Blueprint $table) {
        $table->id(); // 主键
        $table->foreignId('user_id'); // 外键
        $table->decimal('amount', 8, 2); // 金额
        $table->timestamps(); // 自动生成 created_at 和 updated_at
    });
}
down():回滚变更

当你运行 php artisan migrate:rollback 时,系统会执行这个方法,撤销 up 做的事情。

PHP

public function down() {
    Schema::dropIfExists('orders'); // 删掉这个表
}

3. 核心命令全家桶 (面试必记)

在面试中,面试官可能会问你如何处理不同场景下的数据库变更,你需要熟练说出这些命令:

  • php artisan migrate 运行所有尚未执行的迁移。

  • php artisan migrate:rollback 回滚最后一次迁移操作(回滚上一个 Batch)。

  • php artisan migrate:rollback --step=5 回滚最后的 5 个迁移文件。

  • php artisan migrate:reset 回滚数据库中所有的迁移,相当于把表全删了。

  • php artisan migrate:refresh 先重置(reset)再重新执行(migrate)。常用语开发阶段重新洗牌。

  • php artisan migrate:fresh (最暴力) 直接把数据库里的表全删了(不管是不是迁移生成的),然后从头开始跑。


4. 迁移状态表:migrations

你可能会好奇,Laravel 怎么知道哪些文件跑过了? 你在宝塔里打开数据库,会发现多了一张 migrations 表。它只有两个关键字段:

  1. migration: 文件名。

  2. batch: 批次号。

原理: 每次你运行 migrate 命令,Laravel 都会把跑过的文件名存进去。下一次运行时,它会对比文件夹里的文件名和表里的记录,只跑表里没有的文件


5. 生产环境的坑

“生产环境下,执行迁移有什么需要注意的吗?”

这是区分初级和中级开发者的关键点:

  1. 数据丢失风险: 永远不要在生产环境运行 migrate:freshmigrate:refresh

  2. 超时与锁表: 如果表里有几百万条游戏日志,执行 addColumn 可能会导致长时间锁表。

    • 应对: 建议提到你会用 Online DDL 或者是选择在凌晨业务低峰期操作。
  3. 原子性: 尽量保证一个迁移文件只做一件事(比如只创建一个表,而不是同时修改三个表)。如果中间出错了,方便回滚。

  4. 先备份后迁移: 在执行重大变更前,先用 mysqldump 备份数据库是职场生存的基本准则。

Composer相关

composer是什么

对于 PHP 开发者来说,Composer 不是一个可选工具,它是生存工具。Laravel 的每一个核心组件、每一个插件都是通过 Composer 管理的。

Composer类似npm和pip等包管理工具,在PHP和Laravel中,composer可以用来管理项目依赖。

composer.lock与composer.json

这是composer的两个核心配置文件,.json描述当前项目使用的依赖与大致版本以及项目相关的信息等,用于给开发者提供参考,优先级上来说,.json文件的优先级不如.lock文件。

composer.lock (版本锁) 记录了当前项目实际安装的每一个包的精确版本号,它是给机器看的。 关键点: 团队开发中,.lock 文件必须提交到 Git。这样能保证你和同事、以及线上宝塔服务器安装的包版本完全一致,避免“我本地没问题,线上崩了”的情况。

常用命令

  • install 与 update
    install优先读取.lock锁文件来安装对应的依赖,如果有.lock文件他会忽略.json文件,而update会读取json文件进行依赖安装并且更新.lock文件,在生产环境不要乱用这个。

  • composer require
    composer require命令用于安装新的包,并且修改composer.json与composer.lock文件,比手动改文件要安全

  • composer dump-autoload
    用于在手动修改了composer.json文件的情况下清理缓存。

自动加载原理

composer.json中有一个autoload的配置项,例如:

"autoload": {

	"psr-4": {

		"App\\": "app/",
	
		"Database\\Factories\\": "database/factories/",

		"Database\\Seeders\\": "database/seeders/"
	
	}

},

他遵循psr-4规范,通过维护一个类名=>路径的字典来决定如何自动加载,当使用use 类名来引用类的时候,composer的autoLoader会通过这个来寻找文件

性能优化与镜像源

如果是线上部署项目,可以通过

compsoer install --optimize-autoloader --no-dev

表示启用自动加载优化,会将自动加载的动态表转为静态表增加查找速度,并且通过--no-dev参数指定排除开发测试使用包例如单元测试等。

在国内环境加速和避免网络异常的问题,应该使用

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

更换为阿里镜像源。

数据库优化相关

B树与B+树

B树是一种多路平衡查找树,可以理解为一种升级版的二叉树。
B树的每一个节点又存数据又存索引,这会导致能够存储的索引数量有限,而B+树的非叶子节点只存索引不存数据,这样树会非常的扁平,能够存储更多的索引来快速查找,并且B+树的叶子节点中又有双向循环链表链接,因此又能更好的提升查找性能。
相对于B 树,B+树存放的索引更多,在查询的时候进行的磁盘IO更少因此速度更快,并且查询性能更加稳定,范围查询极强(只需要找到第一个,然后顺着链表一直找。)

为什么我们的主键通常建议用自增 ID,而不是 UUID(随机字符串)?

这和 B+ 树的结构有关。

B+ 树的叶子节点是按顺序排列的。如果使用自增 ID,每次新插入数据都是在末尾追加,树的结构非常稳定,索引维护开销小。

如果使用 UUID 这种随机字符串,新数据可能会插入到树的中间位置,导致频繁的**页分裂(Page Split)**和数据移动。这会产生大量的磁盘 IO 和碎片,严重拉低数据库的写入性能。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐