You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Lighthouse GraphQL查询未命中Redis缓存问题排查

GraphQL缓存未命中问题:已生成缓存但查询耗时仍未优化

问题背景

我有一个GraphQL查询,获取1700条数据耗时约10秒,计划通过设置10分钟TTL的缓存优化性能。已按文档在Query上添加@cache指令,Redis中能看到生成的缓存键且包含结果,但每次请求响应时间仍维持在10秒左右,疑似缓存未命中。

GraphQL查询代码

query GetMapAssets($first: Int, $page: Int) {
    assets(first: $first, page: $page, where: {
        AND: [
            {
                column: "mag_id"
                operator: IS_NOT_NULL
            }
            {
                column: "last_mag_data_id"
                operator: IS_NOT_NULL
            }
        ]
    }) {
        paginatorInfo {
            count
            currentPage
            lastPage
            total
        }
        data {
            id
            reference
            lastPosition {
                latitude
                longitude
            }
            asset_type {
                id
                name
            }
            state {
                id
            }
            branches {
                data {
                    id
                }
            }
            sites {
                data {
                    site_type {
                        id
                        name
                    }
                }
            }
            mag {
                mag_id
            }
            asset_group {
                id
            }
            date_enter_site
        }
    }
}

Schema定义

extend type Query @guard(with: ["passport", "web"]) {
    assets(
        orderBy: _ @orderBy(columns: [
            "id",
            "reference",
            "buy_value",
            "asset_type_name",
            "asset_mag_id",
            "asset_group_reference",
            "asset_state_name"
        ])
        where: _ @whereConditions
    ): [Asset]
        @paginate(defaultCount: 10, scopes: [
            "filterBranch",
            "assetTypeName",
            "assetMagId",
            "assetGroupReference",
            "assetStateName"
        ])
        @can(ability: "view", model: "App\\Models\\Asset")
        @cache (private: true, maxAge: 600)
    asset(reference: String @where(key: "reference"), id: ID @where(key: "id")): Asset
        @first
        @can(ability: "view", model: "App\\Models\\Asset")
}

type Asset {
    id: ID!
    mag: Mag @belongsTo
    company: Company @belongsTo
    branches(
        orderBy: _ @orderBy(columns: ["id", "name"])
        where: _ @whereConditions
    ): [Branch] @belongsToMany(type: PAGINATOR)
    asset_type: AssetType @belongsTo
    state: AssetState @belongsTo
    reference: String!
    buy_value: Float
    last_mag_data_id: Int
    asset_group: AssetGroup @belongsTo
    responsible(
        orderBy: _ @orderBy(columns: ["id", "name"])
        where: _ @whereConditions
    ): [User] @belongsToMany(relation: "users", type: PAGINATOR)
    shipping_orders: [ShippingOrder] @hasMany(scopes:["tracked"])
    responsibles(
        orderBy: _ @orderBy(columns: ["id", "name"])
        where: _ @whereConditions
    ): [User] @hasMany(relation: "users", type: PAGINATOR)
    mag_datas(
        orderBy: _ @orderBy(columns: ["id", "device_timestamp"])
        where: _ @whereConditions
    ): [MagData] @hasMany(type: PAGINATOR)
    latestMagData: MagData @hasOne
    custom_parameters(
        orderBy: _ @orderBy(columns: ["id", "name"])
        where: _ @whereConditions
    ): [CustomParameter] @morphMany(relation: "custom_parameters", type: PAGINATOR)
    lastTemperature: MagData
    lastPosition: MagData @hasOne
    current_shipping_order: [ShippingOrder] @hasMany(relation: "shipping_orders", scopes: ["tracked"])
    sites(
        orderBy: _ @orderBy(columns: ["id", "name"])
        where: _ @whereConditions
    ): [Site] @belongsToMany(type: PAGINATOR) @can(ability: "view", model: "App\\Models\\Site")
    previous_sites(
        orderBy: _ @orderBy(columns: ["id", "name"])
        where: _ @whereConditions
    ): [Site] @belongsToMany(type: PAGINATOR) @can(ability: "view", model: "App\\Models\\Site")
    date_enter_site: DateTime
}

Lighthouse缓存配置

'cache' => [
    'enable' => env('LIGHTHOUSE_CACHE_ENABLE', 'local' !== env('APP_ENV')),
    'key' => env('LIGHTHOUSE_CACHE_KEY', 'lighthouse-schema'),
    'store' => env('LIGHTHOUSE_CACHE_STORE', null),
    'ttl' => env('LIGHTHOUSE_CACHE_TTL', null),
],

Redis中生成的缓存键

1) "company_cache:lighthouse:query:5463d14c79c39a60ba59927e444e37095660a3cc28cf14bf4cc97c2ecb263c34"
2) "company_cache:lighthouse-schema"
3) "company_cache:lighthouse:auth:1:Query::assets:first:2000:page:0:where:{"operator":"=","AND":[{"column":"mag_id","operator":"NotNull"},{"column":"last_mag_data_id","operator":"NotNull"}]}"

排查与解决步骤

1. 确认缓存键参数匹配

  • 核对请求参数与缓存键的参数:比如查询实际传的first是1700,但Redis缓存键里是first:2000,参数不匹配会导致每次生成新缓存,无法命中。确保请求的first、page参数和缓存键中的完全一致。
  • 检查where条件序列化差异:查询中用的是IS_NOT_NULL,缓存键里是NotNull,虽然语义相同,但Lighthouse的缓存键是基于参数序列化结果生成的,要确认请求的where条件序列化后和缓存键中的字符串完全一致。

2. 检查@cache指令配置

  • 确认@cache指令作用范围:@paginate会包装分页结果,@cache放在@paginate之后是正确的,但要确保使用的Lighthouse版本支持对分页字段缓存,避免版本兼容性问题。
  • 验证private: true的影响:private表示缓存是用户专属的,缓存键中的auth:1对应用户ID,测试时要确保使用同一个用户发起请求,否则会无法命中缓存。

3. 验证Lighthouse缓存配置生效

  • 确认LIGHTHOUSE_CACHE_STORE:配置为null时会使用Laravel默认缓存驱动,检查.env中CACHE_DRIVER=redis是否正确设置。
  • 开启缓存开关:如果在本地测试,需要手动设置LIGHTHOUSE_CACHE_ENABLE=true,因为默认配置是local环境不开启。

4. 监听缓存命中事件

在Laravel的AppServiceProvider的boot方法中添加日志监听,确认请求是否真的命中缓存:

Cache::listen(function ($event) {
    Log::info("Cache event: {$event->event}", ['key' => $event->key]);
});

执行查询后查看日志,如果是miss说明缓存键不匹配;如果是hit但仍慢,排查Redis反序列化或其他中间件耗时问题。

5. 其他排查点

  • 检查权限逻辑:@can指令是否在缓存之前执行?如果缓存命中,应该不会走到数据库查询和权限验证的耗时逻辑,若仍耗时,可能是缓存未真正生效。
  • 测试Redis性能:用redis-cli ping测试Redis响应速度,排除Redis本身性能问题。

内容的提问来源于stack exchange,提问作者Radyum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 14:17:33