技术交流,请加QQ群:538327407

我的各种github 开源项目和代码:https://github.com/linbin524

背景

笔者 目前架构的IOT 项目是使用abp 框架作为后台,虽然abp的框架适用于中小型项目框架,但由于架构优美,笔者认为还是可以经过改造,作为大型项目中使用。但IOT 的这个项目目前刚上线不久,十几天数据库已经有了上百GB,而且由于实施检查设备状态,调用设备状态维护表,审计日志压力很大,单单审计日志一天的数据量就有几十万,目前在架构上,笔者做了几个优化处理;

1、针对审计日志,笔者重写了Abp 原有的 IAuditingStore,实现mongodb和redis 两种转移,并且针对审计日志内容做了过滤,DisableAuditing特性标记指定的类或方法不进行记录。

ps:abp 虽然有mongodb 的封装,但它的出发点是和EF 同一个模式,左右系统唯一的ORM,如果要使用abp 的mongo 封装,必须要替代EF,或者重写ABP UnitOfWorkOptions,否则直接用会出现工作单元转换失败的问题。

2、站点层面使用nginx 做了反向代理,进行多站点服务,通信模式由原来的队列、改为服务化,EventBus等方式

3、数据库底层 做了Percona XtraDB Cluster—MySQL 集群处理迁移。

思考评估:1、审计日志这样处理,从源头做了缩减,并且进行Nosql拆分,有助于缓解数据库压力。

2、中间层的处理是一般IOT 中间件各种脚手架的组合,成熟,也有经过多年生产环境的检验。

3、数据库底层 使用Percona XtraDB Cluster,是因为它支持集群,可以缓解数据库请求压力,又支持abp的事务;

但从真正大系统考虑,其实最理性的模式应该是分片,结合SOA、或者微服务才能真正解决底层压力,目前考量了Tidb(张善友 张队推荐的)、oceanbase(淘宝 自有数据库,生产环境十年)、mycat中间件(听说这个坑多)等,

为了暂时不做大改造,只能先使用 Percona XtraDB Cluster,后续可能使用Orleans(Azure 云框架)、akka.net(大型的框架) 或者 Service Fabric(微服务框架)

二、Percona XtraDB Cluster 评估

优点如下:

1.当执行一个查询时,在本地节点上执行。因为所有数据都在本地,无需远程访问。

2.无需集中管理。可以在任何时间点失去任何节点,但是集群将照常工作。

3.良好的读负载扩展,任意节点都可以查询。

缺点如下:

1.加入新节点,开销大。需要复制完整的数据。

2.不能有效的解决写缩放问题,所有的写操作都将发生在所有节点上。

3.有多少个节点就有多少重复的数据。

Percona XtraDB Cluster是MySQL高可用性和可扩展性的解决方案.

Percona XtraDB Cluster提供的特性有:

1.同步复制,事务要么在所有节点提交或不提交。

2.多主复制,可以在任意节点进行写操作。

3.在从服务器上并行应用事件,真正意义上的并行复制。

4.节点自动配置。

5.数据一致性,不再是异步复制。

Percona XtraDB Cluster完全兼容MySQL和Percona Server,表现在:

1.数据的兼容性

2.应用程序的兼容性:无需更改应用程序

1.集群是有节点组成的,推荐配置至少3个节点,但是也可以运行在2个节点上。

2.每个节点都是普通的mysql/percona服务器,可以将现有的数据库服务器组成集群,反之,也可以将集群拆分成单独的服务器。

3.每个节点都包含完整的数据副本。

三、部署流程

1、环境准备

 

在腾讯云上开设三个测试服务器,系统 镜像 CentOS 7.5 64

用远程工具连接三台测试服务器,完成如下操作

(1)  关闭firewalld防火墙

# systemctl disable firewalld --now

关闭防火墙或者允许3306, 4444, 4567和4568四个端口的连接

(2)关闭SELINUX

# setenforce 0
# sed -i 's,^SELINUX=enforcing,SELINUX=disabled,g' /etc/selinux/config

