Mysql MGR搭建

一、架构说明

1.1 架构概述

MGR(单主)+VIP架构是一种分布式数据库架构,其中数据库系统采用单主复制模式,
同时引入虚拟IP(VIP)来提高可用性和可扩展性。
这种架构结合了传统主从复制和虚拟IP技术的优势,为数据库系统提供了高可用、
高性能和可扩展的解决方案。

在这里插入图片描述
每个MGR实例必须部署在单台服务器上
每个复制组各个节点的server端口和内部通信端口必须相同
数据存储引擎仅支持InnoDB
服务器必须支持IPv4网络
数据库隔离级别为READ COMMITTED,不支持可重复读

1.2 环境信息

架构操作系统IPVIP主机名服务版本端口GCT端口磁盘空间内存CPU
MGRredhat7.9192.168.111.30192.168.111.33mgrserver01mysql-8.0.35、python-3.6.8(含py_modes模块)、HAIPMGR-master(VIP服务)33071330750G4G8C
192.168.111.31192.168.111.33mgrserver02mysql-8.0.35、python-3.6.8(含py_modes模块)、HAIPMGR-master(VIP服务)33071330750G4G8C
192.168.111.32192.168.111.33mgrserver03mysql-8.0.35、python-3.6.8(含py_modes模块)、HAIPMGR-master(VIP服务)33071330750G4G8C

1.3 账号信息

用户名用途
root@localhost管理员本地账号
admin管理员远程账号
vip_monitor监控VIP服务
replMGR节点之间数据同步
backup数据备份
i_dbm数据库管理平台
sess_monitorsesswait监控

备注:本次资源测试使用,具体资源根据业务需要调整。

二、环境准备

备注:三个节点都操作。

2.1 划分磁盘:/data

[root@mgrserver01 ~]# fdisk /dev/sdb
Welcome to fdisk (util-linux 2.23.2).Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.Device does not contain a recognized partition table
Building a new DOS disklabel with disk identifier 0x1549bfc0.Command (m for help): n
Partition type:p   primary (0 primary, 0 extended, 4 free)e   extended
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-419430399, default 2048):
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-419430399, default 419430399):
Using default value 419430399
Partition 1 of type Linux and of size 200 GiB is setCommand (m for help): w
The partition table has been altered!Command (m for help): t
Selected partition 1
Hex code (type L to list all codes): 8e
Changed type of partition 'Linux' to 'Linux LVM'Command (m for help): w
The partition table has been altered!Calling ioctl() to re-read partition table.
Syncing disks.磁盘更新
# partprobe /dev/sdbLVM操作
创建PV
# pvcreate /dev/sdb1创建VG
# vgcreate vg_data /dev/sdb1创建LV
# lvcreate -L 299G -n lv_data vg_data格式化文件系统
# mkfs.xfs /dev/mapper/vg_data-lv_data挂载文件系统
# mount /dev/mapper/vg_data-lv_data /data设置开机自动启动
# vi /etc/fstab
/dev/mapper/vg_data-lv_data /data                   xfs     defaults        0 0

2.2 在data目录下创建数据库相应目录

mkdir -p /data/soft
mkdir -p /data/mysql8.0.35/3307
mkdir -p /data/mysql8.0.35/install
mkdir -p /data/mysql8.0.35/tools
mkdir -p /data/mysql8.0.35/tools/HAIPMGR
cd /data/mysql8.0.35/3307
mkdir {data,binlog,logs,conf,relaylog,tmp}

2.3 软件包下载

##MySQL软件包
[root@mgrserver01 soft]# cd /data/soft
[root@mgrserver01 soft]# wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.35-linux-glibc2.12-x86_64.tar.xz
--2024-01-25 16:59:42--  https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.35-linux-glibc2.12-x86_64.tar.xz
正在解析主机 downloads.mysql.com (downloads.mysql.com)... 23.64.178.143, 2600:140b:2:5ad::2e31
正在连接 downloads.mysql.com (downloads.mysql.com)|23.64.178.143|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 302 Moved Temporarily
位置:https://cdn.mysql.com/archives/mysql-8.0/mysql-8.0.35-linux-glibc2.12-x86_64.tar.xz [跟随至新的 URL]
--2024-01-25 16:59:43--  https://cdn.mysql.com/archives/mysql-8.0/mysql-8.0.35-linux-glibc2.12-x86_64.tar.xz
正在解析主机 cdn.mysql.com (cdn.mysql.com)... 96.7.189.131, 2600:140b:2:593::1d68, 2600:140b:2:58f::1d68
正在连接 cdn.mysql.com (cdn.mysql.com)|96.7.189.131|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:617552732 (589M) [text/plain]
正在保存至: “mysql-8.0.35-linux-glibc2.12-x86_64.tar.xz”100%[====================================================================================================================================================================>] 617,552,732 4.28MB/s 用时 2m 17s2024-01-25 17:02:02 (4.30 MB/s) - 已保存 “mysql-8.0.35-linux-glibc2.12-x86_64.tar.xz” [617552732/617552732])##python-3.6.8.tar.xz
https://www.python.org/downloads/release/python-368/##HAIPMGR-master
https://github.com/gaopengcarl/HAIPMGR

2.4 设置/etc/security/limits.conf、 /etc/sysctl.conf、/etc/hosts,禁用selinux、关闭防火墙

 [root@mgrserver01 ~]# cat /etc/security/limits.conf
