背景
今天遇到一个奇怪的问题,首次启动MySQL服务时报错:
mysqld_safe error: log-error set to '/xxx/xxx/xxx/xxx/alert.log', however file don't exists. Create writable for user 'mysql'.
我在网上找到很多博客,解决方法很简单,就是去touch /xxx/xxx/xxx/xxx/alert.log,然后重启。
问题到这里就解决了,但是疑惑增加了:
-
为什么启动MySQL的进程的账号aaa明明有创建文件的权限,这里却报错没有权限创建文件;
-
解决方案也很诡异,因为我用的也是aaa账号去执行的touch /xxx/xxx/xxx/xxx/alert.log ;
-
我启动的时候用的aaa账号,但是为什么报错里显示 mysql没有权限呢(Create writable for user ‘mysql’. )
百思不得其解,看了很多篇文章,好像都无法解决我的疑惑,正所谓源码之下,了无秘密。
柳暗花明
我带着这些疑惑,翻到mysqld_safe.sh的源码,此时真相大白:
if [ -f "$err_log" -o -p "$err_log" ]; then # Log to err_log file
log_notice "Logging to '$err_log'."
elif [ "x$user" = "xroot" ]; then # running as root, mysqld can create log file; continue
echo "Logging to '$err_log'." >&2
else
case $logdir in
# We can't create $err_log, however mysqld can; continue
/tmp|/var/tmp|/var/log/mysql|$DATADIR)
echo "Logging to '$err_log'." >&2
;;
# We can't create $err_log and don't know if mysqld can; error out
*)
log_error "error: log-error set to '$err_log', however file don't exists. Create writable for user '$user'."
exit 1
;;
esac
fi
fi
这段代码是 MySQL 启动脚本中的一部分,主要用于确定是否能够将错误日志(err_log)写入到指定的文件中,以及在这个过程中进行相应的日志记录。
首先检查是否存在名为 $err_log 的文件或命名管道(-f “$err_log” -o -p “$err_log”)。如果存在,会记录日志提示“Logging to ‘$err_log’.”。(这就是为什么touch 日志后能解决这个问题)
如果不存在 $err_log 文件,且当前用户是 root 用户,则记录日志提示“Logging to ‘$err_log’.”。
如果既不存在 $err_log 文件,又不是 root 用户,那么会根据 $logdir(日志目录)的值进行不同处理:
如果 $logdir 的值为 /tmp、/var/tmp、/var/log/mysql 或者 $DATADIR(数据目录),表示 MySQL 具有在这些目录中创建日志文件的权限,因此会记录日志提示“Logging to ‘$err_log’.”。
如果 $logdir 的值不在上述列举的目录中,那么记录错误日志提醒,指出错误信息“error: log-error set to ‘$err_log’, however file don’t exists. Create writable for user ‘$user’.”,并退出 MySQL 的启动过程。
我们首次启动时没有这个alert.log,而我们的账号不是root,这个路径也不是/tmp等目录,所以才会走到最后一个分支里面报错。
到这里应该已经解决了大部分的疑惑,但有一些小伙伴说,命令明明是在root权限下执行的,为什么还会报错没有权限呢?比如我在一个博客上看到,引用过来:
[root@test2 my57_3307]# /usr/local/mysql57/bin/mysqld_safe --defaults-file=/dbdata/mysql/my57_3307/my57_3307.cnf --ledir=/usr/local/mysql57/bin
2018-08-26T14:12:45.459798Z mysqld_safe error: log-error set to '/var/log/mysqld/my57_3307.log', however file don't exists. Create writable for user 'mysql'.
于是我继续翻源码mysqld_safe.sh,这里只标注相关的源码,感兴趣的小伙伴自行去看完整源码:
# in mysqld_safe.sh
user='@MYSQLD_USER@'
--user=*) user="$val"; SET_USER=1 ;;
可以看到如果在启动命令里有–user选项的,才会按照这个user去启动,否则是用’@MYSQLD_USER@'账号,以上命令没有指定–user,那么这个账号默认是什么呢?为什么大家的报错里最后的用户都是mysql呢?
根据全局搜索,在CMakeLists.txt里面找到了答案:
# in CMakeLists.txt
SET(MYSQLD_USER "mysql")
CMake 中的 SET(MYSQLD_USER “mysql”) 命令用于设置变量 MYSQLD_USER 的值为 “mysql”。通常情况下,这种设置会被用于替换代码中的类似 @MYSQLD_USER@ 这样的占位符。在 CMake 构建过程中,这些占位符会被实际的变量值所替代。
至此真相大白了。
真相似乎很简单,但寻找真相的道路比较曲折,但了解真相后也很开心,与君分享~