当前位置:首页>学习笔记>brpc 学习笔记(九):bthread_rwlock_t——协程友好的读写锁

brpc 学习笔记(九):bthread_rwlock_t——协程友好的读写锁

  • 2026-05-07 14:23:26
brpc 学习笔记(九):bthread_rwlock_t——协程友好的读写锁
摘要:上一篇文章拆解了 bthread_sem_t 的实现——butex 的值直接就是信号量的计数值,用 CAS 消费、fetch_add 生产。本文继续拆解 bthread 同步原语中组合最复杂的类型:读写锁 bthread_rwlock_t。它用两个信号量(reader_sema + writer_sema)和一个互斥锁(write_queue_mutex)组合实现,核心是 reader_count 的编码技巧——正值表示活跃读者数量,负值(减去 2^30)表示有写者等待。读锁只需一次 fetch_add(1) 就能在无写者时立即获取;写锁通过减去 2^30 "冻结"读者计数,阻止新读者进入(写者优先策略)。整个设计参考了 Go 语言的 RWMutex。

在上一篇文章中,我们拆解了 bthread_sem_t——协程友好的信号量。我们看到 butex 的值直接就是计数值,用 CAS 消费、fetch_add 生产、butex_wake_n 唤醒。

现在我们拆解 bthread 同步原语中组合最复杂的类型:读写锁

读写锁的规则:多个读者可以同时读取,但写者必须独占(读写互斥、写写互斥)。这在"读多写少"的场景中比互斥锁高效得多——互斥锁连读操作都要串行化。

但读写锁的实现也最复杂:需要区分读者和写者,需要处理"读者饿死写者"或"写者饿死读者"的公平性问题。bthread 的读写锁是如何解决这些问题的?让我们从数据结构开始。


一、bthread_rwlock_t:两个信号量 + 一个互斥锁