2、主节点部署

(1)安装PXC yum源

yum install http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm

(2) 安装PXC

# yum install Percona-XtraDB-Cluster-56

最终下载下来的版本是Percona-XtraDB-Cluster-56-5.6.30

(3) 修改    /etc/my.cnf

     vim  /etc/my.cnf
[mysqld]

datadir=/var/lib/mysql
user=mysql wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
#集群的ip
wsrep_cluster_address=gcomm://节点ip1,节点ip2,节点ip3 binlog_format=ROW default_storage_engine=InnoDB innodb_autoinc_lock_mode=2 #当前主节点的ip
wsrep_node_address=当前节点ip wsrep_sst_method=xtrabackup-v2 wsrep_cluster_name=my_centos_cluster
#初始化一个mysql的用户和密码
wsrep_sst_auth="admin:123456"

(4)启动主节点

 systemctl start mysql@bootstrap.service

(5)进入mysql

登录 (初始化状态,无密码,遇到要输密码直接回车)

mysql -uroot -p

(6) 登录客户端查看数据库的状态,在进行权限配置允许ip访问,默认无法远程访问,但是我们需要远程通过图形化等界面查看,所以要做如下配置

mysql> show status like 'wsrep%';

CREATE USER 'admin'@'localhost' IDENTIFIED BY '123456';//如果这里报错,看一下是否有 用户存在了

GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'admin'@'localhost'; 

FLUSH PRIVILEGES;

完成后可以用Navicat For mysql 连接看一下是否可以成功访问

3、其他两个节点的配置

(1)安装PXC yum源

yum install http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm

(2) 安装PXC

# yum install Percona-XtraDB-Cluster-56

(3) 修改    /etc/my.cnf

     vim  /etc/my.cnf
[mysqld]

datadir=/var/lib/mysql
user=mysql wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
#集群的ip
wsrep_cluster_address=gcomm://节点ip1,节点ip2,节点ip3 binlog_format=ROW default_storage_engine=InnoDB innodb_autoinc_lock_mode=2 #当前主节点的ip
wsrep_node_address=当前节点ip wsrep_sst_method=xtrabackup-v2 wsrep_cluster_name=my_centos_cluster
#初始化一个mysql的用户和密码
wsrep_sst_auth="admin:123456"

(4)启动当前节点(这一步和主节点不一样)

systemctl start mysql

(5)进入mysql

登录 (初始化状态,无密码,遇到要输密码直接回车)

mysql -uroot -p

(6) 登录客户端查看数据库的状态,在进行权限配置允许ip访问,默认无法远程访问,但是我们需要远程通过图形化等界面查看,所以要做如下配置

mysql> show status like 'wsrep%';

CREATE USER 'admin'@'localhost' IDENTIFIED BY '123456';//如果这里报错,看一下是否有 用户存在了

GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'admin'@'localhost'; 

FLUSH PRIVILEGES;

完成后可以用Navicat For mysql 连接看一下是否可以成功访问

(7)可以在mysql中执行如下命令查看

 show status like 'wsrep%';

如果正常,可以出现如下界面,标识当前三个集群节点

(8)如果出现启动节点时候出现异常,可以查看提示的操作,看看日志,百度一下看看是什么错误,怎么解决,因为各种错误都有,就不好一一解释了。

比如笔者在操作过程中就出现如下错误

ecStop=/usr/bin/mysql-systemd stop (code=exited, status=2)

后面查找原因应该是 防火墙等问题,进行关闭拦截等操作,就是一开始 环境准备的后面那一步,关闭防火墙、SELINUX,

主节点重启

systemctl stop mysql@bootstrap.service
systemctl start mysql@bootstrap.service

其他节点也再次启动

systemctl start mysql

4、abp 进行数据库迁移

(1)abp 想要进行mysql 支持,网上的教程有,我就不重复造轮子自己参考(不要要注意 组件的版本,如果出现差异可能会失败)

https://www.jianshu.com/p/543e34da16a7?winzoom=1