...
mysql   soft   nproc   16384
mysql   hard   nproc  16384
mysql   soft   nofile   65536
mysql   hard   nofile   65536
mysql  soft  stack   1024
mysql  hard  stack   1024[root@mgrserver01 ~]# cat /etc/sysctl.conf....
kernel.sysrq = 1
#basic setting
#net.ipv6.conf.all.disable_ipv6 = 1
#net.ipv6.conf.default.disable_ipv6 = 1
kernel.pid_max = 524288
fs.file-max = 6815744
fs.aio-max-nr = 1048576
kernel.sem = 500 256000 250 8192
net.ipv4.ip_local_port_range = 9000 65000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
#net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1800
net.ipv4.tcp_retries2 = 5
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 4096
net.core.netdev_max_backlog = 3000
vm.swappiness = 0
net.ipv6.conf.eth0.disable_ipv6 = 1
vm.swappiness = 1[root@mgrserver01 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.111.30  mgrserver01
192.168.111.31  mgrserver02
192.168.111.31  mgrserver03getenforce 
setenforce 0
sed -i s/SELINUX=enforcing/SELINUX=disabled/ /etc/selinux/configsystemctl stop firewalld
systemctl disable firewalld

2.5 创建mysql用户

groupadd mysql
useradd -g mysql -d /home/mysql mysql
echo "777dba_test" | passwd --stdin mysql
chown -R mysql:mysql /data
chmod -R 775 /data

三、开始安装Mysql数据库

注意:三节点都安装

3.1 依赖包安装

## mysql 依赖
[root@mgrserver01 soft]# yum -y install make cmake libaio numactl.x86_64## python 依赖
[root@mgrserver01 soft]# yum -y install gcc openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel zlib*                     

3.2 安装jemalloc、python-3.6.8

我们在日常运维工作中,发现测试环境的mysql服务端偶尔出现内存使用率非常高的情况。
系统中查看mysqld已用内存量远远超出了innodb_buffer_pool_size配置的数倍之多。
另外在问题发生时,查看mysqld中的连接数仅500多,且大部分连接为sleep状态。
也就是说,虽然系统分配了大量的内存给mysqld,但其实际上并不需要用到那么多内存,内存可能产生了碎片,内存没有及时GC。
所以,尝试将mysqld的内存管理器改为使用 jemalloc 来提升内存效能。
jemalloc 是由Jason Evans 在FreeBSD 项目中引入的新一代内存分配器。
它是一个通用的malloc 实现,侧重于减少内存碎片和提升高并发场景下内存的分配效率,其目标是能够替代malloc。
redis目前默认使用的内存管理器就是 jemalloc 。

[root@mgrserver01]# cd /data/soft
[root@mgrserver01 soft]# wget https://github.com/jemalloc/jemalloc/archive/refs/tags/5.3.0.tar.gz
[root@mgrserver01 soft]# tar -zvxf jemalloc-5.3.0.tar.gz -C /data/soft/
[root@mgrserver01 soft]# cd jemalloc-5.3.0
[root@mgrserver01 jemalloc-5.3.0]# ls
autogen.sh  config.stamp     COPYING       jemalloc.pc     Makefile.in   src
bin         config.stamp.in  doc           jemalloc.pc.in  msvc          test
build-aux   config.status    doc_internal  lib             README        TUNING.md
ChangeLog   configure        include       m4              run_tests.sh  VERSION
config.log  configure.ac     INSTALL.md    Makefile        scripts
[root@mgrserver01 jemalloc-5.3.0]# ./autogen.sh
autoconf
./autogen.sh:5: autoconf: 未找到命令
Error 0 in autoconf
[root@mgrserver01 jemalloc-5.3.0]# yum -y install autoconf
[root@mgrserver01 jemalloc-5.3.0]# ./configure --prefix=/usr/local/jemalloc --libdir=/usr/ocal/lib
[root@mgrserver01 jemalloc-5.3.0]# make && make install
[root@mgrserver01 jemalloc-5.3.0]# echo "export LD_PRELOAD=/usr/local/lib/libjemalloc.so" >>/root/.bash_profile[root@mgrserver01 soft]# tar -Jxvf Python-3.6.8.tar.xz
[root@mgrserver01 soft]# cd Python-3.6.8
[root@mgrserver01 Python-3.6.8]# ./configure prefix=/usr/local/python3
[root@mgrserver01 Python-3.6.8]# make && make install 
[root@mgrserver01 Python-3.6.8]# ln -fs /usr/local/python3/bin/python3 /usr/bin/python3 
[root@mgrserver01 Python-3.6.8]# ln -fs /usr/local/python3/bin/pip3 /usr/bin/pip3 

3.3 安装常用模块psutil、pymysql(vip服务依赖包)

[root@mgrserver01 py_modes_2]# pip3 install psutil==5.7.0
Collecting psutil==5.7.0Downloading https://files.pythonhosted.org/packages/c4/b8/3512f0e93e0db23a71d82485ba256071ebef99b227351f0f5540f744af41/psutil-5.7.0.tar.gz (449kB)100% |████████████████████████████████| 450kB 1.1MB/s
Installing collected packages: psutilRunning setup.py install for psutil ... done
Successfully installed psutil-5.7.0[root@mgrserver01 py_modes_2]# pip3 install PyMySQL==0.9.3
Collecting PyMySQL==0.9.3Downloading https://files.pythonhosted.org/packages/ed/39/15045ae46f2a123019aa968dfcba0396c161c20f855f11dea6796bcaae95/PyMySQL-0.9.3-py2.py3-none-any.whl (47kB)100% |████████████████████████████████| 51kB 179kB/s
Installing collected packages: PyMySQL
Successfully installed PyMySQL-0.9.3[root@mgrserver01 py_modes_2]# pip3 install cryptography==3.3.1
Collecting cryptography==3.3.1Downloading https://files.pythonhosted.org/packages/7c/b6/1f3dd48a22fcd56f19e6cfa95f74ff0a64b046306354e1bd2b936b7c9ab4/cryptography-3.3.1-cp36-abi3-manylinux1_x86_64.whl (2.7MB)100% |████████████████████████████████| 2.7MB 40kB/s
Collecting six>=1.4.1 (from cryptography==3.3.1)Downloading https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl
Collecting cffi>=1.12 (from cryptography==3.3.1)Downloading https://files.pythonhosted.org/packages/3a/12/d6066828014b9ccb2bbb8e1d9dc28872d20669b65aeb4a86806a0757813f/cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl (402kB)100% |████████████████████████████████| 409kB 32kB/s
Collecting pycparser (from cffi>=1.12->cryptography==3.3.1)Downloading https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl (118kB)100% |████████████████████████████████| 122kB 43kB/s
Installing collected packages: six, pycparser, cffi, cryptography
Successfully installed cffi-1.15.1 cryptography-3.3.1 pycparser-2.21 six-1.16.0

3.4 解压软件包

[root@mgrserver01 soft]# tar -Jxvf /data/soft/mysql-8.0.35-linux-glibc2.12-x86_64.tar.xz -C /data/mysql8.0.35/install/

[root@mgrserver01 soft]# cd /data/mysql8.0.35/install/
[root@mgrserver01 install]# ll
总用量 0
drwxr-xr-x. 9 root root 129 126 16:50 mysql-8.0.35-linux-glibc2.12-x86_64
[root@mgrserver01 install]# mv mysql-8.0.35-linux-glibc2.12-x86_64/ mysql-8.0.35
[root@mgrserver01 install]# ls
mysql-8.0.35

3.5 my.cnf文件配置

8.0版本

注意:server-id 三个节点不一致,节点之间需要修改的参数。

##节点一
server-id = 303307											#IP主机位 + 端口号
innodb_buffer_pool_size = 4G									#物理内存的一半
innodb_thread_concurrency = 4								#CPU核数
innodb_write_io_threads = 4									#CPU核数
innodb_read_io_threads = 4									#CPU核数
loose-group_replication_local_address = 192.168.111.30:13307	#物理ip:GCT端口		
loose-group_replication_group_seeds = "192.168.111.30:13307,192.168.111.31:13307,192.168.111.32:13307"
loose-group_replication_ip_whitelist = "192.168.111.0/24"			#白名单网段##节点三
server-id = 313307											#IP主机位 + 端口号
innodb_buffer_pool_size = 4G									#物理内存的一半
innodb_thread_concurrency = 4								#CPU核数
innodb_write_io_threads = 4									#CPU核数
innodb_read_io_threads = 4									#CPU核数
loose-group_replication_local_address = 192.168.111.31:13307	#物理ip:GCT端口		
loose-group_replication_group_seeds = "192.168.111.30:13307,192.168.111.31:13307,192.168.111.32:13307"
loose-group_replication_ip_whitelist = "192.168.111.0/24"			#白名单网段##节点三
server-id = 323307											#IP主机位 + 端口号
innodb_buffer_pool_size = 4G									#物理内存的一半
innodb_thread_concurrency = 4								#CPU核数
innodb_write_io_threads = 4									#CPU核数
innodb_read_io_threads = 4									#CPU核数
loose-group_replication_local_address = 192.168.111.32:13307	#物理ip:GCT端口		
loose-group_replication_group_seeds = "192.168.111.30:13307,192.168.111.31:13307,192.168.111.32:13307"
loose-group_replication_ip_whitelist = "192.168.111.0/24"			#白名单网段

3.6 搭建基础库

3.6.1 初始化mysql数据库

/data/mysql8.0.35/install/mysql-8.0.35/bin/mysqld --defaults-file=/data/mysql8.0.35/3307/conf/my.cnf --initialize --user=mysql --log_error_verbosity --explicit_defaults_for_timestamp

3.6.2 命令执行完毕输出无误,启动数据库

nohup /data/mysql8.0.35/install/mysql-8.0.35/bin/mysqld_safe --defaults-file=/data/mysql8.0.35/3307/conf/my.cnf &

3.6.3 登录数据库

登录密码在error.log里

 grep password /data/mysql8.0.35/3307/logs/error.log
2024-01-31T13:57:14.481383+08:00 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: r2_#y)mg&OV2
/data/mysql8.0.35/install/mysql-8.0.35/bin/mysql --defaults-file=/data/mysql8.0.35/3307/conf/my.cnf -uroot -p

3.6.4 修改Mysql的root用户密码、创建管理员远程账号

alter user 'root'@'localhost' identified by "r2_#y)mg&OV3";
create user 'admin'@'%' identified by 'r2_#y)mg&OV3';
grant all on *.* to admin;
reset master;  ##清除二进制日志,按需执行。
flush privileges;

3.6.5 数据库关闭命令

/data/mysql8.0.35/install/mysql-8.0.35/bin/mysqladmin --defaults-file=/data/mysql8.0.35/3307/conf/my.cnf -uroot -p shutdown

四、MGR配置

4.1 创建复制组

4.1.1 在mgrserver01节点执行

reset master;	#清除二进制日志
SET SQL_LOG_BIN=0; 	# 禁止当前会话中的SQL语句被写入二进制日志
CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password by 'r2_#y)mg&OV3';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';SET SQL_LOG_BIN=1;	#启用当前会话中的SQL语句写入二进制日志
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='r2_#y)mg&OV3' FOR CHANNEL 'group_replication_recovery';INSTALL PLUGIN group_replication SONAME 'group_replication.so';
show plugins;SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

SET GLOBAL group_replication_bootstrap_group=ON; #启动 MySQL 组复制的命令,它将允许一个节点在启动时作为新的组复制的启动节点。在执行这个命令之前,请确保你已经正确地配置了组复制的参数和设置。

START GROUP_REPLICATION; 是用于启动 MySQL 组复制的命令。这个命令会使启动节点加入到现有的组内,并与其他节点进行通信以实现数据复制和同步。

SET GLOBAL group_replication_bootstrap_group=OFF; 命令被用来关闭启动节点的引导模式,这意味着启动节点将正式加入组复制,并且不再作为新的组启动节点。使用这个命令之前,请确保启动节点已成功地加入了组内。

4.1.2 查看状态

SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 2c7bd415-c173-11ee-a4d1-000c293f5404 | mgrserver01 |        3307 | ONLINE       | PRIMARY     | 8.0.35         | XCom                       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
1 row in set (0.00 sec)# 添加一些数据
CREATE DATABASE test;
USE test;
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
INSERT INTO t1 VALUES (1, 'Luis');
SELECT * FROM t1;

4.1.3 在mgrserver02、mgrserver03节点执行

reset master;	#清除二进制日志
SET SQL_LOG_BIN=0;	# 禁止当前会话中的SQL语句被写入二进制日志
CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password by 'r2_#y)mg&OV3';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';SET SQL_LOG_BIN=1;	#启用当前会话中的SQL语句写入二进制日志
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='r2_#y)mg&OV3' FOR CHANNEL 'group_replication_recovery';INSTALL PLUGIN group_replication SONAME 'group_replication.so';
START GROUP_REPLICATION;

4.1.4 查看状态

SELECT * FROM performance_schema.replication_group_members;
root@localhost: 18:37:  [(none)]> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 2c7bd415-c173-11ee-a4d1-000c293f5404 | mgrserver01 |        3307 | ONLINE       | PRIMARY     | 8.0.35         | XCom                       |
| group_replication_applier | 33e4f69b-c173-11ee-9a5d-000c29f132d9 | mgrserver03 |        3307 | ONLINE       | SECONDARY   | 8.0.35         | XCom                       |
| group_replication_applier | 3fc65174-c173-11ee-94aa-000c29c1073f | mgrserver02 |        3307 | ONLINE       | SECONDARY   | 8.0.35         | XCom                       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
3 rows in set (0.00 sec)

4.1.5 查看mgr_node01的数据是否同步

SHOW DATABASES LIKE 'test';
SELECT * FROM test.t1;
root@localhost: 11:25:  [(none)]> SHOW DATABASES LIKE 'test';
+-----------------+
| Database (test) |
+-----------------+
| test            |
+-----------------+
1 row in set (0.00 sec)root@localhost: 11:25:  [(none)]> SELECT * FROM test.t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | Luis |
+----+------+
1 row in set (0.00 sec)

4.2 查看MGR集群状态

SELECT MEMBER_ID,MEMBER_HOST,MEMBER_PORT,MEMBER_STATE,IF(global_status.VARIABLE_NAME IS NOT NULL,'PRIMARY','SECONDARY') AS MEMBER_ROLE
FROMperformance_schema.replication_group_membersLEFT JOINperformance_schema.global_status ON global_status.VARIABLE_NAME = 'group_replication_primary_member'AND global_status.VARIABLE_VALUE = replication_group_members.MEMBER_ID;root@localhost: 11:23:  [test]> SELECT->     MEMBER_ID,->     MEMBER_HOST,->     MEMBER_PORT,->     MEMBER_STATE,->     IF(global_status.VARIABLE_NAME IS NOT NULL,->         'PRIMARY',->         'SECONDARY') AS MEMBER_ROLE-> FROM->     performance_schema.replication_group_members->         LEFT JOIN->     performance_schema.global_status ON global_status.VARIABLE_NAME = 'group_replication_primary_member'->         AND global_status.VARIABLE_VALUE = replication_group_members.MEMBER_ID;
+--------------------------------------+-------------+-------------+--------------+-------------+
| MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE |
+--------------------------------------+-------------+-------------+--------------+-------------+
| 2c7bd415-c173-11ee-a4d1-000c293f5404 | mgrserver01 |        3307 | ONLINE       | PRIMARY     |
| 33e4f69b-c173-11ee-9a5d-000c29f132d9 | mgrserver03 |        3307 | ONLINE       | SECONDARY   |
| 3fc65174-c173-11ee-94aa-000c29c1073f | mgrserver02 |        3307 | ONLINE       | SECONDARY   |
+--------------------------------------+-------------+-------------+--------------+-------------+
3 rows in set (0.00 sec)

4.3 创建监控视图

在主库执行即可。

4.3.1 mysql 5.7 使用以下命令创建

USE sys;
DELIMITER $$
CREATE FUNCTION IFZERO(a INT, b INT)
RETURNS INT
DETERMINISTIC
RETURN IF(a = 0, b, a)$$CREATE FUNCTION LOCATE2(needle TEXT(10000), haystack TEXT(10000), offset INT)
RETURNS INT
DETERMINISTIC
RETURN IFZERO(LOCATE(needle, haystack, offset), LENGTH(haystack) + 1)$$CREATE FUNCTION GTID_NORMALIZE(g TEXT(10000))
RETURNS TEXT(10000)
DETERMINISTIC
RETURN GTID_SUBTRACT(g, '')$$CREATE FUNCTION GTID_COUNT(gtid_set TEXT(10000))
RETURNS INT
DETERMINISTIC
BEGINDECLARE result BIGINT DEFAULT 0;DECLARE colon_pos INT;DECLARE next_dash_pos INT;DECLARE next_colon_pos INT;DECLARE next_comma_pos INT;SET gtid_set = GTID_NORMALIZE(gtid_set);SET colon_pos = LOCATE2(':', gtid_set, 1);WHILE colon_pos != LENGTH(gtid_set) + 1 DOSET next_dash_pos = LOCATE2('-', gtid_set, colon_pos + 1);SET next_colon_pos = LOCATE2(':', gtid_set, colon_pos + 1);SET next_comma_pos = LOCATE2(',', gtid_set, colon_pos + 1);IF next_dash_pos < next_colon_pos AND next_dash_pos < next_comma_pos THENSET result = result +SUBSTR(gtid_set, next_dash_pos + 1,LEAST(next_colon_pos, next_comma_pos) - (next_dash_pos + 1)) -SUBSTR(gtid_set, colon_pos + 1, next_dash_pos - (colon_pos + 1)) + 1;ELSESET result = result + 1;END IF;SET colon_pos = next_colon_pos;END WHILE;RETURN result;
END$$CREATE FUNCTION gr_applier_queue_length()
RETURNS INT
DETERMINISTIC
BEGINRETURN (SELECT sys.gtid_count( GTID_SUBTRACT( (SELECT
Received_transaction_set FROM performance_schema.replication_connection_status
WHERE Channel_name = 'group_replication_applier' ), (SELECT
@@global.GTID_EXECUTED) )));
END$$CREATE FUNCTION gr_member_in_primary_partition()
RETURNS VARCHAR(3)
DETERMINISTIC
BEGINRETURN (SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROM
performance_schema.replication_group_members WHERE MEMBER_STATE != 'ONLINE') >=
((SELECT COUNT(*) FROM performance_schema.replication_group_members)/2) = 0),
'YES', 'NO' ) FROM performance_schema.replication_group_members JOIN
performance_schema.replication_group_member_stats USING(member_id));
END$$CREATE VIEW gr_member_routing_candidate_status AS SELECT
sys.gr_member_in_primary_partition() as viable_candidate,
IF( (SELECT (SELECT GROUP_CONCAT(variable_value) FROM
performance_schema.global_variables WHERE variable_name IN ('read_only',
'super_read_only')) != 'OFF,OFF'), 'YES', 'NO') as read_only,
sys.gr_applier_queue_length() as transactions_behind, Count_Transactions_in_queue as 'transactions_to_cert' from performance_schema.replication_group_member_stats;$$
DELIMITER ;