typedef struct bthread_rwlock_t {    bthread_sem_t reader_sema;             // 读者等待写者完成    bthread_sem_t writer_sema;             // 写者等待读者完成    int reader_count;                      // 活跃读者计数(编码值)    int reader_wait;                       // 写者等待期间还需退出的读者数    bool wlock_flag;                       // 写锁持有标志    bthread_mutex_t write_queue_mutex;     // 写者队列互斥锁    bthread_contention_site_t writer_csite;// 写者竞争统计bthread_rwlock_t;
七个字段,但核心是三个原语的组合:
reader_sema  (bthread_sem_t)  →  读者在此等待写者完成writer_sema  (bthread_sem_t)  →  写者在此等待读者完成write_queue_mutex (bthread_mutex_t) →  写者之间串行化
读者之间的并发通过 reader_count 的原子操作实现——不需要锁。写者之间通过 write_queue_mutex 串行化。读写之间的协调通过 reader_count 的编码和两个信号量完成。

二、reader_count 的编码技巧

reader_count 是理解读写锁的关键。它用一个 int 同时编码两个信息:

reader_count
含义
>= 0
无写者等待,值就是活跃读者数量
< 0
有写者等待,值 = 实际读者数 - 2^30

RWLockMaxReaders = 1 << 30(2^30 = 1073741824)。写者获取锁时执行 fetch_add(-2^30),使 reader_count 变为负数。

为什么是 2^30 而不是 2^31?因为 int 是有符号 32 位整数,范围 [-2^31, 2^31-1]。减去 2^30 后,低 30 位仍然是实际读者数量,符号位确保值为负。实际读者数不可能达到 2^30(十亿级),所以不会溢出。

示例:
无写者,3 个读者:  reader_count = 3有写者等待,3 个读者:reader_count = 3 - 2^30 = -1073741821无写者,0 个读者:  reader_count = 0有写者等待,0 个读者:reader_count = -2^30 = -1073741824

这种编码的好处:一次原子操作就能同时传达"读者数量"和"是否有写者"两个信息。读者加锁时 fetch_add(1) 后检查结果——如果 >= 0,快速路径获取成功;如果 < 0,知道有写者在等。


三、初始化与销毁

3.1 bthread_rwlock_init

intbthread_rwlock_init(bthread_rwlock_t* __restrict rwlock,                        const bthread_rwlockattr_t* __restrict) {    // 初始化两个信号量(初值 0),禁用各自的竞争采样(读写锁自行管理)    bthread_sem_init(&rwlock->reader_sema, 0);    bthread_sem_disable_csite(&rwlock->reader_sema);    bthread_sem_init(&rwlock->writer_sema, 0);    bthread_sem_disable_csite(&rwlock->writer_sema);    // 初始化计数器    rwlock->reader_count = 0;    rwlock->reader_wait = 0;    rwlock->wlock_flag = false;    // 初始化写者队列互斥锁(禁用竞争采样)    bthread_mutexattr_t attr;    bthread_mutexattr_init(&attr);    bthread_mutexattr_disable_csite(&attr);    bthread_mutex_init(&rwlock->write_queue_mutex, &attr);    bthread_mutexattr_destroy(&attr);    return 0;}

三个要点:

  1. 信号量初值为 0——读者和写者都在信号量上等待"被唤醒",初始时没有信号
  2. 禁用内部原语的竞争采样——读写锁自行管理竞争分析,避免内部原语的采样干扰
  3. reader_count 和 reader_wait 初始化为 0——初始时没有活跃读者

3.2 bthread_rwlock_destroy

intbthread_rwlock_destroy(bthread_rwlock_t* rwlock){    bthread_sem_destroy(&rwlock->reader_sema);    bthread_sem_destroy(&rwlock->writer_sema);    bthread_mutex_destroy(&rwlock->write_queue_mutex);    return 0;}

按初始化的逆序销毁三个内部原语,各自归还 butex 到 ObjectPool。


四、加读锁

4.1 rdlock:阻塞获取

staticintrwlock_rdlock_impl(bthread_rwlock_t* rwlock,                              const struct timespec* abstime) {    // 第一步:原子递增读者计数    int reader_count = ((butil::atomic<int>*)&rwlock->reader_count)        ->fetch_add(1, butil::memory_order_acquire) + 1;    // 第二步:快速路径——无写者等待    if (reader_count >= 0) {        return 0;   // 直接获取读锁成功    }    // 第三步:慢速路径——有写者等待,在 reader_sema 上阻塞    return bthread_sem_timedwait(&rwlock->reader_sema, abstime);}
加读锁只需一次原子操作就能判断是否成功:
快速路径fetch_add(1) 后结果 >= 0 → 无写者等待,直接获取读锁。这是最常见的路径——大多数情况下没有写者。
慢速路径fetch_add(1) 后结果 < 0 → 有写者等待,在 reader_sema 上阻塞等待。
为什么先加 1 再判断?这是一种"乐观获取"策略。先假设没有写者,直接增加读者计数。如果发现有写者,也不需要撤销——读者计数已经加了 1,reader_wait 会在写者的等待逻辑中正确计算。大多数情况下读锁不会遇到写者竞争,所以 fetch_add 非常高效。

4.2 tryrdlock:非阻塞获取

staticinlineintrwlock_tryrdlock(bthread_rwlock_t* rwlock){    while (true) {        int reader_count = ((butil::atomic<int>*)&rwlock->reader_count)            ->load(butil::memory_order_relaxed);        if (reader_count < 0) {            return EBUSY;   // 有写者,获取失败        }        if (((butil::atomic<int>*)&rwlock->reader_count)                ->compare_exchange_weak(reader_count, reader_count + 1, ...)) {            return 0;       // CAS 成功        }        // CAS 失败,重试    }}

与 rdlock 的区别:使用 CAS 而非 fetch_add,遇到写者时直接返回 EBUSY 而非阻塞。


五、释放读锁

staticinlineintrwlock_unrdlock(bthread_rwlock_t* rwlock){    // 第一步:原子递减读者计数    int reader_count = ((butil::atomic<int>*)&rwlock->reader_count)        ->fetch_add(-1, butil::memory_order_relaxed) - 1;    // 第二步:快速路径——无写者等待    if (reader_count >= 0) {        return 0;   // 直接返回    }    // 安全检查:双重解锁检测    if (reader_count + 1 == 0 || reader_count + 1 == -RWLockMaxReaders) {        CHECK(false) << "rwlock_unrdlock of unlocked rwlock";        return EINVAL;    }    // 第三步:有写者等待,递减 reader_wait    int reader_wait = ((butil::atomic<int>*)&rwlock->reader_wait)        ->fetch_add(-1, butil::memory_order_relaxed) - 1;    if (reader_wait != 0) {        return 0;   // 还有其他读者未退出    }    // 第四步:最后一个读者唤醒写者    bthread_sem_post(&rwlock->writer_sema);    return 0;}

释放读锁分两步:

快速路径fetch_add(-1) 后结果 >= 0 → 无写者等待,直接返回。

慢速路径fetch_add(-1) 后结果 < 0 → 有写者等待。递减 reader_wait。如果 reader_wait 降为 0,说明当前是最后一个读者,负责唤醒写者(通过 writer_sema)。

关键细节:只有最后一个读者才唤醒写者。这避免了多个读者同时 post writer_sema 导致信号量计数不正确。


六、加写锁

staticinlineintrwlock_wrlock_impl(bthread_rwlock_t* rwlock,                                     const struct timespec* abstime) {    // 第一步:获取 write_queue_mutex(与其他写者竞争)    int rc = bthread_mutex_trylock(&rwlock->write_queue_mutex);    if (0 != rc) {        rc = bthread_mutex_timedlock(&rwlock->write_queue_mutex, abstime);        if (0 != rc) return rc;    }    // 第二步:宣布有写者等待(reader_count -= RWLockMaxReaders)    int reader_count = ((butil::atomic<int>*)&rwlock->reader_count)        ->fetch_add(-RWLockMaxReaders, butil::memory_order_release);    // 第三步:等待活跃读者全部退出    if (reader_count != 0 &&        ((butil::atomic<int>*)&rwlock->reader_wait)            ->fetch_add(reader_count) + reader_count != 0) {        // trywait 检查是否最后一个读者已经 post        rc = bthread_sem_trywait(&rwlock->writer_sema);        if (0 != rc) {            rc = bthread_sem_timedwait(&rwlock->writer_sema, abstime);            if (0 != rc) {                bthread_mutex_unlock(&rwlock->write_queue_mutex);                return rc;            }        }    }    rwlock->wlock_flag = true;  // 标记写锁已持有    return 0;}

加写锁分三步:

6.1 第一步:写者之间串行化

bthread_mutex_trylock(&rwlock->write_queue_mutex);

多个写者通过 write_queue_mutex 串行化。只有一个写者能持有这把锁——其他写者必须等待。这是写写互斥的保证。

6.2 第二步:宣布有写者等待

fetch_add(-RWLockMaxReaders)

将 reader_count 减去 2^30,使其变为负数。这有两个效果:

  1. 后续的读者会检测到负值,进入慢速路径在 reader_sema 上阻塞——这就是写者优先策略
  2. 返回值是减之前的旧值,即当前活跃的读者数量

6.3 第三步:等待读者退出

fetch_add(reader_count) + reader_count

将活跃读者数量累加到 reader_wait。如果 reader_wait 不为 0,说明还有读者未退出,写者在 writer_sema 上等待。

这里有一个优化:先 trywait 检查最后一个读者是否已经 post 了 writer_sema。如果是,直接消费信号量,不需要阻塞。

6.4 trywrlock:非阻塞获取

staticinlineintrwlock_trywrlock(bthread_rwlock_t* rwlock){    int rc = bthread_mutex_trylock(&rwlock->write_queue_mutex);    if (0 != rc) return rc;    int expected = 0;    if (!CAS(reader_count, 0 → -RWLockMaxReaders)) {        bthread_mutex_unlock(&rwlock->write_queue_mutex);        return EBUSY;    }    rwlock->wlock_flag = true;    return 0;}

两个条件缺一不可:

  1. write_queue_mutex
     可以立即获取(无其他写者)
  2. reader_count == 0
    (无活跃读者)

只有两个条件同时满足,才能 CAS 成功获取写锁。


七、释放写锁

staticinlineintrwlock_unwrlock(bthread_rwlock_t* rwlock){    rwlock->wlock_flag = false;    // 恢复 reader_count    int reader_count = ((butil::atomic<int>*)&rwlock->reader_count)        ->fetch_add(RWLockMaxReaders, butil::memory_order_release) + RWLockMaxReaders;    // 慢路径:唤醒被阻塞的读者 + 释放写者互斥锁    rwlock_unwrlock_slow(rwlock, reader_count);    return 0;}staticinlinevoidrwlock_unwrlock_slow(bthread_rwlock_t* rwlock, int reader_count){    bthread_sem_post_n(&rwlock->reader_sema, reader_count);  // 批量唤醒读者    bthread_mutex_unlock(&rwlock->write_queue_mutex);        // 允许下一个写者进入}
释放写锁分三步:
  1. 清除写锁标志wlock_flag = false
  2. 恢复 reader_count: fetch_add(RWLockMaxReaders) 加回 2^30。加回后的值就是写者持有期间累积的等待读者数量(他们在 reader_sema 上阻塞)
  3. 唤醒等待者sem_post_n 批量唤醒 reader_count 个等待的读者,然后释放 write_queue_mutex 让下一个写者进入

注意释放顺序:先唤醒读者,再释放写者互斥锁。这确保被唤醒的读者在下一个写者获取锁之前有机会运行。


八、unlock:自动判断读锁/写锁

staticinlineintrwlock_unlock(bthread_rwlock_t* rwlock){    if (rwlock->wlock_flag) {        return rwlock_unwrlock(rwlock);   // 释放写锁    } else {        return rwlock_unrdlock(rwlock);   // 释放读锁    }}

九、写者优先策略

读写锁有一个经典的公平性问题:如果读者不断到来,写者可能永远等不到——这就是"读者饿死写者"。bthread 读写锁通过 write_queue_mutex 实现了写者优先策略

时间线:  t1: 读者A 获取读锁(reader_count = 1)  t2: 写者X 获取 write_queue_mutex → reader_count -= 2^30 → 等待读者退出  t3: 读者B 尝试获取读锁 → reader_count < 0 → 在 reader_sema 上阻塞  t4: 读者A 释放读锁 → 最后一个读者 → 唤醒写者X  t5: 写者X 获取写锁  t6: 写者X 释放写锁 → 唤醒读者B + 释放 write_queue_mutex

关键:在 t3 时刻,读者 B 检测到 reader_count < 0,被阻塞而不是直接获取读锁。这就是写者优先——一旦有写者开始等待,新的读者不能"插队"。

而 write_queue_mutex 在整个写锁持有期间都被持有(从加锁到释放),确保了:

  • 同一时间只有一个写者活跃(写写互斥)
  • 写者等待期间,新读者被挡在 reader_sema 上(写者优先)

十、完整的状态转换图

把所有操作串联起来:

初始状态:reader_count = 0,无锁rdlock():    fetch_add(1) → reader_count >= 0? → 成功(共享读)                   reader_count < 0?  → 阻塞在 reader_semawrlock():    获取 write_queue_mutex    → fetch_add(-2^30)(宣布有写者)    → 等待活跃读者退出(reader_wait 降为 0)    → 设置 wlock_flag = trueunrdlock():    fetch_add(-1) → reader_count >= 0? → 直接返回                   reader_count < 0?  → reader_wait--                     → reader_wait == 0? → post(writer_sema) 唤醒写者unwrlock():    wlock_flag = false    fetch_add(+2^30)(恢复读者计数)    → sem_post_n(reader_sema)(唤醒等待的读者)    → unlock(write_queue_mutex)(允许下一个写者)

关键观察:读写锁的所有状态转换都通过 reader_count 的原子操作完成。 读者用 fetch_add(±1),写者用 fetch_add(±2^30)。两种操作的"步长"不同,不会互相干扰——读者加 1 后检查正负,写者减 2^30 后读者看到负值。


十一、C++ 封装层

11.1 RWLock

class RWLock {public:    RWLock()  { bthread_rwlock_init(&_rwlock, NULL); }    ~RWLock() { bthread_rwlock_destroy(&_rwlock); }    voidrdlock()       { bthread_rwlock_rdlock(&_rwlock); }    voidwrlock()       { bthread_rwlock_wrlock(&_rwlock); }    booltry_rdlock()   { return 0 == bthread_rwlock_tryrdlock(&_rwlock); }    booltry_wrlock()   { return 0 == bthread_rwlock_trywrlock(&_rwlock); }    booltimed_rdlock(const timespec* abstime) { ... }    booltimed_wrlock(const timespec* abstime) { ... }    voidunlock()       { bthread_rwlock_unlock(&_rwlock); }    native_handler_type native_handler() { return &_rwlock; }private:    bthread_rwlock_t _rwlock{};};

RAII 封装,构造时初始化,析构时销毁。

11.2 RAII 守卫

RWLockRdGuard(读锁守卫):

RWLockRdGuard guard(rwlock);   // 构造时加读锁// ... 临界区 ...// 析构时自动释放
RWLockWrGuard(写锁守卫):
RWLockWrGuard guard(rwlock);   // 构造时加写锁// ... 临界区 ...// 析构时自动释放

11.3 lock_guard 特化

// 通过 read 参数选择读锁/写锁std::lock_guard<bthread_rwlock_trd_lk(rwlock, true);   // 读锁std::lock_guard<bthread_rwlock_twr_lk(rwlock, false);  // 写锁

为 bthread_rwlock_t 和 bthread::RWLock 都提供了 std::lock_guard 特化。Debug 模式下会在加锁失败时记录 FATAL 日志并将指针置空,防止析构时 double unlock。


十二、与 Go RWMutex 的关系

源码注释明确说明:bthread_rwlock_t 是 Go 语言 RWMutex 的 bthread 实现。

A `bthread_rwlock_t' is a reader/writer mutual exclusion lock, which is a bthread implementation of golang RWMutex.For details, see https://github.com/golang/go/blob/master/src/sync/rwmutex.go

核心算法完全一致:

Go RWMutex
bthread_rwlock_t
读者计数
int32
 readerCount
int
 reader_count
等待读者数
int32
 readerWait
int
 reader_wait
读者唤醒
sync.Mutex
 + sync.Semaphore
bthread_sem_t
 reader_sema
写者等待
runtime_Semacquire
bthread_sem_t
 writer_sema
写者串行化
sync.Mutex
 wlock
bthread_mutex_t
 write_queue_mutex
最大读者数
1 << 30
1 << 30

区别在于阻塞原语:Go 使用 goroutine 的 semaphore,bthread 使用 butex。


十三、总结

bthread_rwlock_t 的设计可以归纳为一句话:用 reader_count 的编码技巧在读者和写者之间传递状态,用信号量和互斥锁组合实现等待和唤醒

四个关键设计:

1. reader_count 的双重编码。 一个 int 同时编码活跃读者数量和是否有写者等待。正值(≥ 0)表示读者数量,负值(减去 2^30)表示有写者。读者和写者的原子操作步长不同(±1 vs ±2^30),互不干扰。

2. 写者优先策略。 写者通过 write_queue_mutex 串行化,并在获取锁时将 reader_count 减为负值。后续的读者检测到负值后被阻塞在 reader_sema 上,不能"插队"领先于写者。

3. 组合式设计。 读写锁不是从零实现,而是组合了两个信号量和一个互斥锁。读者在 reader_sema 上等待写者完成,写者在 writer_sema 上等待读者完成,写者之间通过 write_queue_mutex 串行化。

4. 读者的乐观获取。 加读锁时先 fetch_add(1) 再判断是否有写者——大多数情况下没有写者,一次原子操作就能获取锁。即使有写者,读者计数已经加了 1,reader_wait 的计算仍然正确。


本文基于 Apache brpc 源码(src/bthread/rwlock.hsrc/bthread/rwlock.cppsrc/bthread/types.h)撰写。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-05-07 14:54:50 HTTP/2.0 GET : https://67808.cn/a/486695.html
  2. 运行时间 : 0.122189s [ 吞吐率:8.18req/s ] 内存消耗:4,720.63kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=1a14a61c3f3b68a5eef34f3932a5eaaf
  1. /yingpanguazai/ssd/ssd1/www/no.67808.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/no.67808.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/no.67808.cn/runtime/temp/6df755f970a38e704c5414acbc6e8bcd.php ( 12.06 KB )
  140. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000516s ] mysql:host=127.0.0.1;port=3306;dbname=no_67808;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000653s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.003608s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000363s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000760s ]
  6. SELECT * FROM `set` [ RunTime:0.000299s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000744s ]
  8. SELECT * FROM `article` WHERE `id` = 486695 LIMIT 1 [ RunTime:0.003947s ]
  9. UPDATE `article` SET `lasttime` = 1778136890 WHERE `id` = 486695 [ RunTime:0.005290s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.000465s ]
  11. SELECT * FROM `article` WHERE `id` < 486695 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000654s ]
  12. SELECT * FROM `article` WHERE `id` > 486695 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000471s ]
  13. SELECT * FROM `article` WHERE `id` < 486695 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.002079s ]
  14. SELECT * FROM `article` WHERE `id` < 486695 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003410s ]
  15. SELECT * FROM `article` WHERE `id` < 486695 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.004161s ]
0.123876s