(2) 将数据库连接字符串改为 主节点

 <add name="Default" connectionString="server=主节点ip;port=3306;database=abpzero4_6db;uid=admin;password=123456;" providerName="MySql.Data.MySqlClient" />

(3) 执行迁移

(4)查看对应的三台服务器集群都自动同步该数据库

(5)在Appservice 中建立测试服务进行增删改查、事务等测试

using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Dynamic;
using Abp.Linq.Extensions; using MyCompanyName.AbpZeroTemplate;
using MyCompanyName.AbpZeroTemplate.ZLDB_Domain;
using MyCompanyName.AbpZeroTemplate.Authorization.Consignee.Exporting;
using MyCompanyName.AbpZeroTemplate.ZLDB_Domain.Dtos;
using MyCompanyName.AbpZeroTemplate.Dto; namespace MyCompanyName.AbpZeroTemplate
{ /// <summary>
/// 收货地址 业务实现接口
/// </summary>
public class ConsigneeAppService : AbpZeroTemplateAppServiceBase, IConsigneeAppService
{
private readonly IRepository<Consignee, Guid> _consigneeRepository;
private readonly IConsigneeListExcelExporter _iConsigneeListExcelExporter; /// <summary>
/// 构造函数自动注入我们所需要的类或接口
/// </summary>
public ConsigneeAppService(IRepository<Consignee, Guid> consigneeRepository,
IConsigneeListExcelExporter iConsigneeListExcelExporter)
{
_consigneeRepository = consigneeRepository;
_iConsigneeListExcelExporter = iConsigneeListExcelExporter;
//_consigneeMongoDbRepository = consigneeMongoDbRepository;
} /// <summary>
/// 获取所有数据列表
/// </summary>
/// <returns>返回数据集合</returns>
public async Task<List<ConsigneeDto>> GetAllList(string guid = "")
{
//try
//{
// var model = new Consignee() { DealerId = System.Guid.NewGuid() };
// var mr = _consigneeMongoDbRepository.Insert(model);
//}
//catch (Exception ex)
//{ // throw;
//} //调用Task仓储的特定方法GetAllWithPeople
var resultList = await _consigneeRepository.GetAllListAsync(); return Mapper.Map<List<ConsigneeDto>>(resultList).ToList();
} /// <summary>
/// 获取分页数据列表 分页具体代码需要适当修改,如orderby 需要匹配 创建时间 或者其他数据Id(int)
/// </summary>
/// <returns>返回数据集合</returns>
public async Task<PagedResultDto<ConsigneeDto>> GetPagedListAsync(PagedAndFilteredInputDto input)
{
var query = _consigneeRepository.GetAll();
//TODO:根据传入的参数添加过滤条件 var resultCount = await query.CountAsync();
var resultconsignee = await query
.OrderBy(x => x.Id)
.PageBy(input)
.ToListAsync(); var resultListDtos = resultconsignee.MapTo<List<ConsigneeDto>>(); return new PagedResultDto<ConsigneeDto>(
resultCount,
resultListDtos
);
} /// <summary>
/// 获取指定条件的数据列表 webapi 无法使用
/// </summary>
/// <returns>返回数据集合</returns>
public async Task<List<ConsigneeDto>> GetListByCodition(Expression<Func<Consignee, bool>> predicate)
{ var resultList = await _consigneeRepository.GetAllListAsync(predicate);
return Mapper.Map<List<ConsigneeDto>>(resultList).ToList();
} /// <summary>
/// 导出excel 具体方法
/// </summary>
/// <returns>excel文件</returns>
/// public async Task<FileDto> GetConsigneeToExcel()
///{
/// var resultList = await _consigneeRepository.GetAllListAsync();
/// var consigneeDtos= Mapper.Map<List<ConsigneeDto>>(resultList).ToList();
/// return _iConsigneeListExcelExporter.ExportToFile(consigneeDtos);
/// } /// <summary>
/// 根据指定id 获取数据实体
/// </summary>
/// <param name="input">当前id</param>
/// <returns></returns>
public async Task<ConsigneeDto> GetConsigneeForEditAsync(NullableIdDto<System.Guid> input)
{
var output = new ConsigneeDto(); ConsigneeDto consigneeEditDto; if (input.Id.HasValue)
{
var entity = await _consigneeRepository.GetAsync(input.Id.Value);
consigneeEditDto = entity.MapTo<ConsigneeDto>();
}
else
{
consigneeEditDto = new ConsigneeDto();
} output = consigneeEditDto;
return output;
} /// <summary>
/// 根据Id创建或编辑操作
/// </summary>
/// <param name="input">实体</param>
/// <returns></returns>
public async Task CreateOrUpdateConsigneeAsync(ConsigneeDto input)
{
if (!string.IsNullOrWhiteSpace(input.Id))
{
await Update(input);
}
else
{
await Create(input);
}
} /// <summary>
/// 新增
/// </summary>
/// <param name="input">新增参数</param>
/// <returns>新增实体</returns>
public async Task<Guid> Create(ConsigneeDto input)
{
input.Id = new Consignee().Id.ToString();
var resultObj = input.MapTo<Consignee>();
var result = await _consigneeRepository.InsertAsync(resultObj); return result.Id;
} /// <summary>
/// 新增
/// </summary>
/// <param name="input">新增参数</param>
/// <returns>新增实体</returns>
public async Task<int> CreateList(List<ConsigneeDto> list)
{
foreach (var input in list) {
if (input.Contact.Contains("ex")) {
throw new Exception("测试分布式异常!");
}
input.Id = new Consignee().Id.ToString(); var resultObj = input.MapTo<Consignee>();
var result = await _consigneeRepository.InsertAsync(resultObj);
} return list.Count();
} /// <summary>
/// 修改
/// </summary>
/// <param name="input">修改参数</param>
/// <returns>修改实体</returns>
public async Task<ConsigneeDto> Update(ConsigneeDto input)
{
Consignee obj = await _consigneeRepository.GetAsync(new Guid(input.Id));
input.MapTo(obj);
var result = await _consigneeRepository.UpdateAsync(obj);
return obj.MapTo<ConsigneeDto>();
} /// <summary>
/// 删除
/// </summary>
/// <param name="input">删除Dto</param>
/// <returns>无返回值</returns>
public async System.Threading.Tasks.Task Delete(EntityDto<string> input)
{
await _consigneeRepository.DeleteAsync(new Guid(input.Id));
} /// <summary>
/// 删除 webapi 无法使用
/// </summary>
/// <param name="predicate">删除条件</param>
/// <returns>无返回值</returns>
public async System.Threading.Tasks.Task DeleteByCondition(Expression<Func<Consignee, bool>> predicate)
{
await _consigneeRepository.DeleteAsync(predicate); }
} }

