【发布时间】:2016-06-20 02:12:01
【问题描述】:
这主要是理论,所以如果它变得冗长,我很抱歉。
背景
我正在从事的项目从其他网站(外部网站,非我们托管)提取信息。我们希望尽可能提供最接近实时的信息,以便我们的用户能够立即获得相关信息。这意味着不断监控和更新表格。
很难展示我以前在这方面的工作,但在过去几周里,我一直在搜索“在数据库中维护实时数据”和“在发生外部更改时立即更新数据库”等类似内容.但一切都无济于事。我想维护最新记录的问题很常见,所以我不确定为什么针对它的彻底解决方案似乎如此罕见。
为了遵守 SO 的指导方针,我不是在寻找意见,而是在寻找当前的最佳实践和行业中最常用/接受的有效方法。
目前,使用cron job,我们能做的最好的事情就是每分钟运行一个进程。
* * * * * cd /home/.../public_html/.../ && /usr/bin/php .../robot.php >/dev/null 2>&1
问题是,我们正在从数千个其他站点提取数据(每一行都是一个站点),有时更新可能需要几分钟或更长时间。每分钟只调用一次函数是不够的。理想情况下,我们想要近乎即时的分辨率。
检查是否需要更新行很快。基本上只是您的简单哈希比较:
if(hash(current) != hash(previous)){
... update row ...
}
使用仅由 cron 作业触发的进程意味着,如果一行最终得到更新,则该进程将被暂停,直到它完成,或者直到 cron 作业在一分钟后触发一个新进程。
没有布埃诺!呸!如果由于某种可怕的命运转折,每一行都需要更新,那么可能需要数小时(或更长时间)才能使所有记录成为最新。并且在那个时候,已经被传递的行将会过时。
注意:数据库的设置方式使得当前正在更新的行无法被新进程访问。该函数实质上是向下爬取表格,找到下一个尚未读取/更新的可用行,然后深入研究。更新完成后,它会继续下一个可用行。
每个进程在到达表的末尾或表中的所有行都标记为已读时被终止。此时,所有行都被重置为未读,流程重新开始。
随着收集的数据量越来越大,提高分辨率的唯一方法是同时运行多个进程。
但是多少才算太多?
可能的解决方案(方法)
到目前为止,我想出的最好的方法是,尽可能快地遍历所有行,是这样的:
Cron Job 调用第一个进程 (P1)
P1 浏览表,直到找到未读且需要更新的行,然后潜入
一旦 P1 进入该行,它就会调用第二个相同的进程 (P2) 从该点继续
P2 浏览表,直到找到未读且需要更新的行,然后潜入
一旦 P2 进入该行,它就会调用第三个相同的进程 (P3) 从该点继续
...等等。
本质上,每当一个进程进入一行来更新它时,就会调用一个新进程来继续。
但是...父进程并没有死。这意味着一旦他们完成更新,他们就会再次开始爬取表格,寻找下一个可用行。
并且...除此之外,每分钟仍会触发一个新的 cron 作业。
这意味着可能有数千个相同的进程可能同时运行。进程数不能超过表中的记录数。最坏的情况是每一行都同时更新,并且在任何更新完成之前触发一两个 cron 作业。 cron 作业将立即终止,因为没有可更新的行。随着每个进程完成其更新,它也会因同样的原因立即死亡。
上述情况是最坏的情况。每次传递需要更新超过 5 或 10 行的可能性不大,但理论上可以同时更新每一行。
可能的改进(主要是资源,而不是速度或分辨率)
监控和限制允许的活动进程的数量,并杀死任何被触发的新进程。但这会引出诸如“多少才算太多?”和“达到特定分辨率所需的最小数量是多少?”之类的问题
让每个进程一次标记多行 (5-10),并且在处理完集合中的所有行之前不要继续。这会导致同时处理的最大数量减少一个因子,无论多少行一次被标记。
就像我一开始所说的,这对于数据库架构师来说肯定是一个常见的问题。有没有比我提出的更好/更快/更有效的方法来维护当前记录?
感谢您一直陪伴我!
【问题讨论】:
-
为了解决此类问题,我们使用队列管理器、cron 和简单的手工 php-daemon(使用 symfony2 框架占用大约 60mb 内存)。 Ofc 守护进程可以是多线程的,但我们使用简单的单线程版本。此外,当脚本启动多次并在找到运行副本时退出时,守护程序会检查自身的运行状态。守护程序在 10 分钟后死亡(以解决可能的内存泄漏)或队列为空。像这样的......
标签: php mysql sql-server database cron