php 如何防止缓存穿透?

php-fpm 运行环境下,N多并发请求到来时,如果此时缓存里没有数据,只允许一个请求去MySQL取数据然后更新缓存,其他请求再从缓存取,如何实现?

阅读 4.6k
3 个回答
  1. 分布式锁 如:redis锁 set('key', 'value', ['nx', 'ex'=>10])

  2. Gearman

这是一个很经典的线程同步问题。你只希望有一个请求去请求MySQL,也就是希望多个线程竞争一个资源,得到资源的那一个线程才有权力访问MySQL。这是线程同步时锁起到的基本作用。撇开语言不讲,单从理论本身,常见的锁

  • 排他锁:如数据库中的写锁。同一时间只能有一个写操作。也就是把所有操作串行化。这虽然可以达到你的目的,但是这个锁太重,对效率损伤非常严重。你已经在考虑使用缓存来加速访问,显然这样的高并发环境下排他锁不适用。

  • 共享锁:如数据库中的读锁。多个读操作可以同时进行,但读的时候不能写。而一旦有一个写操作过来,所有读操作都必须等。

你需要的是共享锁,很多语言中会实现这类读写锁。虽然我不知道PHP有没有,对这门语言不熟,原理上讲是一样的。

  • 先尝试获取读锁,读取缓存

  • 发现缓存为空,提升为写锁

  • 再次验证缓存是否为空(为什么?)

  • 如果还是为空,则可以访问数据库读数据写缓存

  • 释放锁

  • 完成

这样已经把缓存刷新造成的影响降到最低,但是仍然是有额外开销的。有没有更好的办法?也是有的,不要让用户请求的线程去竞争写锁,单独运行一个线程负责定期刷新缓存,只有它去拿写锁,保证缓存里一直有东西,其他线程甚至都不用锁,只管读就好了。

线程同步即使做得再完美,对性能的损伤都是不容忽视的,所以能不做尽量不做,高并发环境里面这就是最基本的一条原则。

PS:我也不知道这个问题为什么打上MongoDB的标签。我是谁?我在哪?

你自己的问题里不就有答案了么?加个锁就行。redis 本身就可以做锁。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题