在swagger ui中增删改查都已经正常,而且数据在三个数据库中正常同步

针对事务,做了人为异常处理,确认会实现回滚(abp 自带工作单元处理事务)

 五、后记

这一次只是做了简单的实验性测试,后续需要在加强深入检测,才可以用生产环境中。

读后感觉不错,有收获可以微信请作者喝杯咖啡,读后有疑问请加微信,拉群研讨,注明来意

ABP 框架 数据库底层迁移 Mysql 集群的更多相关文章

  1. 容器化|自建 MySQL 集群迁移到 Kubernetes

    背景 如果你有自建的 MySQL 集群,并且已经感受到了云原生的春风拂面,想将数据迁移到 Kubernetes 上,那么这篇文章可以给你一些思路. 文中将自建 MySQL 集群数据,在线迁移到 Kub ...

  2. 数据库选型之亿级数据量并发访问(MySQL集群)

    刘 勇  Email:lyssym@sina.com 简介 针对实际应用中并发访问MySQL的场景,本文采用多线程对MySQL进行并发读取访问,其中以返回用户所需的数据并显示在终端为测试结束节点,即将 ...

  3. MySQL/MariaDB数据库的Galera高可用性集群实战

      MySQL/MariaDB数据库的Galera高可用性集群实战 作者:尹正杰  版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Galera Cluster概述 1>.什么是Gale ...

  4. 京东分布式MySQL集群方案介绍

    背景 数据库作为一个非常基础的系统,任何一家互联网公司都会使用,数据库产品也很多,有Oracle.SQL Server .MySQL.PostgeSQL.MariaDB等,像SQLServer/Ora ...

  5. 分布式MySQL集群方案的探索与思考

    转载:http://www.infoq.com/cn/articles/exploration-of-distributed-mysql-cluster-scheme?utm_campaign=rig ...

  6. mysql集群数据一致性校验

    目前,mysql在互联网行业使用地如火如荼,很多大型网站都在使用MySQL数据库,通过搭建mysql主备集群,实现高性能,高可用的存储方案.mysql集群的共同特性是通过复制来实现主备间的同步,保证主 ...

  7. MySQL集群的可行方案

    如果单MySQL的优化始终还是顶不住压力时,这个时候我们就必须考虑MySQL的高可用架构(很多同学也爱说成是MySQL集群)了,目前可行的方案有: 一.MySQL Cluster优势:可用性非常高,性 ...

  8. mysql集群搭建--韩国庆

    按照我给大家提供的步骤,一步一步来,你就能配好mysql集群环境 什么是mycat 简单的说,MyCAT就是: •一个彻底开源的,面向企业应用开发的“大数据库集群” •支持事务.ACID.可以替代My ...

  9. mysql集群7.4.1

    一:mysql集群原理: 1 mysql集群分为三个节点: 1.1 控制节点:本身不提供服务只是控制整个集群的开启与关闭 1.2 数据节点:真正提供数据库的存储,并和其他数据节点关联用 1.3 sql ...