4.3.2 mysql 8.0 使用以下命令创建

USE sys;
DELIMITER $$CREATE FUNCTION my_id() RETURNS TEXT(36) DETERMINISTIC NO SQL RETURN (SELECT @@global.server_uuid as my_id);$$CREATE FUNCTION gr_member_in_primary_partition()RETURNS VARCHAR(3)DETERMINISTICBEGINRETURN (SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROMperformance_schema.replication_group_members WHERE MEMBER_STATE NOT IN ('ONLINE', 'RECOVERING')) >=((SELECT COUNT(*) FROM performance_schema.replication_group_members)/2) = 0),'YES', 'NO' ) FROM performance_schema.replication_group_members JOINperformance_schema.replication_group_member_stats USING(member_id) where member_id=my_id());
END$$CREATE VIEW gr_member_routing_candidate_status AS SELECT
sys.gr_member_in_primary_partition() as viable_candidate,
IF( (SELECT (SELECT GROUP_CONCAT(variable_value) FROM
performance_schema.global_variables WHERE variable_name IN ('read_only',
'super_read_only')) != 'OFF,OFF'), 'YES', 'NO') as read_only,
Count_Transactions_Remote_In_Applier_Queue as transactions_behind, Count_Transactions_in_queue as 'transactions_to_cert' 
from performance_schema.replication_group_member_stats where member_id=my_id();$$DELIMITER ;

