MySQL series (2): mysqld_safe analysis

introduction

In the previous section, we manually compiled mysql5.7.28. This section starts with the startup script mysqld_safe and continues to introduce MySQL. Compared with running mysqld directly with mysqld_safe, it has the following advantages:

  • Commands are simpler. We can run mysqld_safe directly, and it will help us splicing parameters such as basedir/datadir/plugin-dir/user/log-error/pid-file to start mysqld.
  • Run more reliably. It monitors mysqld, has a restart mechanism, and cleans up the pid/socket/shutdown files when the mysqld process exits abnormally.

Reminder: There are many codes ahead, if you are not patient enough, you can directly skip to the final summary and keywords!

analyze

ignore signal

trap '' 1 2 3 15
trap '' 13

Signals SIGHUP/SIGINT/SIGQUIT/SIGTERM/SIGPIPE are ignored. At this time, press Ctrl+C or kill $PID, it will not be exited. But you can kill the process with kill -9 $PID.

Setting parameters

defaults=
case "$1" in
    --no-defaults|--defaults-file=*|--defaults-extra-file=*)
      defaults="$1"; shift
      ;;
esac

If the first argument is --no-defaults or --defaults-file or --defaults-extra-file, assign to defaults and shift the argument left.

find_basedir_from_cmdline "$@"

# --basedir is already overridden on command line
if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION" ; then
  # Search for mysqld and set ledir
  for dir in bin libexec sbin bin ; do
    if test -x "$MY_BASEDIR_VERSION/$dir/mysqld" ; then
      ledir="$MY_BASEDIR_VERSION/$dir"
      break
    fi
  done

else
  # Basedir should be parent dir of bindir, unless some non-standard
  # layout is used

  cd "`dirname $0`"
  if [ -h "$0" ] ; then
    realpath="`ls -l  "$0" | awk '{print $NF}'`"
    cd "`dirname "$realpath"`"
  fi
  cd ..
  MY_PWD="`pwd`"

  # Search for mysqld and set ledir and BASEDIR
  for dir in bin libexec sbin bin ; do
    if test -x "$MY_PWD/$dir/mysqld" ; then
      MY_BASEDIR_VERSION="$MY_PWD"
      ledir="$MY_BASEDIR_VERSION/$dir"
      break
    fi
  done

  # If we still didn't find anything, use the compiled-in defaults
  if test -z "$MY_BASEDIR_VERSION" ; then
    MY_BASEDIR_VERSION='/usr/local/mysql'
    ledir='/usr/local/mysql/bin'
  fi
fi
find_basedir_from_cmdline () {
    
    
  for arg in "$@"; do
    case $arg in
      --basedir=*)
        MY_BASEDIR_VERSION="`echo "$arg" | sed -e 's;^--[^=]*=;;'`"
        # Convert to full path
        cd "$MY_BASEDIR_VERSION"
        if [ $? -ne 0 ] ; then
          log_error "--basedir set to '$MY_BASEDIR_VERSION', however could not access directory"
          exit 1
        fi
        MY_BASEDIR_VERSION="`pwd`"
        ;;
    esac
  done
}

If --basedir is set, assign the corresponding full path to MY_BASEDIR_VERSION. sed -e 's; –[ =]*=;;' indicates that the content between – and = is removed, that is, only the value behind –basedir= is left. ledir is set to the directory where mysqld is located. If --basedir is not set, assign the parent of the directory where the current script is located to MY_BASEDIR_VERSION. If there is no mysqld, the default value /usr/local/mysql is used, and the default value of ledir is /usr/local/mysql/bin.

if test -d $MY_BASEDIR_VERSION/data/mysql
then
  DATADIR=$MY_BASEDIR_VERSION/data
# Or just give up and use our compiled-in default
else
  DATADIR=/usr/local/mysql/data
fi

if test -z "$MYSQL_HOME"
then 
  MYSQL_HOME=$MY_BASEDIR_VERSION
fi
export MYSQL_HOME

Set DATADIR, the default is /usr/local/mysql/data. And output MYSQL_HOME as an environment variable.

