order_by_rand引发的大报错

症状一 磁盘满了

40G 的磁盘突然满了,查看后发现是 mysql 的 ibtmp1 文件超过 32G 了,这个是临时文件,通过修改配置解决。

ibtmp1是非压缩的 innodb 临时表的独立表空间,通过 innodb_temp_data_file_path 参数指定文件的路径,文件名和大小,默认配置为 ibtmp1:12M:autoextend,也就是说在支持大文件的系统这个文件大小是可以无限增长的。

1
2
3
4
5
6
# 使用的命令
du -h --max-depth=1
ls -alh

# my.cnf 配置增加
innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:5G

当时这里忽略了一个问题,为什么这个文件体积暴增?

症状二 程序执行很慢,定时任务重复执行

因为要全量发布文章,定时任务每 5 分钟执行发布 70 篇文章,发现多了好多进程,根据启动时间说明 5 分钟 70 篇文章没有发布完,导致下一个任务启动重复从数据库里取数据发布,这个时候想到的是定时任务加锁,防止重复执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. 利用临时文件是否存在来实现
$lockFile = '/home/cronlock/test.lock';
if(file_exists($lockFile)) {
exit('not finish);
}
touch($lockFile);
doTheCron();
unlink($lockFile);

2. 利用排它锁来实现
$fp = fopen("/tmp/testlock.txt","w+");
if(flock($fp,LOCK_EX|LOCK_NB)){
doTheCron()
flock($fp,LOCK_UN)
}else{
echo('文件已锁定');
}
fclose($fp);

3. linux flock 锁机制 不需要改代码
*/5 * * * * flock -xn /tmp/test.lock -c 'sudo -u www /usr/bin/php /www/wwwroot/admin.abc.com/crontab.php' >> /www/abc/data/crontab.log

症状三 php 执行慢

观察文章发布日志,发现获取推荐文章比较慢,进而发现 order by rand() 函数,以为一共 2 万条数据不会慢到哪里去,没想到这个是巨坑。

1
2
3
4
5
6
7
8

#sql:select d_id ,{文章标题},{文章作者id},{作者名称},{文章摘要},{文章内容},{图片展示}, url, createdatetime, savedatetime from {文章详情页} where published='y' order by rand() limit 5

#sql:select d_id ,{文章标题},{文章作者id},{作者名称},{文章摘要},{文章内容},{图片展示}, url, createdatetime, savedatetime from {文章详情页} where published='y' and d_id>=(SELECT floor(RAND() * (SELECT MAX(d_id) FROM {文章详情页}))) ORDER BY d_id LIMIT 5;

#sql: select d_id ,{文章标题},{文章作者id},{作者名称},{文章摘要},{文章内容},{图片展示}, url, createdatetime, savedatetime {文章详情页} AS t1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM {文章详情页})) AS id) AS t2
WHERE t1.d_id >= t2.d_id
ORDER BY t1.d_id ASC LIMIT 5

修改完,2 分钟内 2000 条文章发布完毕。