4.4 安装VIP服务

三个节点都部署

 unzip /data/soft/HAIPMGR-master.zip -d /data/mysql8.0.35/tools/HAIPMGR/chown -R mysql.mysql /data/mysql8.0.35/tools/HAIPMGR/

4.4.1 修改配置文件

vi /data/mysql8.0.35/tools/HAIPMGR/HAIPMGR-master/parameter.py
par_var={"vip":"192.168.111.33","cluser_ip":{"192.168.111.30":"eth0","192.168.111.31":"eth0","192.168.111.32":"eth0"},"ip_gateway":"192.168.111.2","mysql_port":"3307","inter_port":"13307","passwd":"Monitor@dba1","user":"vip_monitor","platfrom":"linux","sleeptime":7,"install_path":"/data/mysql8.0.35/tools/HAIPMGR/HAIPMGR-master/"}

4.4.2 创建监控用户

在主库执行以下命令

create user vip_monitor@'%' IDENTIFIED WITH mysql_native_password by 'Monitor@dba1';
grant select on performance_schema.* to vip_monitor@'%';

4.4.3 启动VIP服务

nohup python3 /data/mysql8.0.35/tools/HAIPMGR/HAIPMGR-master/HAIPMGR.py &

4.4.4 检查vip是否绑定在primary节点