if [ -n "${PLUGIN_DIR}" ]; then
  plugin_dir="${PLUGIN_DIR}"
else
  # Try to find plugin dir relative to basedir
  for dir in lib64/mysql/plugin lib64/plugin lib/mysql/plugin lib/plugin
  do
    if [ -d "${MY_BASEDIR_VERSION}/${dir}" ]; then
      plugin_dir="${MY_BASEDIR_VERSION}/${dir}"
      break
    fi
  done
  # Give up and use compiled-in default
  if [ -z "${plugin_dir}" ]; then
    plugin_dir='/usr/local/mysql/lib/plugin'
  fi
fi
plugin_dir="${plugin_dir}${PLUGIN_VARIANT}"

Look for the plugin directory, the default is /usr/local/mysql/lib/plugin.

USER_OPTION=""
if test -w / -o "$USER" = "root"
then
  if test "$user" != "root" -o $SET_USER = 1
  then
    USER_OPTION="--user=$user"
  fi
  if test -n "$open_files"
  then
    ulimit -n $open_files
  fi
fi

if test -n "$open_files"
then
  append_arg_to_args "--open-files-limit=$open_files"
fi

If you execute mysqld_safe without setting parameters, the default user is mysql, so USER_OPTION="–user=mysql" here. open_files need to set --open-files-limit, the default is empty.

if test -z "$pid_file"
then
  pid_file="$DATADIR/`hostname`.pid"
  pid_file_append="`hostname`.pid"