随机推荐

  1. 带token的get和post方法

    GET和POST传值失败,多半是传输的字符串和URL的事 public static string ExcuteGetToken(string serviceUrl, string ReqInfo, ...

  2. 设置frameset高度

      设置frameset的高度 设置frameset高度 目前做了一个项目,界面如下: 这是使用frameset做的,在宽屏下开发一直没有发现什么问题,直到一个用户使用800*600的机子测试的时候, ...

  3. SpringBoot定制修改Servlet容器

    1.如何修改Servlet容器的相关配置: 第一种:在application.properties中修改和server有关的配置(ServerProperties提供): server.port=80 ...

  4. maven打包证(支付)书问题

    要将证书文件后缀添加到resource文件夹内范围,添加打包文件后缀(include) 添加maven配置: <resources> <resource> <direct ...

  5. iOS中堆和栈的区别

    管理方式: 对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来讲,释放工作有程序员控制,容易产生memory Leak. 申请大小: 栈:在Windows下,栈是向低地址扩展的数据结构,是一块 ...

  6. 2019.02.09 bzoj2440: [中山市选2011]完全平方数(二分答案+容斥原理)

    传送门 题意简述:qqq次询问(q≤500)(q\le500)(q≤500),每次问第kkk个不被除111以外的完全平方数整除的数是多少(k≤1e9)(k\le1e9)(k≤1e9). 思路:考虑二分 ...

  7. ThinkPHP redirect 传参

    重定向带参 $this->redirect('pay/under_line_success',array('order_id'=>$stuInfo),5,'页面跳转中….'); 第一个参数 ...

  8. js获取浏览器类型进行判断

    本文为webuploader.js中学习心得,感谢开源,从中加入了ie的edge判断 /** * @description 简单的浏览器检查结果. * * * `webkit` webkit版本号,如 ...

  9. ACtiveMQ中间件-发布订阅模式

    前言:ActiveMQ学习心得 1.MQ是什么 MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信, ...

  10. SecureCRTv7.3 和 navicat110_mysql

    激活步骤: 一.首次使用: 1.保持SecureCRT未打开. 2.打开注册机keygen.exe文件(Windows vista ,7,8需要以管理员身份运行),点击[Patch]按钮,会让你选择文 ...