ip addr
[root@mgrserver01 HAIPMGR-master]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope hostvalid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:0c:29:3f:54:04 brd ff:ff:ff:ff:ff:ffinet 192.168.111.30/24 brd 192.168.111.255 scope global noprefixroute eth0valid_lft forever preferred_lft foreverinet 192.168.111.33/24 brd 192.168.111.255 scope global secondary eth0:3307valid_lft forever preferred_lft forever

4.4.5 查看日志输出

tail -f /data/mysql8.0.35/tools/HAIPMGR/HAIPMGR-master/tail -f HAIPMGR3307.log
***********************************HAIPMGR:One loop begin:*********************************** file:HAIPMGR.py line:75 fun:main
2024-02-06 16:24:22,153 - logger.Fun_log_create - INFO - all_vip.check_stat:[STAGE1]------------------------------------- file:all_vip.py line:72 fun:check_stat
2024-02-06 16:24:22,154 - logger.Fun_log_create - INFO - fun:getl_ip_isincluter:local ip addr is [('192.168.111.30', 'eth0'), ('192.168.111.33', 'eth0:3307')] file:tool.py line:139 fun:getl_ip_isincluter
2024-02-06 16:24:22,155 - logger.Fun_log_create - INFO - fun:getl_ip_isincluter:local ip 192.168.111.30 addr is in cluster dict_items([('192.168.111.30', 'eth0'), ('192.168.111.31', 'eth0'), ('192.168.111.32', 'eth0')]) file:tool.py line:145 fun:getl_ip_isincluter
2024-02-06 16:24:22,155 - logger.Fun_log_create - INFO - all_vip.check_stat:[STAGE1] check scuess: local ip 192.168.111.30             is in cluster {'192.168.111.30': 'eth0', '192.168.111.31': 'eth0', '192.168.111.32': 'eth0'} file:all_vip.py line:76 fun:check_stat
2024-02-06 16:24:22,155 - logger.Fun_log_create - INFO - all_vip.check_stat:[STAGE2]------------------------------------- file:all_vip.py line:87 fun:check_stat
2024-02-06 16:24:22,158 - logger.Fun_log_create - INFO - fun:is_mysqld_up:192.168.111.30 3307 vip_monitor connect mysqld sucess file:tool.py line:187 fun:is_mysqld_up
2024-02-06 16:24:22,159 - logger.Fun_log_create - INFO - all_vip.check_stat:[STAGE2] mysqld ip:192.168.111.30 port:3307 connect scuess file:all_vip.py line:91 fun:check_stat
2024-02-06 16:24:22,159 - logger.Fun_log_create - INFO - all_vip.check_stat:[STAGE3]------------------------------------- file:all_vip.py line:103 fun:check_stat
2024-02-06 16:24:22,159 - logger.Fun_log_create - INFO - fun:is_mgrok_master: connect mysqld sucess file:tool.py line:240 fun:is_mgrok_master
2024-02-06 16:24:22,160 - logger.Fun_log_create - INFO - fun:is_mgrok_master: current host mgrserver01 is MGR online node mgrserver01 file:tool.py line:283 fun:is_mgrok_master
2024-02-06 16:24:22,160 - logger.Fun_log_create - INFO - fun:is_mgrok_master: current host mgrserver01 is MGR master node mgrserver01 file:tool.py line:291 fun:is_mgrok_master
2024-02-06 16:24:22,160 - logger.Fun_log_create - INFO - all_vip.check_stat:[STAGE3] check scuess: mysqld ip:192.168.111.30 is master             node file:all_vip.py line:107 fun:check_stat
2024-02-06 16:24:22,160 - logger.Fun_log_create - INFO - all_vip.check_stat:[STAGE4]------------------------------------- file:all_vip.py line:120 fun:check_stat
2024-02-06 16:24:24,178 - logger.Fun_log_create - INFO - fun:is_connect_ip: ping reslut is (0, 'PING 192.168.111.2 (192.168.111.2) 56(84) bytes of data.\n64 bytes from 192.168.111.2: icmp_seq=1 ttl=128 time=0.137 ms\n64 bytes from 192.168.111.2: icmp_seq=2 ttl=128 time=0.491 ms\n64 bytes from 192.168.111.2: icmp_seq=3 ttl=128 time=1.44 ms\n\n--- 192.168.111.2 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2011ms\nrtt min/avg/max/mdev = 0.137/0.692/1.448/0.553 ms') file:tool.py line:63 fun:is_connect_ip
2024-02-06 16:24:24,179 - logger.Fun_log_create - INFO - all_vip.check_stat:[STAGE4] check sucess: gateway 192.168.111.2 is connect sucess file:all_vip.py line:124 fun:check_stat
2024-02-06 16:24:24,179 - logger.Fun_log_create - INFO - all_vip.check_vip:[STAGE5]------------------------------------- file:all_vip.py line:139 fun:check_vip
2024-02-06 16:24:24,179 - logger.Fun_log_create - INFO - fun:is_vip_local: check vip 192.168.111.33 is hit local ip ('192.168.111.33', 'eth0:3307') file:tool.py line:322 fun:is_vip_local
2024-02-06 16:24:24,180 - logger.Fun_log_create - INFO - all_vip.check_vip:Vip 192.168.111.33 must start on this node file:all_vip.py line:145 fun:check_vip
2024-02-06 16:24:24,180 - logger.Fun_log_create - INFO - all_vip.check_vip:Vip 192.168.111.33 is on this node keep it file:all_vip.py line:148 fun:check_vip
2024-02-06 16:24:24,180 - logger.Fun_log_create - INFO - all_vip.oper_vip:[STAGE6]------------------------------------- file:all_vip.py line:194 fun:oper_vip
2024-02-06 16:24:24,180 - logger.Fun_log_create - INFO - all_vip.oper_vip:Vip opertion keep on  file:all_vip.py line:209 fun:oper_vip

