技术分享 | 从库数据的查找和参数 slave_rows_search_algorithms

探讨MySQL主从复制中从库的数据查找策略,包括不同算法(TABLE_SCAN, INDEX_SCAN, HASH_SCAN)对性能的影响,及如何通过合理设置主键和索引来减少延迟。

作者:高鹏
文章末尾有他著作的《深入理解MySQL主从原理 32讲》,深入透彻理解MySQL主从,GTID相关技术知识。

本文节选自《深入理解MySQL主从原理》第24节
注意:本文分为正文和附件两部分,都是图片格式,如果正文有图片不清晰可以将附件的图片保存到本地查看。

本节包含一个笔记如下:
https://www.jianshu.com/p/5183fe0f00d8

我们前面已经知道了对于 DML 语句来讲其数据的更改将被放到对应的 Event 中。比如‘Delete’语句会将所有删除数据的 before_image放到DELETE_ROWS_EVENT 中,从库只要读取这些 before_image 进行数据查找,然后调用相应的‘Delete’的操作就可以完成数据的删除了。下面我们来讨论一下从库是如何进行数据查找的。

本节我们假定参数 binlog_row_image 设置为‘FULL’也就是默认值,关于 binlog_row_image 参数的影响在第11节已经描述过了。

一、从一个列子出发

在开始之前我们先假定参数‘slave_rows_search_algorithms’为默认值,即:

  • TABLE_SCAN,INDEX_SCAN

因为这个参数会直接影响到对索引的利用方式。

我们还是以‘Delete’操作为例,实际上对于索引的选择‘Update’操作也是一样的,因为都是通过 before_image 去查找数据。我测试的表结构、数据和操作如下:

mysql> show create table tkkk \G
*************************** 1. row ***************************
       Table: tkkk
Create Table: CREATE TABLE `tkkk` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  `c` int(11) DEFAULT NULL,
  KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> select * from tkkk;
+------+------+------+
| a    | b    | c    |
+------+------+------+
|    1 |    1 |    1 |
|    2 |    2 |    2 |
|    3 |    3 |    3 |
|    4 |    4 |    4 |
|    5 |    5 |    5 |
|    6 |    6 |    6 |
|    7 |    7 |    7 |
|    8 |    8 |    8 |
|    9 |    9 |    9 |
|   10 |   10 |   10 |
|   11 |   11 |   11 |
|   12 |   12 |   12 |
|   13 |   13 |   13 |
|   15 |   15 |   15 |
|   15 |   16 |   16 |
|   15 |   17 |   17 |
+------+------+------+
16 rows in set (2.21 sec)
mysql> delete from tkkk where a=15;
Query OK, 3 rows affected (6.24 sec)
因为我做了debug索引这里时间看起来很长

对于这样一个‘Delete’语句来讲主库会利用到索引 KEY a,删除的三条数据我们实际上只需要一次索引的定位(参考btr_cur_search_to_nth_level函数),然后顺序扫描接下来的数据进行删除就可以了。大概的流程如下图:

image.png

这条数据删除的三条数据的 before_image 将会记录到一个 DELETE_ROWS_EVENT 中。从库应用的时候会重新评估应该使用哪个索引,优先使用主键和唯一键。对于 Event 中的每条数据都需要进行索引定位操作,并且对于非唯一索引来讲第一次返回的第一行数据可能并不是删除的数据,还需要需要继续扫描下一行,在函数 Rows_log_event::do_index_scan_and_update 中有如下代码:

while (record_compare(m_table, &m_cols))//比较每一个字段 如果不相等 扫描下一行
  {
    while((error= next_record_scan(false)))//扫描下一行
    {
      /* We just skip records that has already been deleted */
      if (error == HA_ERR_RECORD_DELETED)
        continue;
      DBUG_PRINT("info",("no record matching the given row found"));
      goto end;
    }
  }

这些代价是比主库更大的。在这个列子中没有主键和唯一键,因此依旧使用的是索引KEY a,大概流程如下图:

image.png

但是如果我们在从库增加一个主键,那么在从库进行应用的时候流程如下:

image.png

我们从上面的流程来看,主库‘Delete’操作和从库‘Delete’操作主要的区别在于:

  • 从库每条数据都需要索引定位查找数据。
  • 从库在某些情况下通过非唯一索引查找的数据第一条数据可能并不是删除的数据,因此还需要继续进行索引定位和查找。

