WilliamUUUU / LearningNote

0 stars 0 forks source link

Mysql #4

Open WilliamUUUU opened 1 year ago

WilliamUUUU commented 1 year ago

init

WilliamUUUU commented 1 year ago

0 简介

mysql是目前最火免费开源的数据库,运行速度快。性能卓越,服务稳定,很少出现异常宕机。可满足大部中小型企业的业务需求。

1 安装准备

本次安装是在Linux机器上进行的

2 tar包安装

这种方式适合在一个机器上启动多个mysql实例,如果需要一个实例,推荐使用yum安装。因为笔者这里需要模拟mysql集群,所以在这里使用了这种方式安装。

去官网下载自己想要的版本,这里就下载最新的版本。下载地址:

https://dev.mysql.com/downloads/mysql/

image

这里有两个不同的压缩包,一个是更小的tar.xz,一个是正常的tar,只是两种解压的命令不同

# tar.xz
tar xvJf mysql-8.0.32-linux-glibc2.12-x86_64.tar.xz
# tar
tar -zxvf mysql-8.0.32-linux-glibc2.12-x86_64.tar

将解压好的文件夹放到指定目录中,我这里放的是/usr/local/mysql中。

# 移动
mv -i mysql-8.0.32-linux-glibc2.12-x86_64 /usr/local/mysql
# 重命名
cd /usr/local/mysql
cp mysql-8.0.32-linux-glibc2.12-x86_64 mysql3306

创建一些文件夹

cd /usr/local/mysql/mysql3306
# 配置文件夹
mkdir etc
# 数据文件夹
mkdir data

编写配置文件 vim ect/my.cnf

[mysqld]
bind-address=0.0.0.0
#MySQL监听端口
port=3306
user=mysql
#MySQL安装目录
basedir=/usr/local/mysql/mysql3306
#MySQL数据存储目录
datadir=/usr/local/mysql/mysql3306/data
#MySQL客户程序与服务器之间的本地通信指定一个套接字文件
socket=/usr/local/mysql/mysql3306/mysql.sock
# 数据库日志文件
log-error=/usr/local/mysql/mysql3306/mysql.err
#数据库进程文件目录
pid-file=/usr/local/mysql/mysql3306/mysql.pid
#数据库或数据表的默认字符集
character_set_server=utf8mb4
#符号连接,如果设置为1,则mysql数据库和表里的数据支持储存在datadir目录之外的路径下,默认都是0(较新版本的mysql下默认是1);
symbolic-links=0
explicit_defaults_for_timestamp=true

初始化

./mysqld --defaults-file=/usr/local/mysql/mysql3306/etc/my.cnf --basedir=/usr/local/mysql/mysql3306 
--datadir=/usr/local/mysql/mysql3306/data --user=mysql --initialize --console

查看初始密码

cat mysql.err

image

启动mysql

修改启动文件

vim mysql.server

basedir='/usr/local/mysql/mysql3306'
datadir='/usr/local/mysql/mysql3306/data'

设置MySQL自启动

cp /usr/local/mysql/mysql3306/support-files/mysql.server /etc/init.d/mysql

启动

service mysql start

使用用户名和密码登录mysql

./bin/mysql -uroot -p
#输入生成的密码

登录之后是不能进行任何操作的,会提示让你修改密码

alter user user() identified by "xxxx"; # xxx是你的密码

还需要修改连接地址才能使用远程访问

update user set host = '%' where user = 'root';

最后刷新权限

FLUSH PRIVILEGES;

安装结束

3 yum安装

参考: https://blog.csdn.net/weixin_41947378/article/details/125213069

设置阿里云镜像

# 先将原来的镜像备份
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
# 下载新的 CentOS-Base.repo
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
# 生成缓存
yum makecache
# 安装wget(如果有了就不需要安装了)
yum -y install wget

这里是cenos7,选择7版本

image

image

# 下载发布包
wget https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm
# 安装发布包
rpm -ivh mysql80-community-release-el7-7.noarch.rpm

安装mysql

yum -y install mysql-community-server
# 这是安装最新的版本,如果需要安装其他版本需要修改一下版本

安装其他版本mysql