#查看进程

ps aux|grep -i mgr
[root@mgrserver01 HAIPMGR-master]# ps aux | grep -i mgr
postfix    1231  0.0  0.1  89984  4284 ?        S    01:09   0:00 qmgr -l -t unix -u
root       4860  0.1  0.4 223584 17052 pts/0    S+   15:37   0:03 python3 HAIPMGR.py
root       5253  0.0  0.0 128172  1236 pts/1    S+   16:24   0:00 tail -f HAIPMGR3307.log
root       5262  0.0  0.0 130752  1568 pts/2    S+   16:25   0:00 grep --color=auto -i mgr

4.5 安装sesswait

4.5.1 创建账号、目录

在主库创建sesswait监控账号
CREATE USER ‘sess_monitor’@‘%’ IDENTIFIED BY ‘Session_monit77’;
GRANT PROCESS ON . TO sess_monitor@‘%’;
GRANT select on sys.* to sess_monitor@‘%’;

4.5.2配置安全登录认证

在三个节点上部署sesswait

/data/mysql8.0.35/install/mysql-8.0.35/bin/mysql_config_editor set -S /data/mysql8.0.35/3307/tmp/mysql.sock --login-path=sesswait -usess_monitor -p

4.5.3 查看

[root@mgrserver01 HAIPMGR-master]# /data/mysql8.0.35/install/mysql-8.0.35/bin/mysql_config_editor print --all
[sesswait]
user = "sess_monitor"
password = *****
socket = "/data/mysql8.0.35/3307/tmp/mysql.sock"