else
  pid_file_append="$pid_file"
  case "$pid_file" in
    /* ) ;;
    * )  pid_file="$DATADIR/$pid_file" ;;
  esac
fi
append_arg_to_args "--pid-file=$pid_file_append"

if test -n "$mysql_unix_port"
then
  append_arg_to_args "--socket=$mysql_unix_port"
fi
if test -n "$mysql_tcp_port"
then
  append_arg_to_args "--port=$mysql_tcp_port"
fi

Append parameters --pid-file/--socket/--port. If mysql_unix_port and mysql_tcp_port are not specified at startup, --socket and --port are not appended.

build command

cmd="`mysqld_ld_preload_text`$NOHUP_NICENESS"

for i in  "$ledir/$MYSQLD" "$defaults" "--basedir=$MY_BASEDIR_VERSION" \
  "--datadir=$DATADIR" "--plugin-dir=$plugin_dir" "$USER_OPTION"
do
  cmd="$cmd "`shell_quote_string "$i"`
done
cmd="$cmd $args"
# Avoid 'nohup: ignoring input' warning
test -n "$NOHUP_NICENESS" && cmd="$cmd < /dev/null"

Splice startup commands and parameters, and test, where –log-error and –pid-file come from $args.

nohup /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --log-error=DESKTOP-7GPR56L.err --pid-file=DESKTOP-7GPR56L.pid

Excuting an order

Next is the main loop while true

while true
do
  start_time=`date +%M%S`

  eval_log_error "$cmd"
  
  ...
done
eval_log_error () {
    
    
  cmd="$1"
  case $logging in
    file)
      if [ -w / -o "$USER" = "root" ]; then
        cmd="$cmd > /dev/null 2>&1"
      else
        cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1"
      fi
      ;;
    syslog)
      cmd="$cmd --log-syslog=1 --log-syslog-facility=$syslog_facility '--log-syslog-tag=$syslog_tag' > /dev/null 2>&1"
      ;;
    both)
      if [ -w / -o "$USER" = "root" ]; then
        cmd="$cmd --log-syslog=1 --log-syslog-facility=$syslog_facility '--log-syslog-tag=$syslog_tag' > /dev/null 2>&1"
      else
        cmd="$cmd --log-syslog=1 --log-syslog-facility=$syslog_facility '--log-syslog-tag=$syslog_tag' >> "`shell_quote_string "$err_log"`" 2>&1"
      fi
      ;;
    *)
      echo "Internal program error (non-fatal):" \
           " unknown logging method '$logging'" >&2
      ;;
  esac

  #echo "Running mysqld: [$cmd]"
  eval "$cmd"
}

In the eval_log_error function, continue to splice parameters and execute them. This time, the log is redirected to cmd="$cmd > /dev/null 2>&1". The final command is as follows:

nohup /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --log-error=DESKTOP-7GPR56L.err --pid-file=DESKTOP-7GPR56L.pid < /dev/null > /dev/null 2>&1

exception handling

if test ! -f "$pid_file"		# This is removed if normal shutdown
  then
    break
  else                                  # self's mysqld crashed or other's mysqld running
    PID=`cat "$pid_file"`
    if kill -0 $PID > /dev/null 2> /dev/null
    then                                # true when above pid belongs to a running mysqld process
      log_error "A mysqld process with pid=$PID is already running. Aborting!!"
      exit 1
    fi
  fi

  if test -f "$pid_file.shutdown"	# created to signal that it must stop
  then
    log_notice "$pid_file.shutdown present. The server will not restart."
    break
  fi

The pid file is located in the data directory by default, and the content is the PID. The pid file is deleted on a normal shutdown. If it is determined that there is already a running mysqld process, the script exits. If there is a shutdown file, exit the loop.

if test $end_time -gt 0 -a $have_sleep -gt 0
  then
    # throttle down the fast restarts
    if test $end_time -eq $start_time
    then
      fast_restart=`expr $fast_restart + 1`
      if test $fast_restart -ge $max_fast_restarts
      then
        log_notice "The server is respawning too fast. Sleeping for 1 second."
        sleep 1
        sleep_state=$?
        if test $sleep_state -gt 0
        then
          log_notice "The server is respawning too fast and no working sleep command. Turning off trottling."
          have_sleep=0
        fi

        fast_restart=0
      fi
    else
      fast_restart=0
    fi
  fi

If frequent freezes occur, try to sleep for 1 second to wait for the next cycle restart, and record the log.

if true && test $KILL_MYSQLD -eq 1
  then
    # Test if one process was hanging.
    # This is only a fix for Linux (running as base 3 mysqld processes)
    # but should work for the rest of the servers.
    # The only thing is ps x => redhat 5 gives warnings when using ps -x.
    # kill -9 is used or the process won't react on the kill.
    numofproces=`ps xaww | grep -v "grep" | grep "$ledir/$MYSQLD\>" | grep -c "pid-file=$pid_file"`

    log_notice "Number of processes running now: $numofproces"
    I=1
    while test "$I" -le "$numofproces"
    do 
      PROC=`ps xaww | grep "$ledir/$MYSQLD\>" | grep -v "grep" | grep "pid-file=$pid_file" | sed -n '$p'` 

      for T in $PROC
      do
        break
      done
      #    echo "TEST $I - $T **"
      if kill -9 $T
      then
        log_error "$MYSQLD process hanging, pid $T - killed"
      else
        break
      fi
      I=`expr $I + 1`
    done
  fi
  if [ ! -h "$pid_file" ]; then
    rm -f "$pid_file"
  fi
  if [ ! -h "$safe_mysql_unix_port" ]; then
    rm -f "$safe_mysql_unix_port"
  fi
  if [ ! -h "$pid_file.shutdown" ]; then
    rm -f "$pid_file.shutdown"
  fi

If KILL_MYSQLD=1, kill all mysqld processes and clean up the three files of pid/socket/shutdown.

Summarize

Excellent projects always have something worth learning, such as learning how to write mysqld_safe, we can learn its trap, nohup and log redirection, etc., to simply implement a script for a daemon to start a background process. In addition, at this time, there is no need to worry about the end of the process caused by the exit of the user terminal. Of course, there are many ways, both service and screen can achieve this effect.

Key words

Ignore signals; set parameters; generate commands; execute commands; exception handling


Welcome to pay attention to the official account, it is more convenient to get push, and to communicate when encountering problems!

Technical long-distance running

Guess you like

Origin blog.csdn.net/CanvaChen/article/details/102886375