# 查看 MySQL Yum 存储库中的所有子存储库,并查看其中哪些子存储库已启用或禁用
yum repolist all | grep mysql

image

# 禁用 8.0 系列的子存储库并启用 5.7 系列的子存储库
yum-config-manager --disable mysql80-community
yum-config-manager --enable mysql57-community
# 再执行安装
yum -y install mysql-community-server

启动mysql

#启动 MySQL 服务器
systemctl start mysqld
#检查 MySQL 服务器的状态
systemctl status mysqld

登录mysql

# 使用命令来查找mysql生成的临时密码
grep 'temporary password' /var/log/mysqld.log

登录之后和第2节操作一样

WilliamUUUU commented 1 year ago

0 准备

创建一个父工程,添加一些公共依赖。

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

        <!--  hutool工具包      -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.4</version>
        </dependency>
        <!--fastJSON-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.9</version>
        </dependency>

        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
    </dependencies>

各种方法就在其他创建子工程就好了。较为常用几种方式有JDBC、JPA、MyBaits等

1 JDBC

jdbc是比较原始的一种连接数据库的方式,需要自己编写sql,还有sql注入的风险

创建子模块

<parent>
    <artifactId>test_mysql</artifactId>
    <!-- 这里生成自己的groupId -->
    <groupId>xx.xx</groupId> 
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>test_jdbc</artifactId>

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
    <!-- druid连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.9</version>
    </dependency>
</dependencies>

在resouce下创建jdbc的配置文件jdbc.properties

druid.driverClassName=com.mysql.cj.jdbc.Driver
druid.url=jdbc:mysql://119.91.222.216:3306/test
druid.username=root
druid.password=testmysqlcluster@
druid.minIdle=5
druid.initialSize=5
druid.maxActive=10
druid.maxWait=60000

编写工具类与测试方法

public class JdbcUtil
{
    private static Properties jdbcProps;

    private static DruidDataSource druidDataSource;

