数据库中间件
为什么需要数据库中间件?
一、场景1:数据库存储的数据量不是很大,但是并发的读写操作很大,超过数据库服务器的处理能力。
二、场景2:并发的读写操作很大,加缓存,也会有很多问题,比如命不中,还是会有大量的请求到数据库,数据库支撑不住。
场景3:读写分离+缓存,
增加数据库访问模块----->数据库中间件
可以一主多从,数据库中间需要具备负载均衡的特点,将请求均匀的打到多个从服务器上,不允许有多个相同的主库,其实很简单,多个主库间数据同步难以保证数据的一致性 , 数据量大的时候单库存储不下来。
分库存储。
数据库中间件解析sql,a表到a库中查询,b表到b库中查询 ,屏蔽掉下边数据库的变化对dao层的影响。
数据量很大 , 并发压力大: 分库+读写分离
三、假如单表的数据量很大,超出了单个表的上限。
此时引出了 :分区表,分表的概念。
根据一定的规则将数据分到多个表中存储。如何决定数据存储到哪个表?如何查询多表,如何做到对dao层透明。
数据库中间件设计要点?
MyCat
MyCat入门
MyCat 是数据库分库分表中间件,是国内最活跃性能最好的开源数据库中间件。
官网:http://www.mycat.org.cn/
从不同的角度看MyCat
- 一个彻底开源的,面向企业应用开发的大数据库集群。
- 支持事务,可以替代mysql 的加强版数据库。
- 一个可以视为mysql集群的企业级数据库,一般用来代替oracle数据库集群。
- 一个融合了内存缓存技术,NoSQL技术,HDFS大数据的新型sql server.
- 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品。
- 一个新颖的数据库中间件产品。
MyCat应用场景
- 单纯的读写分离,此时配置最为简单,支持读写分离,主从切换。
- 分库分表,对于超过1000万的表进行分片,最大支持1000亿的单表分片。
- 多租户应用,每个应用一个库,但是应用程序只连接MyCat,从而不改造程序本身,实现多租户化。
- 报表系统,借助于MyCat的分表能力,处理大规模报表的统计。
- 替代Hbase,分析大数据。
- 作为海量数据实时查询的一种简单有效方案,比如100亿条频繁查询的记录需要在三秒内查询出结果(利用集群,查询分到多节点上,最后结果合并)。除了基于主键查询,还可能存在范围查询或者其他属性查询,此时MyCat是最简单有效的选择。
MyCat工作原理
原理:拦截 ,它会拦截用户发送过来的SQL语句,首先解析SQL语句,做一些特定的分析,如分片分析,路由分析,读写分离分析,缓存分析等;然后将次SQL发往后端的真实数据库,并将返回返回的结果做适当的处理,比如结果合并,聚合处理,排序处理,分页处理等,最终再返回给用户。
架构:
解释:从上往下,首先应用连接mycat,通过的是原生的mysql协议,就像连接mysql一样,只要引入mysql的jar包,通过jdbc直接连到mycat,正常的mysql客户端也可以直接连接到mycat。下一层是连接池,来处理跟应用之间的连接,当应用把SQL语句发来之后SQL解析组件,SQL优化组件,SQL路由组件进行解析,最终SQL执行组件来执行SQL,通过之前的解析mycat知道这条SQL语句应该传到哪一个具体的数据库节点上。把语句进行处理后发到对应的数据库节点上去执行,把结果返回,一样的经历之前说的结果集合并,排序 ,聚合 最后把数据返回给应用。
在mycat内部还具有监控管理模块,对外提供连接,可以通过管理组件来查看mycat工作情况,内部信息。
关键特性
- 支持SQL92标准
- 遵守Mysql本机协议,跨语言,跨平台,跨数据库的通用中间件代理。
- 基于心跳的自动故障切换,支持读写分离,支持MySQL主从,以及Galera群集复制。
- 支持Galera for MySQL实现,Percona集群或MariaDB集群
- 基于Nio实现,有效管理线程,高并发问题。
- 支持数据的多片自动路由与聚合,支持总和,计数,最大等常用的聚合函数。
- 支持单库内部任意join,支持跨库2表join,甚至基于caltlet的多表join。
- 支持通过串联表,ER关系的分片策略,实现了高效的多表联接查询。
- 支持多租户方案。
- 支持分布式事务(弱xa)。
- 支持串联序列号,解决分布式下的主键生成问题。
- 分片规则丰富,插件化开发,易于扩展。
- 强大的网络,命令行监控。
- 支持前端作为mysq通用代理,以JDBC方式支持Oracle,DB2,SQL Server,mongodb,巨杉。
- 支持密码加密
- 支持服务降级
- 支持IP白名单
- 支持SQL黑名单,sql注入攻击拦截
- 支持分表(1.6)
- 基于ZooKeeper管理,在线升级,扩容,智能优化,大数据处理(2.0开发版)
MyCat安装及基本使用
# mysql8.0 建议5.7
# jdk1.8
# mycat1.6.6
wget https://repo.mysql.com//mysql80-community-release-el8-1.noarch.rpm
rpm -ivh mysql80-community-release-el8-1.noarch.rpm
# 安装MySQL服务
yum install mysql-server
systemctl list-unit-files|grep mysqld
# 开机自启动MySQL
systemctl enable mysqld.service
# 查看是否启动
ps -ef|grep mysql
# 启动mysql
systemctl start mysqld.service
#直接mysql 就可以进入
mysql
# 修改密码
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
# 使用密码进入mysql
mysql -uroot -p123456
# 但是不可以远程连接
select host from user where user='root';
update user set host = '%' where user ='root';
update user set host = 'localhost' where user ='root';
# 设置时区
show variables like'%time_zone';
set global time_zone = '+8:00';
# 下载 mycat http://dl.mycat.org.cn/1.6-RELEASE/
tar -zxvf Mycat-server-1.6.6.1-release-20181031195535-linux.tar.gz -C /usr/local/
# 环境变量
vi /etc/profile
export MYCAT_HOME=/usr/local/mycat
source /etc/profile
# 新建用户 mycat
useradd mycat
passwd mycat
chown -R mycat:mycat /usr/local/mycat
# 5.7mysql
# 网址 https://www.jianshu.com/p/ee018b20a6e6
yum install libncurses*
# 离线下载的tar上传至/usr/local目录下
# 为了方便将mysql安装包改名称,直接改为mysql.tar.gz
tar -xvzf mysql.tar.gz
# 创建一个mysql用户
useradd -s /bin/false -d /usr/local/mysql mysql
# 进到mysql安装目录
cd/usr/local/mysql
# 创建data目录
mkdir data
# 授权
chown -R mysql.mysql ../mysql
# 进行初始化 末尾有个密码需要记录
cd bin
./mysqld --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --initialize
# 设置启动
cp ../support-files/mysql.server /etc/init.d/mysqld
vim /etc/my.cnf
# /etc/my.cnf的内容如下
------------------------------------------------------------------------------------
[mysqld]
port = 3306
basedir = /usr/local/mysql
datadir=/usr/local/mysql/data
socket=/tmp/mysql.sock
pid-file=/usr/local/mysql/data/mysql.pid
log-error=/usr/local/mysql/data/error.log
character_set_server=utf8
user=mysql
max_connections=1500
symbolic-links=0
# 东八区时区,北京时区
default-time-zone = '+8:00'
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
# 导入包允许的大小设置
max_allowed_packet=1024M
# 忽略表名大小写;1表示忽略大小写,0表示解析大小写
lower_case_table_names=1
# 也可以使用跳过密码,不建议使用
skip-grant-tables
!includedir /etc/my.cnf.d
-----------------------------------------------------------------------------------
# mysql 添加环境变量
vim /etc/profile
# 末尾加这个语句
PATH=/usr/local/mysql/bin:$PATH
# 启动命令
service mysqld start 或者 /etc/init.d/mysqld start
# 停止命令
service mysqld stop 或者 /etc/init.d/mysqld stop
# 重启命令
service mysqld restart 或者 /etc/init.d/mysqld restart
# 进入mysql
./bin/mysql -u root -p #记录的密码
# 执行sql脚本改密码
set password=password('123456');
use mysql;
update user set host='%' where user='root';
flush privileges;
# navcat测试连接
Mycat读写分离
核心概念
数据库中间件:对应用,MyCat就是数据库服务,对于后端数据库集群,MyCat是后端数据库集群的代理。
逻辑概念
逻辑库:在mycat数据库服务中定义,管理的数据库,在schema.xml中定义。
逻辑表:在逻辑库中定义的包含需要分库分表存储的表。
dataNode:数据节点(分片节点),逻辑表分片的存放节点(在那个机器的的哪个数据库上)
dataHost:数据主机(节点主机),数据节点所在的主机
物理概念
writeHost:写主机,真实的数据库服务主机
readHost:读主机,真实的数据库服务主机
# 主从设置
# https://www.cnblogs.com/lelehellow/p/9633315.html
grant replication slave on *.* to 'repl'@'192.168.125.131' identified by '123456';
vi /etc/my.cnf
[mysqld]
log-bin=mysql-bin # 日志文件名前缀
server-id=1 # 在集群中的唯一id,值可以为1~2^32-1的整数
service mysqld restart
从库执行
CHANGE MASTER TO MASTER_HOST='192.168.125.130', MASTER_USER='repl', MASTER_PASSWORD='123456';
schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 逻辑库 -->
<schema name="TESTDB1" checkSQLschema="true" sqlMaxLimit="100">
<!-- 逻辑表 -->
<!-- rule="auto-sharding-long" -->
<table name="T_ORDER" dataNode="dn1,dn2" />
</schema>
<!-- 数据分片 俩个分片 一个主机 -->
<dataNode name="dn1" dataHost="localhost1" database="orders" />
<dataNode name="dn2" dataHost="localhost1" database="orders" />
<!-- 主机地址 -->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="192.168.125.130:3306" user="root"
password="123456">
<!-- can have multi read hosts -->
<readHost host="hostS2" url="192.168.125.131:3306" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
逻辑库schema,每个Mycat可以有多个逻辑库,每个逻辑库都有自己的独立相关配置。
假如不配置schema标签,所有的表都会默认属于同一个默认的逻辑库。
属性说明:
# schema元素的属性说明
name = 逻辑数据库名
dataNode = 如果库中存在没有指定dataNode的表,这里配置它的默认存储节点
checkSQLschema = 是否检查SQL语句中的库名,默认false不检查,当设置为true时会检查,当真实库名与逻辑库名一 致时,会去掉语句中的库名前缀
sqlMaxLimit = 对于拆分库的schema,当值设置为某个具体的数值,执行sql语句时,如果没有limit,mycat会自动加
逻辑表 table标签定义,所有需要分片的表都使用table标签来定义。
# table标签属性
# <table name="T_ORDER" dataNode="dn1,dn2" />
name = 同一个逻辑库中定义的逻辑表名必须只能有一个
dataNode = 存放逻辑表数据的数据节点 可以有多个
rule = 指定逻辑表要分片的规则名称,规则在rule.xml中定义,与tableRule标签中的name属性一致
ruleRequired = 该属性指定是否必须要绑定分片规则,true为绑定,如果没有配置分片规则,会报错
primaryKey = 逻辑表对应真实表的主键,当分片的规则是使用非主键进行分片的,那么在使用主键查询时就会发送查询语 句到所有配置的数据节点上,如果该属性配置了主键那么mycat会缓存主键与具体节点的信息,那么再次 使用主键进行查询时就不会进行广播式的查询会直接将语句发送到指定数据节点
type = 用来定义逻辑表的类型 目前只有全局表(global)和普通表(不需要配置)俩种
autoincrement = 是否使用主键自增长
subTables = 库内分表(了解)
needAddLimit = 是否在每条语句后加limit限制 默认true
# childTable标签
# 用于定义E-R分片的字表,字表使用父表的分片规则,通过标签上的属性与父表进行关联,主要是配置字表与父表的关联关系
name = 字表名称
primaryKey = 字表的主键列名
joinKey = 字表中与父表关联的列
parentKey = 父表中关联的列
dataHost 标签
dataHost 定义了Mycat中的数据主机,数据主机定义了具体的数据库服务,读写分离配置和心跳语句,这里是物理数据库服务关联的地方。
<!-- 数据库地址 -->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="192.168.125.130:3306" user="root"
password="123456">
<!-- can have multi read hosts -->
<readHost host="hostS2" url="192.168.125.131:3306" user="root" password="123456" />
</writeHost>
</dataHost>
# 属性说明
name = 数据库主机名 必须唯一
maxCon = 指定每个读写实例连接池的最大连接
minCon = 指定每个读写实例连接池的最小连接 初始化连接池的大小
balance = 负载均衡类型
balance = 0 不开启读写分离,所有的读操作都发送到当前可用的写主机上
balance = 1 全部的readHost与stand by writeHost 参与select 语句的负载均衡。
#单理解--> 当双主双从(m1-s1,m2-s2,并且m1 m2 互为主备)正常情况下 m2 s2 s1 都参与select语
#的负载均衡
balance = 2 所有的操作都随机的分发到 写 和 读 机器上
balance = 3 所有的读操作都分发到写主机对应的读主机上
writeType = 写操作发送到哪一个writeHost.
0 : 所有的写操作都发送给第一个writeHost,第一个挂了切换到还生存的第二个writeHost,重新启动后以切换后的为准。有切换记录,dnindex.properties
1 : 随机发送到writeHost 1.5之后作废了
switchType = -1表示不自动切换;1为默认值,自动切换;2 基于mysql主从同步的状态决定是否切换
dbType = 指定后端连接数据库的类型
dbDriver = 指定后端链接数据库的驱动,有俩种 native 和 jdbc
heartbeat 标签
这个标签内指明用于和后端数据库进行心跳检查的语句
MySQL 使用 select user()
Oracle 使用 select 1 from dual
writeHost 指定的数据库宕机,那这个writeHost 对应的readHost 都不能再使用,并且系统会自动检测,是否需要切换到备用的writeHost.
MyCat分库分表
Mycat中表的分类
分片表
分片表:指有很多大数据,需要切分到多个数据库的表,这样每一个分片都有一部分数据,所有的分片构成了完整的数据。
非分片表
非分片表:一个数据库中并不是所有的表数据都很大,某些表是可以不用切分的,非分片是相对于分片表来定义的,就是针对那些不需要进行数据切分的表。
ER表
ER表:是基于E-R关系的数据分片策略,子表的记录与所关联的父表记录在同一个数据分片上,保证数据join不会跨库操作。
E-R分片是解决跨分片数据join的一种很好的思路,也是数据切分规划的一条重要规则。
全局表
全局表:类似数据字典这种,这种表基本很少变动。
业务表往往需要和字典表join查询,当业务表因为规模进行分片业务表和字典表之间的就会存在跨库。在MyCat中通过表冗余来解决跨库join,即在他的定义中指定的dataNode上都有一份该表的拷贝。(简单理解将数据字典这种类型的表定义为全局表)
分片规则
在rule.xml中定义分片规则
<tableRule name="sharding-by-murmur">
<rule>
<!-- 分片键 -->
<columns>id</columns>
<!-- 分片算法 -->
<algorithm>murmur</algorithm>
</rule>
</tableRule>
<function name="murmur"
class="io.mycat.route.function.PartitionByMurmurHash">
<property name="seed">0</property><!-- 默认是0 -->
<property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
<property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->
<!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替 -->
<!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property>
用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->
</function>