4.5.4 sess_monitor用户登录测试

/data/mysql8.0.35/install/mysql-8.0.35/bin/mysql --login-path=sesswait

4.5.5 创建监控脚本

自动化脚本统一放到/home/mysql/DBmanagerment下

mkdir -p /home/mysql/DBmanagerment/sesswait
chown -R mysql.mysql /home/mysql/DBmanagerment/
cat /home/mysql/DBmanagerment/sesswait.sh
#!/bin/bashUSEROPTIONS="--login-path=sesswait"
MYSQL="/data/mysql8.0.35/install/mysql-8.0.35/bin/mysql"
i=0
export LANG="en_US.UTF-8"while (($i<2881))
do
echo "#############当前时间#############"
$MYSQL $USEROPTIONS -e "select now();"
echo "#############活动会话数#############"
$MYSQL $USEROPTIONS -e "select user,count(*) from information_schema.PROCESSLIST where COMMAND not in ('Sleep') group by user;"
echo "#############连接会话数#############"
$MYSQL $USEROPTIONS -e "select user,count(*) from information_schema.PROCESSLIST  group by user;"
echo "#############正在锁的事务#############"
$MYSQL $USEROPTIONS -e "SELECT lock_id,lock_trx_id,lock_mode,lock_table,lock_index,lock_data FROM INFORMATION_SCHEMA.INNODB_LOCKS;"
$MYSQL $USEROPTIONS -e "SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;"echo "#############运行时间大于5秒的事务#############"
$MYSQL $USEROPTIONS -e "select ID,USER,COMMAND,TIME,STATE,INFO from information_schema.PROCESSLIST where TIME>5 and COMMAND not in ('Sleep');"
echo "#############运行时间大于60秒的事务#############"
$MYSQL $USEROPTIONS -e "select ID,USER,COMMAND,TIME,STATE,INFO from information_schema.PROCESSLIST where TIME>60 and COMMAND not in ('Sleep');"
echo "#############运行时间大于600秒的事务#############"
$MYSQL $USEROPTIONS -e "select ID,USER,COMMAND,TIME,STATE,INFO from information_schema.PROCESSLIST where TIME>600 and COMMAND not in ('Sleep');"echo "#############运行中的SQL#############"
$MYSQL $USEROPTIONS -e "select ID,USER,COMMAND,TIME,STATE,INFO from information_schema.PROCESSLIST where COMMAND not in ('Sleep');"
echo "#############延迟监控#############"
$MYSQL $USEROPTIONS -e "select * from sys.gr_member_routing_candidate_status"
echo "############################Will Sleep 30 Seconds#############################"
let i++
sleep 30
doneecho "###################################!Clean 10 day!####################################"
find /home/mysql/DBmanagerment/sesswait/sesswait_* -type f  -mtime +10 -exec rm {} \;

4.5.6 添加计划任务

每天晚上00:01点生成新文件

crontab -e
0 4 * * * sh /home/mysql/DBmanagerment/sess.shvi /home/mysql/DBmanagerment/sess.sh
sh /home/mysql/DBmanagerment/sesswait.sh > /home/mysql/DBmanagerment/sesswait/sesswait_`date +%F`.log 2>&1 &

赋予权限

chown -R mysql.mysql /home/mysql/DBmanagerment/
chmod -R 755 /home/mysql/DBmanagerment/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/671922.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十分钟GIS——geoserver+postgis+udig从零开始发布地图服务

1数据库部署 1.1PostgreSql安装 下载到安装文件后&#xff08;postgresql-9.2.19-1-windows-x64.exe&#xff09;&#xff0c;双击安装。 指定安装目录&#xff0c;如下图所示 指定数据库文件存放目录位置&#xff0c;如下图所示 指定数据库访问管理员密码&#xff0c;如下图所…

uniapp canvas游标卡尺效果

效果 根据公司业务仿照写的效果。原项目从微信小程序转uniapp,未测试该效果在android端效果。 uniapp直接使用canvas不可做子组件,否则无效果显示,其次显示时要考虑页面渲染超时的问题。 如效果所见,可以设置取值精度。 gitee地址:project_practice: 项目练习 - Gitee.…

用python编写爬虫,爬取房产信息

题目 报告要求 工程报告链接放在这里 https://download.csdn.net/download/Samature/88816284使用 1.安装jupyter notebook 2.用jupyter notebook打开工程里的ipynb文件&#xff0c;再run all就行 注意事项 可能遇到的bug 暂无&#xff0c;有的话私信我

Java 中使用多线程的方式有哪些

在 Java 中&#xff0c;使用多线程主要有以下几种方式&#xff1a; 实现 Runnable 接口&#xff1a;这是实现多线程的一种方式。一个类实现 Runnable 接口后&#xff0c;就意味着它是一个线程的执行对象。当一个线程启动时&#xff0c;需要一个与之关联的 Runnable 对象。 publ…