    static {
        jdbcProps = new Properties();
        try
        {
            // 读取配置文件
            Resource resource = new ClassPathResource("jdbc.properties");
            jdbcProps.load(resource.getInputStream());
            //配置连接池
            druidDataSource = new DruidDataSource();
            druidDataSource.configFromPropety(jdbcProps);
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     * @return 连接对象
     */
    public static DruidPooledConnection getConnection()
    {
        try
        {
            return druidDataSource.getConnection();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args)
    {
        DruidPooledConnection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try
        {
            connection = getConnection();
            statement = connection.createStatement();
            resultSet = statement.executeQuery("select * from tbl_userinfo limit 10");
            while (resultSet.next())
            {
                String accountName = resultSet.getString("AccountName");
                System.out.println("accountName = " + accountName);
            }
        } catch (SQLException e)
        {
            e.printStackTrace();
        }
    }
}

// 结果
10:22:59.809 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
accountName = admin
accountName = guest
accountName = SPA
accountName = MAXtest
accountName = 产品三部
accountName = PRM
accountName = ATESS
accountName = hua
accountName = hua007
accountName = 18101

2 spring data jpa

2.1 概述

JPA(Java Persistence API)是sun公司提出的一套java持久化规范。它为java开发人员提供了一种对象/关联映射工具来管理java应用中的关系数据。主要是为了简化现有的持久化开发工作和整合ORM技术。

spring data jpa是spring在ORM框架及JPA规范的基础上封装的一套JPA框架,底层使用了Hibernate 的 JPA 技术实现。

2.2 快速入门

创建一个子模块,导入以下依赖

<dependencies>
    <!-- JPA依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- 单元测试依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

编写配置文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://119.XX.XX.216:3306/test?useUnicode=true&characterEncoding=utf8
    username: root
    password: XXXX
  jpa:
    hibernate:
      naming:
        # 默认是a_b下划线命名,加了之后变成aB驼峰命名
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

编写实体类

@Entity
@Data
@Table(name = "tbl_userinfo")
public class UserInfoDTO
{
    @Id
    // 自增
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Integer id;

    @Column(name = "AccountName")
    private String accountName;

    @Column(name = "Enabled")
    private Integer enabled;

    @Column(name = "LastLoginTime")
    private String lastLoginTime;

    @Column(name = "country")
    private String counrty;
}

编写接口

public interface UserDao extends Repository<UserInfoDTO, Integer>
{
    UserInfoDTO findFirstByAccountNameIs(String accoutName);

    @Modifying
    @Transactional
    @Query("update UserInfoDTO u set u.accountName = ?1 where u.id = ?2")
    Integer updateUser(@Param("accountName") String accountName, @Param("id") Integer id);
}

测试

@SpringBootTest
public class TestJpa
{

    @Autowired
    private UserDao userDao;

    @Test
    public void test1()
    {
        UserInfoDTO infoDTO = userDao.findFirstByAccountNameIs("admin");
        System.out.println(JSONObject.toJSONString(infoDTO));
    }
    // {"accountName":"admin","counrty":"Thailand","enabled":1,"id":1,"lastLoginTime":"2023-03-13 20:28:09"}

    @Test
    public void test2()
    {
        Integer baidu = userDao.updateUser("baidu", 2);
        System.out.println("baidu = " + baidu);
    }
}

3 MyBatis与MyBatis-plus

3.1 概述

mybatis是目前世面上比较主流的框架,它是一个半ORM框架,内部封装了JDBC、加载驱动、创建连接等一系列过程,使开发者只需要关注于SQL本身。同时作为半ORM框架它也支持将对象映射成数据库中的字段。Mybatis还存在两级缓存,一级缓存是默认开启的,当一个查询sql执行后,会把结果写入sqlSession中,如果下次sql的相同的话就直接从缓存中取出结果,一但发生commit(修改、添加、删除)就会清空;二级缓存是默认关闭的,范围是 mapper 级别,mapper 以命名空间为单位创建缓存数据结构,结构是 map。mybatis 的二级缓存是通过 CacheExecutor 实现的。CacheExecutor其实是 Executor 的代理对象。所有的查询操作,在 CacheExecutor 中都会先匹配缓存中是否存在,不存在则查询数据库。

mybatis-plus是mybatis的进阶版,封装了很多基础SQL,CRUD等,还有代码生成器可以生成一些基础代码。

3.2 快速入门

本次是以mybatis-plus为例的

<dependencies>
    <!-- 单元测试依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- mybaits-plus依赖 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency>
</dependencies>

配置文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://119.xx.xx.216:3306/test?useUnicode=true&characterEncoding=utf8
    username: root
    password: xxxx

实体类

@TableName("tbl_userinfo")
@Data
public class UserInfoDTO
{
    @TableId(type = IdType.AUTO, value = "ID")
    private Integer id;

    @TableField("AccountName")
    private String accountName;

    @TableField("Enabled")
    private Integer enabled;

    @TableField("LastLoginTime")
    private String lastLoginTime;

    @TableField("country")
    private String country;
}

mapper

@Mapper
public interface UserMapper extends BaseMapper<UserInfoDTO>
{
}

测试

@SpringBootTest
public class Test1
{
    @Autowired
    private UserMapper userMapper;

    @Test
    public void test1()
    {
        UserInfoDTO userInfoDTO = userMapper.selectById(2);
        System.out.println(JSONObject.toJSONString(userInfoDTO));
    }
    // {"accountName":"sina","country":"China","enabled":1,"id":2,"lastLoginTime":"2022-12-19 17:38:57"}
}

3.3 动态表名

有些业务数据是以日/月表形式存放的,原始的在对象上加@TableName("xxx")是不行了,所以需要实现动态表名。

以月表为例

配置类

@Configuration
public class MybatisPlusConfig
{
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor()
    {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
        dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> {
            // 获取参数方法
            Calendar c = RequestDataHelper.getRequestData();
            // 根据名字来判断是否需要动态改变表名
            switch (tableName)
            {
                case "tbl_userinfo_":
                    return getMonthTableName(tableName, c);
                default:
                    return tableName;
            }
        });
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
        return interceptor;
    }

    private String getMonthTableName(String tableName, Calendar c)
    {
        if (c == null)
            c = Calendar.getInstance();
        // 获取年
        int year = c.get(Calendar.YEAR);
        // 获取月
        int month = c.get(Calendar.MONTH) + 1;
        String monthStr = month < 10 ? "0" + month : "" + month;
        return tableName + year + "_" + monthStr;
    }
}

这个时间是放在线程变量中的,在需要动态的表名的SQL之前先往线程变量中设置一下日历对象

public class RequestDataHelper {
    /**
     * 请求参数存取
     */
    private static final ThreadLocal<Calendar> REQUEST_DATA = new ThreadLocal<>();

    /**
     * 设置请求参数
     *
     * @param c 请求参数日历对象
     */
    public static void setRequestData(Calendar c) {
        REQUEST_DATA.set(c);
    }

    /**
     * 获取请求参数
     *
     * @return 请求参数日历对象
     */
    public static Calendar getRequestData() {
        return REQUEST_DATA.get();
    }
}

将对象上面的表名改一下

@TableName("tbl_userinfo_")

测试

@Test
public void test2()
{
    Calendar c = Calendar.getInstance();
    System.out.println(DateUtil.formatAsDatetime(c));
    RequestDataHelper.setRequestData(c);
    UserInfoDTO userInfoDTO = userMapper.selectById(1);
    System.out.println(JSONObject.toJSONString(userInfoDTO));
    // 2023-03-14T15:28:23
    // {"accountName":"3月","country":"China","enabled":1,"id":1,"lastLoginTime":"2023-03-14 15:22:06"}
}
@Test
public void test3()
{
    Calendar c = Calendar.getInstance();
    c.add(Calendar.MONTH, -1);
    System.out.println(DateUtil.formatAsDatetime(c));
    RequestDataHelper.setRequestData(c);
    UserInfoDTO userInfoDTO = userMapper.selectById(1);
    System.out.println(JSONObject.toJSONString(userInfoDTO));
    // 2023-02-14T15:31:19
    // {"accountName":"2月","country":"China","enabled":1,"id":1,"lastLoginTime":"2023-02-16 15:22:30"}
}
WilliamUUUU commented 1 year ago

三、集群

3.1 简介

单机运行mysql会存在单点故障及性能瓶颈问题,所以为了提高Mysql的性能及解决问题,集群方案就诞生了。Mysql的集群方案常见的模式有:一主一从、双主、一主多从、高可用方案等。为了解决mysql的性能问题提出了一主一从的方案,主库提供写,从库提供读的操作,但是当主节点宕机了之后需要手动恢复。为了解决单点故障问题提出了双主的方案,就是做一个备库,当其中一个节点宕机之后另一个切换就很方便。而在大型公司如果要使用mysql,都会搭建一个主可用的集群方案。

3.2 主从

3.2.1 搭建

这次是在同一个电脑上搭建不同的端口的Mysql实例,和多台电脑有所不同。安装方式使用tar包安装

1 从官网上下载一个新的Mysql包

有两种格式,tar.xz、tar。只是解压命令不同,推荐使用更小的tar.xz

# tar.xz
tar xvJf mysql-8.0.32-linux-glibc2.12-x86_64.tar.xz
# tar
tar -zxvf mysql-8.0.32-linux-glibc2.12-x86_64.tar

将解压好的文件放在一个文件夹中

# 移动
mv -i mysql-8.0.32-linux-glibc2.12-x86_64 /usr/local/mysql
# 重命名
cd /usr/local/mysql
cp mysql-8.0.32-linux-glibc2.12-x86_64 mysql3306

查看有没有mysql用户,如果没有的话创建一个用户

groups mysql 
# mysql : mysql 如果出现这个说明已经存在

#如果没有就创建
useradd -g mysql mysql

创建一些文件夹

cd /usr/local/mysql/mysql3306
# 配置文件夹
mkdir etc
# 数据文件夹
mkdir data
# binlog文件夹
mkdir binlog

编写配置文件 vim etc/my.cnf

[mysqld]
bind-address=0.0.0.0
#MySQL监听端口
port=3306
user=mysql
#MySQL安装目录
basedir=/usr/local/mysql/mysql3306
#MySQL数据存储目录
datadir=/usr/local/mysql/mysql3306/data
#MySQL客户程序与服务器之间的本地通信指定一个套接字文件
socket=/usr/local/mysql/mysql3306/mysql.sock
# 数据库日志文件
log-error=/usr/local/mysql/mysql3306/mysql.err
#数据库进程文件目录
pid-file=/usr/local/mysql/mysql3306/mysql.pid
#数据库或数据表的默认字符集
character_set_server=utf8mb4
#符号连接,如果设置为1,则mysql数据库和表里的数据支持储存在datadir目录之外的路径下,默认都是0(较新版本的mysql下默认是1);
symbolic-links=0
explicit_defaults_for_timestamp=true
#mysql 服务ID,保证整个集群环境中唯一
server-id=2
#mysql binlog 日志的存储路径和文件名
log-bin=/usr/local/mysql/mysql3307/binlog/binlog
#是否只读,1 代表只读, 0 代表读写
read-only=0
#忽略的数据, 指不需要同步的数据库
binlog-ignore-db=mysql

复制出一份做为从节点

cd /usr/local/mysql
cp -r mysql3306 mysql3307

修改文件夹权限(如果启动报错最大的可能就是这个错误,要仔细检查文件夹下的文件是不是mysql下的)

chown -R mysql:mysql /usr/local/mysql

主节点搭建

初始化

./mysqld --defaults-file=/usr/local/mysql/mysql3306/etc/my.cnf --basedir=/usr/local/mysql/mysql3306 
--datadir=/usr/local/mysql/mysql3306/data --user=mysql --initialize --console

查看初始密码

cat mysql.err

启动mysql

cd /usr/local/mysql/mysql3306/bin
./mysqld_safe --defaults-extra-file=/usr/local/mysql/mysql3306/etc/my.cnf --datadir=/usr/local/mysql/mysql3306/data --user=mysql &

登录3306mysql

cd /usr/local/mysql/mysql3306/bin
./mysql -P3306 -h127.0.0.1 -uroot -p 
Enter password: 输入你的初始密码

登录之后是不能进行任何操作的,会提示让你修改密码

alter user user() identified by "xxxx"; # xxx是你的密码

还需要修改连接地址才能使用远程访问

update user set host = '%' where user = 'root';

最后刷新权限

FLUSH PRIVILEGES;

安装结束

查看主节点信息

show master status;

image

这里的file binlog.000001和Position 157要记得,后面从节点要用

从节点搭建

修改配置文件

vim /usr/local/mysql/mysql3307/etc/my.cnf

[mysqld]
bind-address=0.0.0.0
#MySQL监听端口
port=3307
user=mysql
#MySQL安装目录
basedir=/usr/local/mysql/mysql3307
#MySQL数据存储目录
datadir=/usr/local/mysql/mysql3307/data
#MySQL客户程序与服务器之间的本地通信指定一个套接字文件
socket=/usr/local/mysql/mysql3307/mysql.sock
# 数据库日志文件
log-error=/usr/local/mysql/mysql3307/mysql.err
#数据库进程文件目录
pid-file=/usr/local/mysql/mysql3307/mysql.pid
#数据库或数据表的默认字符集
character_set_server=utf8mb4
#符号连接,如果设置为1,则mysql数据库和表里的数据支持储存在datadir目录之外的路径下,默认都是0(较新版本的mysql下默认是1);
symbolic-links=0
explicit_defaults_for_timestamp=true
#mysql 服务ID,保证整个集群环境中唯一
server-id=2

#mysql binlog 日志的存储路径和文件名
log-bin=/usr/local/mysql/mysql3307/binlog/binlog
#是否只读,1 代表只读, 0 代表读写
read-only=0

#忽略的数据, 指不需要同步的数据库
binlog-ignore-db=mysql

初始化

cd /usr/local/mysql/mysql3307
./bin/mysqld --defaults-file=/usr/local/mysql/mysql3307/etc/my.cnf --basedir=/usr/local/mysql/mysql3307 
--datadir=/usr/local/mysql/mysql3307/data --user=mysql --initialize --console

查看初始密码

cat mysql.err

启动mysql

cd /usr/local/mysql/mysql3307/bin
./mysqld_safe --defaults-extra-file=/usr/local/mysql/mysql3307/etc/my.cnf --datadir=/usr/local/mysql/mysql3307/data --user=mysql &

登录3307mysql

cd /usr/local/mysql/mysql3306/bin
./mysql -P3307 -h127.0.0.1 -uroot -p 
Enter password: 输入你的初始密码

登录之后是不能进行任何操作的,会提示让你修改密码

alter user user() identified by "xxxx"; # xxx是你的密码

还需要修改连接地址才能使用远程访问

update user set host = '%' where user = 'root';

最后刷新权限

FLUSH PRIVILEGES;

指定主库的IP账号、密码、日志文件、位置

change master to master_host= 'xx.xx.xx.xx', master_user='root', master_password='xxx', master_log_file='binlog.000001', master_log_pos=157;
# host和密码都需要填个人的,如果binlog文件名变的话都要相应的改变

开启同步

start slave;

查看状态

show slave status \G;

image

这种两个yes就是OK的

3.2.2 可能出现的问题

1 复制的时候忘记改配置文件了

修改配置文件再重启即可

2 主从数据库中数据不一致导致不能同步

这种情况最容易发生

最简单的方式删除重新来一次

或者查看master的binlog位置,直接跳过

3.2.3 个人见解

这种是两台机器,在项目中需要配置两个数据源,一个用来写一个用来读,而且要严格控制连接从机的账号权限,不然主机和从机同时往同一个表里插入数据就会造成主从不一致而导致同步停止。如果发现不及时,可能后续所有不一致的数据很大,恢复很费力。

3.3 主主(双主)

双主就两个节点互为主从

3.3.1 搭建

参考3.2.1搭建两个不同端口的mysql实例3308、3309,并且已经指定3308为主库而3309为从库

这里可能会出现一个问题,指定master节点的IP和端口时需要加一个master_port选项

./mysql -P3309 -h127.0.0.1 -uroot -pxxx # xxx为你的密码
change master to master_host= 'xx.xx.xx.xx', master_port=3308, master_user='root', master_password='xxx', master_log_file='binlog.000001', master_log_pos=157;

要形成主主结构只需要再反向绑定一次即可。

登录主库3308

cd /usr/local/mysql/mysql3308/bin
./mysql -P3308 -h127.0.0.1 -uroot -pxxx # xxx为你的密码
change master to master_host= 'xx.xx.xx.xx', master_port=3309, master_user='root', master_password='xxx', master_log_file='binlog.000001', master_log_pos=157;

开启同步

start slave;

查看状态

show slave status \G;

image

image

3.3.2 可能出现的问题

1 在操作同一张表的时候主键冲突

可以设置不同主键生成策略

先停止同步,再杀掉两个节点

# 登录mysql3308
cd /usr/local/mysql/mysql3308/bin
./mysql -P3308 -h127.0.0.1 -uroot -pxxx # xxx为你的密码
stop slave;
exit;
# 查看3308节点的进程
ps -ef | grep 3308
# 会出现两个进程
kill -9 xxxx xxxx

再登录3309节点进行相同的操作

修改配置文件,加上下面两个配置

vim /usr/local/mysql/mysql3308/etc/my.cnf

#自动增长起点(根据不同的库进行不同的设置,例如有两个库,两个库的起点分别是1、2)
auto_increment_offset=1
#自动增长步长(有几个主库就是几)
auto_increment_increment=2
vim /usr/local/mysql/mysql3309/etc/my.cnf

#自动增长起点(根据不同的库进行不同的设置,例如有两个库,两个库的起点分别是1、2)
auto_increment_offset=2
#自动增长步长(有几个主库就是几)
auto_increment_increment=2

再启动两个节点,登录开启同步

cd /usr/local/mysql/mysql3308/bin
# 启动节点
./mysqld_safe --defaults-extra-file=/usr/local/mysql/mysql3308/etc/my.cnf --datadir=/usr/local/mysql/mysql3308/data --user=mysql &
#登录
./mysql -P3308 -h127.0.0.1 -uroot -pxxx # xxx为你的密码
# 开启同步
start slave;

3309节点同理。

在3308节点上新增记录会发现Id都是奇数,3309上新增的id都是偶数