对于主库来讲一般只需要一次数据定位查找即可,接下来访问下一条数据就好了。其实对于真正的删除操作来讲并没有太多的区别。如果合理的使用了主键和唯一键可以将上面提到的两点影响降低。在造成从库延迟的情况中,没有合理的使用主键和唯一键是一个比较重要的原因。

最后如果表上一个索引都没有的话,那么情况变得更加严重,简单的图如下:

image.png

我们可以看到每一行数据的更改都需要进行全表扫描,这种问题就非常严重了。这种情况使用参数‘slave_rows_search_algorithms’的HASH_SCAN选项也许可以提高性能,下面我们就来进行讨论。

二、确认查找数据的方式

前面的例子中我们接触了参数‘slave_rows_search_algorithms’,这个参数主要用于确认如何查找数据。其取值可以是下面几个组合(来自官方文档),源码中体现为一个位图:

  • TABLE_SCAN,INDEX_SCAN(默认值)
  • INDEX_SCAN,HASH_SCAN
  • TABLE_SCAN,HASH_SCAN
  • TABLE_SCAN,INDEX_SCAN,HASH_SCAN

在源码中有如下的说明,当然官方文档也有类似的说明:

  /*
    Decision table:
    - I  --> Index scan / search
    - T  --> Table scan
    - Hi --> Hash over index
    - Ht --> Hash over the entire table

    |--------------+-----------+------+------+------|
    | Index\Option | I , T , H | I, T | I, H | T, H |
    |--------------+-----------+------+------+------|
    | PK / UK      | I         | I    | I    | Hi   |
    | K            | Hi        | I    | Hi   | Hi   |
    | No Index     | Ht        | T    | Ht   | Ht   |
    |--------------+-----------+------+------+------|

  */

实际上源码中会有三种数据查找的方式,分别是:

  • ROW_LOOKUP_INDEX_SCAN

对应函数接口:Rows_log_event::do_index_scan_and_update

  • ROW_LOOKUP_HASH_SCAN

对应函数接口:Rows_log_event::do_hash_scan_and_update
它又包含:
(1) Hi --> Hash over index
(2) Ht --> Hash over the entire table
后面讨论

  • ROW_LOOKUP_TABLE_SCAN

对应函数接口:Rows_log_event::do_table_scan_and_update

在源码中如下:

switch (m_rows_lookup_algorithm)//根据不同的算法决定使用哪个方法
    {
      case ROW_LOOKUP_HASH_SCAN:
        do_apply_row_ptr= &Rows_log_event::do_hash_scan_and_update;
        break;

      case ROW_LOOKUP_INDEX_SCAN:
        do_apply_row_ptr= &Rows_log_event::do_index_scan_and_update;
        break;

      case ROW_LOOKUP_TABLE_SCAN:
        do_apply_row_ptr= &Rows_log_event::do_table_scan_and_update;
        break;

决定如何查找数据以及通过哪个索引查找正是通过参数‘slave_rows_search_algorithms’的设置和表中是否有合适的索引共同决定的,并不是完全由‘slave_rows_search_algorithms’参数决定。

下面这个图就是决定的过程,可以参考函数decide_row_lookup_algorithm_and_key(图24-1,高清原图包含在文末原图中)。

24-1.png

三、ROW_LOOKUP_HASH_SCAN方式的数据查找

总的来讲这种方式和 ROW_LOOKUP_INDEX_SCAN和ROW_LOOKUP_TABLE_SCAN 都不同,它是通过表中的数据和 Event 中的数据进行比对,而不是通过 Event 中的数据和表中的数据进行比对,下面我们将详细描述这种方法。

假设我们将参数‘slave_rows_search_algorithms’设置为 INDEX_SCAN,HASH_SCAN ,且表上没有主键和唯一键的话,那么上图的流程将会把数据查找的方式设置为 ROW_LOOKUP_HASH_SCAN。

在 ROW_LOOKUP_HASH_SCAN 又包含两种数据查找的方式:

  • Hi --> Hash over index
  • Ht --> Hash over the entire table

对于 ROW_LOOKUP_HASH_SCAN 来讲,其首先会将 Event 中的每一行数据读取出来存入到 HASH 结构中,如果能够使用到 Hi 那么还会额外维护一个集合(set),将索引键值存入集合,作为索引扫描的依据。如果没有索引这个集合(set)将不会维护直接使用全表扫描,即Ht。
Ht --> Hash over the entire table 会全表扫描,其中每行都会查询 hash 结构来比对数据。Hi --> Hash over index 则会通过前面我们说的集合(set)来进行索引定位扫描,每行数据也会去查询 hash 结构来比对数据。

需要注意一点这个过程的单位是 Event,我们前面说过一个 DELETE_ROWS_EVENT 可能包含了多行数据,Event 最大为 8K 左右。因此使用 Ht --> Hash over the entire table 的方式,将会从原来的每行数据进行一次全表扫描变为每个 Event 才进行一次全表扫描

但是对于 Hi --> Hash over index 来讲效果就没有那么明显了,因为如果删除的数据重复值很少的情况下,依然需要足够多的索引定位查找才行,但是如果删除的数据重复值较多那么构造的集合(set)元素将会大大减少,也就减少了索引查找定位的开销。

考虑另外一种情况,如果我的每条 delete 语句一次只删除一行数据而不是 delete 一条语句删除大量的数据,那这种情况每个 DELETE_ROWS_EVENT 只有一条数据存在,那么使用 ROW_LOOKUP_HASH_SCAN 方式并不会提高性能,因为这条数据还是需要进行一次全表扫描或者索引定位才能查找到数据,和默认的方式没什么区别。

整个过程参考如下接口:

  • Rows_log_event::do_hash_scan_and_update:总接口,调用下面两个接口。
  • Rows_log_event::do_hash_row:将数据加入到hash结构,如果有索引还需要维护集合(set)。
  • Rows_log_event::do_scan_and_update:查找并且进行删除操作,会调用Rows_log_event::next_record_scan进行数据查找。
  • Rows_log_event::next_record_scan:具体的查找方式实现了Hi --> Hash over index和Ht --> Hash over the entire table的查找方式

下面我们还是用最开始的列子,我们删除了三条数据,因此 DELETE_ROW_EVENT 中包含了三条数据。假设我们参数‘slave_rows_search_algorithms’设置为 INDEX_SCAN,HASH_SCAN。因为我的表中没有主键和唯一键,因此会最终使用 ROW_LOOKUP_HASH_SCAN 进行数据查找。但是因为我们有一个索引 key a,因此会使用到 Hi --> Hash over index。为了更好的描述 Hi 和 Ht 两种方式,我们也假定另一种情况是表上一个索引都没有,我将两种方式放到一个图中方便大家发现不同点,如下图(图24-2,高清原图包含在文末原图中):

24-2.png

四、总结

我记得以前有位朋友问我主库没有主键如果我在从库建立一个主键能降低延迟吗?这里我们就清楚了答案是肯定的,因为从库会根据 Event 中的行数据进行使用索引的选择。那么总结一下:

  • slave_rows_search_algorithms 参数设置了 HASH_SCAN 并不一定会提高性能,只有满足如下两个条件才会提高性能:
    (1)(表中没有任何索引)或者(有索引且本条 update/delete 的数据关键字重复值较多)。
    (2) 一个 update/delete 语句删除了大量的数据,形成了很多个 8K 左右的 UPDATE_ROW_EVENT/DELETE_ROW_EVENT。update/delete 语句只修改少量的数据(比如每个语句修改一行数据)并不能提高性能。

  • 从库索引的利用是自行判断的,顺序为主键->唯一键->普通索引。

  • 如果 slave_rows_search_algorithms 参数没有设置 HASH_SCAN ,并且没有主键/唯一键那么性能将会急剧下降造成延迟。如果连索引都没有那么这个情况更加严重,因为更改的每一行数据都会引发一次全表扫描。

因此我们发现在 MySQL 中强制设置主键又多了一个理由。


第24节结束

一键安装mysql脚本#!/bin/bash# # 要用root用户 ## #获取本机的ip地址,然后将这个ip地址设置为环境变量ipaddr的值 export ipaddr=`ip a | grep -w inet | grep -v 127| awk '{ print $2 }' |awk -F '/' '{print $1}'` #将本机的ip地址的第三第四位合在一起,然后赋予给环境变量serverid,加入本机ip为10.192.168.178,那么serverid=168178 export serverid=`echo $ipaddr | awk -F '.' '{print $3$4}'` #以下这一段是为了求得机器的内存是多少。dmidecode -t 17是获取系统内存相关信息,并把这个值赋予给MEM_SIZE这个环境变量 export UNIT=`dmidecode -t 17|grep -i size |grep -i GB|wc -l` if [[ $UNIT -eq 0 ]]; then export MEM_SIZE=`dmidecode -t 17|grep -i size |grep -v 'No'|awk '{sum+=$2;}END{print sum;}'` else export SIZE=`dmidecode -t 17|grep -i size |grep -v 'No'|awk '{sum+=$2;}END{print sum;}'` export MEM_SIZE=$(($SIZE*1024)) fi # 物理内存等于虚拟内存除以2,并把这个值赋予给buffer这个环境变量 export buffer=$(($MEM_SIZE/2)) # 密码需要修改 export pwd=yourpassword export rplpwd=yourpassword #统计一下 系统中装有mariadb-libs的数量 mariadbcheck=`rpm -qa|grep mariadb-libs|wc -l` #如果统计值后发现mariadb-libs的数量等于1,说明系统里装了mariadb-libs,那么我就要用yum remove删除他 if [[ $mariadbcheck -eq 1 ]]; then yum remove -y mariadb-libs fi #解压/opt 目录下的 mysql-8.0.13-linux-glibc2.12-x86_64.tar.xz 文件,并将解压过程中的输出信息重定向到 /dev/null ,这意味着不会在终端显示解压的详细过程输出。 #tar 是用于打包解包文件的命令。-J 表示使用 xz 格式进行解压缩。-x 表示解包。-v 表示显示详细信息。-f 后跟要操作的文件。 tar -Jxvf /opt/mysql-8.0.13-linux-glibc2.12-x86_64.tar.xz >/dev/null mv /opt/mysql-8.0.13-linux-glibc2.12-x86_64/* /usr/local/mysql/ chown -R mysql:mysql /usr/local/mysql/ cp /usr/local/mysql/bin/* /usr/bin/ #检查是否存在/etc/my.cnf个文件,如果存在那么就以当天日期重命名这个文件然后备份为bak,例如my.cnf.20240905140424.bak if [ -s /etc/my.cnf ]; then mv /etc/my.cnf /etc/my.cnf.`date +%Y%m%d%H%M%S`.bak fi #创建一个新的/etc/my.cnf文件,然后将以下内容写进去,直至遇到EOF这个标识符,EOF之后的内容就别写进去了 cat >/etc/my.cnf <<EOF [mysqld] read_only = 0 show_compatibility_56 = 1 port = 3306 server_id = $serverid user = mysql basedir = /usr/local/mysql datadir = /vdb/mysql/data socket = /vdb/mysql/tmp/mysql.sock tmpdir = /vdb/mysql/tmp character_set_server = utf8mb4 transaction_isolation = READ-COMMITTED event_scheduler = 1 default-time-zone = "+8:00" log_timestamps = SYSTEM explicit_defaults_for_timestamp = 1 secure_file_priv = "" skip_slave_start = 1 skip_name_resolve = 1 skip_external_locking = 1 lower_case_table_names = 1 default_storage_engine = InnoDB disabled_storage_engines = ARCHIVE,BLACKHOLE,EXAMPLE,FEDERATED,MEMORY,MERGE,NDB # connection # lock_wait_timeout = 1800 max_connections = 3000 max_connect_errors = 1000000 interactive_timeout = 1800 wait_timeout = 1800 # session memory setting # read_buffer_size = 8M read_rnd_buffer_size = 8M sort_buffer_size = 2M tmp_table_size = 64M join_buffer_size = 64M max_heap_table_size = 64M max_allowed_packet = 64M # cache config # key_buffer_size = 16M table_definition_cache = 2000 table_open_cache = 2000 table_open_cache_instances = 8 query_cache_type = 0 query_cache_size = 0 thread_cache_size = 200 open_files_limit = 65536 binlog_cache_size = 1M max_binlog_cache_size = 512M # log config # log_bin = /vdb/mysql/binlog/mysql-bin binlog_format = row sync_binlog = 1 binlog_error_action = ABORT_SERVER max_binlog_size = 250M binlog_rows_query_log_events = 1 expire_logs_days = 7 log_bin_trust_function_creators = 1 log_slave_updates = 1 relay_log = /vdb/mysql/binlog/mysql-relay relay_log_recovery = 1 master_info_repository = TABLE relay_log_info_repository = TABLE log_error = /vdb/mysql/logs/error.log slow_query_log = 1 slow_query_log_file = /vdb/mysql/logs/slow.log long_query_time = 5 log_queries_not_using_indexes = 1 log_slow_admin_statements = 1 log_slow_slave_statements = 1 log_throttle_queries_not_using_indexes = 10 min_examined_row_limit = 100 # innodb settings # innodb_data_home_dir = /vdb/mysql/data innodb_log_group_home_dir = /vdb/mysql/data innodb_file_per_table = 1 innodb_data_file_path = ibdata1:1G:autoextend innodb_flush_log_at_trx_commit = 1 innodb_buffer_pool_size = ${buffer}M innodb_buffer_pool_instances = 8 innodb_buffer_pool_load_at_startup = 1 innodb_buffer_pool_dump_at_shutdown = 1 innodb_buffer_pool_dump_pct = 25 innodb_lock_wait_timeout = 10 innodb_io_capacity = 10000 innodb_io_capacity_max = 20000 innodb_max_dirty_pages_pct = 60 innodb_flush_method = O_DIRECT innodb_log_file_size = 2G innodb_log_files_in_group = 2 innodb_log_buffer_size = 64M innodb_purge_threads = 2 innodb_write_io_threads = 16 innodb_read_io_threads = 16 innodb_large_prefix = 1 innodb_thread_concurrency = 64 innodb_print_all_deadlocks = 1 innodb_strict_mode = 1 innodb_sort_buffer_size = 32M innodb_stats_persistent_sample_pages = 64 innodb_autoinc_lock_mode = 2 innodb_online_alter_log_max_size = 1G innodb_open_files = 4096 innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:20G # undo config # innodb_rollback_segments = 128 innodb_undo_log_truncate = 1 innodb_max_undo_log_size = 4G # GTID # gtid_mode = ON enforce_gtid_consistency = 1 binlog_gtid_simple_recovery = 1 # MTS # slave_parallel_type = LOGICAL_CLOCK slave_parallel_workers = 16 slave_preserve_commit_order = ON slave_rows_search_algorithms = 'INDEX_SCAN,HASH_SCAN' # SEMISYNC # plugin-load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so" rpl_semi_sync_master_enabled = 1 rpl_semi_sync_slave_enabled = 1 rpl_semi_sync_master_timeout = 60000 #1min rpl_semi_sync_master_wait_for_slave_count = 1 rpl_semi_sync_master_wait_no_slave = 0 # Performance Schema Config # performance-schema-instrument = 'wait/lock/metadata/sql/mdl=ON' performance-schema-instrument = 'memory/%=COUNTED' # Other # innodb_numa_interleave = 1 [mysqldump] quick max_allowed_packet = 2G log-error = /vdb/mysql/logs/dump.log net_buffer_length = 8K [mysqladmin] default-character-set = utf8mb4 socket = /vdb/mysql/tmp/mysql.sock [client] port = 3306 socket = /vdb/mysql/tmp/mysql.sock [mysql] prompt = [\\u@\\h][\\d]:\\_ default-character-set = utf8mb4 no-auto-rehash EOF #这里用的是>>,是追加新内容到/root/.bash_profile,并不是全覆盖。 #然后将以下内容写进去,直至遇到EOF这个标识符,EOF之后的内容就别写进去了 cat >>/root/.bash_profile <<EOF export PATH=/usr/local/mysql/bin:\$PATH EOF 刷新/root/.bash_profile这个文件并使之生效 source /root/.bash_profile #这段命令用于启动 MySQL 服务器,并指定了一些重要的参数: #/usr/local/mysql/bin/mysqld这个是mysql的启动项,相当于windows桌面上的启动图标双击一下就运行该软件了 # --defaults-file=/etc/my.cnf:指定了配置文件的路径。 #--user=mysql:指定以 mysql 用户身份运行服务器。 #--datadir=/vdb/mysql/data:指定数据存储的目录。 #--basedir=/usr/local/mysql:指定 MySQL 的安装目录。 #--initialize-insecure:执行不安全的初始化,这可能意味着设置一些默认的、不太严格的安全选项来快速初始化数据库。 /usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --user=mysql --datadir=/vdb/mysql/data --basedir=/usr/local/mysql --initialize-insecure #把/usr/local/mysql/support-files/mysql.server复制到/etc/init.d/这个文件夹里面去,取名为mysqld #/etc/init.d/ 文件夹通常用于存放系统服务守护进程的启动、停止、重启等操作的脚本。 #也就是说我们这样手动安装的软件mysql,以后也可以用systemctl start mysql这样的命令来启动mysql了比较方便。 # /usr/local/mysql/support-files/mysql.server这个文件其实就是一个程序脚本,你看路径名里都有一个support-files #就是为了方便后续用户可以添加到linux系统里面的快捷启动方式里面(systemctl)。 #但是上面那个/usr/local/mysql/bin/mysqld是mysql的启动文件,这个不是一个脚本,用vim是打不开的,看不到里有啥内容 cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld #赋予/etc/init.d/mysqld具有可执行的权限 chmod 755 /etc/init.d/mysqld #将名为 mysqld 的服务添加到系统的服务管理配置中,方便可以用systemctl来控制启动、停止等状态 chkconfig --add mysqld #设置 MySQL 服务(mysqld)在运行级别 2、3、4、5 中为开机自启动(on)状态。 #运行级别是系统在不同模式下的运行状态,例如,2 通常是多用户模式但没有网络,3 是多用户模式带网络,4 通常未被使用,5 是带有图形界面的多用户模式。 chkconfig --level 2345 mysqld on #将”/usr/local/mysql/lib“写入到/etc/ld.so.conf.d/mysql-x86_64.conf里面去 cat >> /etc/ld.so.conf.d/mysql-x86_64.conf<<EOF /usr/local/mysql/lib EOF #刷新系统的共享缓存,以便程序能够正确找到加载所需的共享 ldconfig #用于检查/proc/vz是否为一个目录,条件为真,就会执行then后面的语句 #ulimit -s unlimited 这条命令用于设置栈大小(stack size)为无限制(unlimited)。 #在某些情况下,如果程序需要较大的栈空间来处理复杂的操作或递归调用,可能会使用这条命令来避免栈空间不足导致的错误。 if [ -d "/proc/vz" ]; then ulimit -s unlimited fi #使用systemctl来启动mysql systemctl start mysqld.service # 创建监控用户,只读用户,复制用户 /usr/local/mysql/bin/mysqladmin -u root password $pwd > /dev/dull #把这两句SQL语句写入到/tmp/mysql_sec_scripts里面去 #这段 SQL 语句的作用是授予用户 'repl_user'@'%' 复制从服务器的权限。 #*.* 表示在所有数据库所有表上授予权限。 #identified by '$rplpwd' 表示设置该用户的密码为 $rplpwd 变量所代表的值。 #flush privileges是刷新授权表; cat > /tmp/mysql_sec_scripts<<EOF grant replication slave on *.* to 'repl_user'@'%' identified by '$rplpwd'; flush privileges; EOF # 使用 /usr/local/mysql/bin/mysql 这个 MySQL 客户端程序, #以 root 用户身份,密码由 $pwd 变量指定,连接到本地主机(localhost)上的 MySQL 服务器 #并执行 /tmp/mysql_sec_scripts 文件中的 SQL 语句 # /usr/local/mysql/bin/mysql刚才上面提到的/usr/local/mysql/bin/mysqld是不太一样的 # mysql是客户端程序,mysqld是主程序,主程序是运行在背后,但是我们人类想看mysql数据库里面的各种数据库具体表格 #就需要用到mysql客户端了,用mysql客户端连接背后的数据库用SQL语句进行相关的增删改查交互操作 /usr/local/mysql/bin/mysql -u root -p$pwd -h localhost </tmp/mysql_sec_scripts #删除/tmp/mysql_sec_scripts rm -f /tmp/mysql_sec_scripts #检查MySQL主程序有没有运行,在运行就赋值mcheck为1,否则为0 mcheck=`systemctl status mysqld.service|grep 'active (running)'|wc -l` #如果/usr/local/mysql/bin/mysql存在,并且/usr/local/mysql/bin/mysqld_safe也存在, #并且/etc/my.cnf也存在,并且MySQL主程序在运行中 #那么输出MySQL 5.7 install completed,以及MySQL网络连接状态信息,不然就输出安装失败Failed #ss -anutlp | grep 3306就是使用 ss 命令查看所有的网络连接状态信息; #(包括套接字的类型、状态、本地地址端口、远程地址端口等),然后通过 grep 3306 筛选出与端口 3306 相关的连接信息。 if [ -s /usr/local/mysql/bin/mysql ] && [ -s /usr/local/mysql/bin/mysqld_safe ] && [ -s /etc/my.cnf ] && [ $mcheck -eq 1 ]; then echo "" echo "MySQL 5.7 install completed" ss -anutlp | grep 3306 else echo "Error: MySQL Install Failed!!" fi
最新发布
08-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值