Netflix Mac(奈飞mac客户端) v2.13.0激活版

Clicker for Netflix Mac版是一款适用于Mac的最佳独立Netflix播放器&#xff0c;具有直接从从Dock启动Netflix&#xff0c;从触摸栏控制Netflix&#xff0c;支持画中画等多种功能&#xff0c;让你拥有更好的观看体验。 软件特色 •直接从Dock启动Netflix •从触摸栏控制Netflix…

每日一练:LeeCode-112、路径总和【二叉树+DFS+回溯】

本文是力扣LeeCode-112、路径总和 学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐LeeCode。 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有…

HarmonyOS4.0——IPC与RPC通信

基本概念 IPC&#xff08;Inter-Process Communication&#xff09;与RPC&#xff08;Remote Procedure Call&#xff09;用于实现跨进程通信&#xff0c;不同的是前者使用Binder驱动&#xff0c;用于设备内的跨进程通信&#xff0c;后者使用软总线驱动&#xff0c;用于跨设备…

【iOS ARKit】人形遮挡

人形遮挡简介 在 AR系统中&#xff0c;计算机通过对设备摄像头采集的图像进行视觉处理和组织&#xff0c;建立起实景空间&#xff0c;然后将生成的虚拟对象依据几何一致性原理嵌入到实景空间中&#xff0c;形成虚实融合的增强现实环境&#xff0c;再输出到显示系统中呈现给使用…

【数据结构】链表OJ面试题3(题库+解析)

1.前言 前五题在这http://t.csdnimg.cn/UeggB 后三题在这http://t.csdnimg.cn/gbohQ 记录每天的刷题&#xff0c;继续坚持&#xff01; 2.OJ题目训练 9. 给定一个链表&#xff0c;判断链表中是否有环。 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成…

假期最好的安排:读书学习成长

假期是每个人放松身心、充电学习的好时机。然而&#xff0c;很多人往往会将假期用于休闲娱乐&#xff0c;错失了充实自己的宝贵机会。本文将介绍如何通过读书学习成长&#xff0c;让你度过一个充实、有意义的假期。 一、知识拓展 假期是知识拓展的好时机。你可以选择一些与你…

嵌入式中《C++之旅》阅读笔记

constexpr constexpr的隐含意思是在编译阶段求值&#xff0c;对于一些求值操作&#xff0c;如果声明为constexpr&#xff0c;那么会编译器会尝试在编译阶段进行计算求值&#xff0c;如果求值成功&#xff0c;则用结果进行替换。 一个常用的例子是如下&#xff1a; constexpr…

深入解析 Spring 事务机制

当构建复杂的企业级应用程序时&#xff0c;数据一致性和可靠性是至关重要的。Spring 框架提供了强大而灵活的事务管理机制&#xff0c;成为开发者处理事务的首选工具。本文将深入探讨 Spring 事务的使用和原理&#xff0c;为大家提供全面的了解和实际应用的指导。 本文概览 首…

ORM模型类

模型 创建两个表 创建模型类 from django.db import models# Create your models here. class BookInfo(models.Model):name models.CharField(max_length10, uniqueTrue) # 书名pub_date models.DateField(nullTrue) # 发布时间read_count models.IntegerField(default…

【JSON2WEB】04 amis低代码前端框架介绍

1 什么是 amis amis 是一个低代码前端框架&#xff0c;它使用 JSON 配置来生成页面&#xff0c;可以减少页面开发工作量&#xff0c;极大提升效率。 看到amis一句话的介绍&#xff0c;感觉就是JSON2WEB要找的前端框架。 amis是百度开源的框架&#xff0c;毕竟是大厂&#xff0c…

即插即用、简单有效的大语言模型推荐算法!港大联合百度推出RLMRec

论文链接&#xff1a; https://arxiv.org/abs/2310.15950 论文代码&#xff1a; https://github.com/HKUDS/RLMRec 实验室主页&#xff1a; https://sites.google.com/view/chaoh/group-join-us?authuser0 TLDR 本文从互信息最大化的理论角度出发&#xff0c;通过引入文本信号…

使用No-SQL数据库支持连接查询用例的讨论

简介 在本文中&#xff0c;我们将简单介绍什么是No-SQL数据库。然后我们会讨论一种使用关系数据库比较容易实现的查询&#xff0c;即连接查询&#xff0c;怎么样使用No-SQL来实现。 什么是No-SQL数据库 与No-SQL数据库相对应的是传统的关系数据库&#xff08;RDBMS&#xff…

JRT监听程序

本次设计避免以往设计缺陷&#xff0c;老的主要为了保持兼容性&#xff0c;在用的设计就不好调了。 首先&#xff0c;接口抽象时候就不在给参数放仪器ID和处理类了&#xff0c;直接放仪器配置实体&#xff0c;接口实现想用什么属性就用什么属性&#xff0c;避免老方式要扩参数时…

java的excel列行合并模版

1.效果 2.模版 <tableborder"1"cellpadding"0"cellspacing"0"class"tablebor"id"TABLE"><tr align"center" class"bg217"><td style"background-color: #008000; color: #ffffff;p…

archlinux 使用 electron-ssr 代理 socks5

提前下载好 pacman 包 https://github.com/shadowsocksrr/electron-ssr/releases/download/v0.2.7/electron-ssr-0.2.7.pacman 首先要有 yay 和 aur 源&#xff0c;这个可以参考我之前的博客 虚拟机内使用 archinstall 安装 arch linux 2024.01.01 安装依赖 yay 安装的&#…

WebGL+Three.js入门与实战——绘制水平移动的点、通过鼠标控制绘制(点击绘制、移动绘制、模拟画笔)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…