三范式和反三范式
代码语言:javascript复制第一范式:所以字段不可再分(关系型数据库天生支持)
第二范式:所有非主键字段必须完全依赖主键字段,不能
出现部分依赖。
第三范式:在二范式的基础上消除传递依赖
互联网初期,没有什么高并发的概念,而且硬件非常昂贵
所以在数据库的设计上几乎都是选取时间换空间的三范式
表结构以及使用单机数据库。
但是时代在进步,社会在发展,高并发和分布式的概念也越来越火热,单机版的数据库已经不能满足如今的互联网,所以就有了mysql的读写分离和主从复制。
mysql主从复制的原理
代码语言:javascript复制#当前mysql在集群中的编号不能重复
server-id=
#开启mysql binlog日志
log-bin=mysql-log
在mysql的配置文件中添加上面两行配置(window系统为mysql数据存储路径下的my.in文件,linux系统在 etc 里面的my.cnf文件)。配置完成之后,主节点就开启binlog日志,在从节点的配置中只需要指定从节点在集群中的编号即可,在从节点中会开前io和sql两个线程,io线程负责登陆主节点监控和抓取主节点的binlog日志信息到当前节点的relay中继日志中,sql线程负责执行中继日志中抓取到的sql语句,从而实现mysql的主从复制。
主从复制的具体实现
登陆主节点查看主节点信息
代码语言:javascript复制mysql> show master statusG;
*************************** 1. row ***************************
File: mysql-log.000005 二进制文件名称
Position: binlog日志指针位置
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
row in set (0.00 sec)
登陆从节点挂载到主节点上
代码语言:javascript复制CHANGE MASTER TO
MASTER_HOST='10.9.104.184', --> 主节点ip地址
MASTER_PORT=, -->主节点mysql端口号
MASTER_USER='root',
MASTER_PASSWORD='root',
MASTER_LOG_FILE='mysql-log.000002',-->主节点二进制文件名称
MASTER_LOG_POS=主节点指针位置
最后不要忘了在从节点上执行如下sql,开启从节点角色
代码语言:javascript复制start slave
mysql的双机热备
其实就是让两个mysql互为主从,上述操作在两个mysql中换着操作一下就好
mysql的读写分离
在一个mysql集群中读写在不同的节点上操作,会大大的提升当前mysql集群的工作效率。但是这非常考验中间件的计算能力,下面介绍一下常用的中间件。
数据库常见的中间件
Cobar
阿里 b2b 团队开发和开源的,属于 proxy 层方案,就是介于应用服务器和数据库服务器之间。应用程序通过 JDBC 驱动访问 Cobar 集群,Cobar 根据 SQL 和分库规则对 SQL 做分解,然后分发到 MySQL 集群不同的数据库实例上执行。早些年还可以用,但是最近几年都没更新了,基本没啥人用,差不多算是被抛弃的状态吧。而且不支持读写分离、存储过程、跨库 join 和分页等操作。
TDDL
淘宝团队开发的,属于 client 层方案。支持基本的 crud 语法和读写分离,但不支持 join、多表查询等语法。目前使用的也不多,因为还依赖淘宝的 diamond 配置管理系统。
Atlas
360 开源的,属于 proxy 层方案,以前是有一些公司在用的,但是确实有一个很大的问题就是社区最新的维护都在 5 年前了。所以,现在用的公司基本也很少了。
Sharding-jdbc
当当开源的,属于 client 层方案,目前已经更名为 ShardingSphere(后文所提到的 Sharding-jdbc,等同于 ShardingSphere)。确实之前用的还比较多一些,因为 SQL 语法支持也比较多,没有太多限制,而且截至 2019.4,已经推出到了 4.0.0-RC1 版本,支持分库分表、读写分离、分布式 id 生成、柔性事务(最大努力送达型事务、TCC 事务)。而且确实之前使用的公司会比较多一些(这个在官网有登记使用的公司,可以看到从 2017 年一直到现在,是有不少公司在用的),目前社区也还一直在开发和维护,还算是比较活跃,个人认为算是一个现在也可以选择的方案。
mycat
Mycat 基于 Cobar 改造的,属于 proxy 层方案,支持的功能非常完善,而且目前应该是非常火的而且不断流行的数据库中间件,社区很活跃,也有一些公司开始在用了。但是确实相比于 Sharding jdbc 来说,年轻一些,经历的锤炼少一些。 主要介绍一下mycat是如何实现数据库的故障转移,和读写分离的
基于mycat的一些配置
进入mycat的conf目录下会有一些mycat的配置文件,下面我来一 一解读
server.xml
代码语言:javascript复制[root@10-42-99-103 conf]# vim server.xml
<!--<property name="processorExecutor">16</property>-->
<!--
<property name="mutiNodeLimitType">1</property> 0:开启小数量级(默认) ;1:开启亿级数
据排序
<property name="mutiNodePatchSize">100</property> 亿级数量排序批量
<property name="processors">32</property> <property name="processorExecutor">32</property>
<property name="serverPort">8066</property> <property name="managerPort">9066</property>
<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
</system>
<user name="root">
<property name="password">root</property>
<property name="schemas">mycatdb</property>
</user>
<!--<user name="user">
<property name="password">user</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>-->
<!--
<quarantine>
<whitehost>
<host host="127.0.0.1" user="mycat"/>
<host host="127.0.0.2" user="mycat"/>
</whitehost>
<blacklist check="false"></blacklist>
</quarantine>
-->
</mycat:server>
代码语言:javascript复制配置mycat作为服务器的一些资源配置,端口占用,线程数量
连接数量,登录用户,权限设置。
<system>
包含了mycat作为一个软件需要配置的所以系统信息
<property name="defaultSqlParser">druidparser</property>
默认的sql语句拦截器阿里的德鲁伊拦截器,maycat作为一个数据库
的中间件,需要对前端连接的sql语句进行拦截。
<property name="useCompression">1</property><!--1为开启mysql压缩协议-->
<user>
定义了可以访问登陆mycat的用户信息和权限
<quarantine>
mycat的防火墙可以进行安全级别的配置
<whitehost>
访问mycat的白名单(ip地址 和用户名)
<blacklist check="false"></blacklist>
sql语句的黑名单
可以定义什么sql语句可以被执行,什么样的sql语句不能被执行
schema.xml
代码语言:javascript复制<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://org.opencloudb/" >
<schema name="mycatdb" checkSQLschema="true" sqlMaxLimit="100">
<!--mstest.user demo01-->
<table name="t_order" primaryKey="order_id" dataNode="dn1,dn2"
rule="easymall-order">
<childTable name="t_order_item"
primaryKey="ID" joinKey="order_id" parentKey="order_id"/>
</table>
</schema>
<!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"
/> -->
<dataNode name="dn1" dataHost="localhost1" database="easydb" />
<dataNode name="dn2" dataHost="localhost2" database="easydb" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<!--hostM1 HOSTM1S1 HOSTM1S2 HOSTM1S3
HOSTM2 HOSTM2S1 HOSTM2S2-->
<writeHost host="hostM2" url="10.42.167.114:3306" user="root"
password="root">
</writeHost>
</dataHost>
<dataHost name="localhost2" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="10.42.99.103:3306" user="root"
password="root">
</writeHost>
</dataHost>
代码语言:javascript复制<schema>
定义mycat中作为服务器给客户端使用的逻辑库,逻辑库中的数据
可以来源于一个真是库,也可以来源于多个真实库。
<schema name="mycatdb" checkSQLschema="true" sqlMaxLimit="100">
checkSQLschema:是否自动添加库名作为表的前缀
sqlMaxLimit="100"每次查询最多查100条数据
<table> 定义逻辑库中的逻辑表,逻辑表的名字必须与真实表的名字保持一致
<table name="company" primaryKey="ID" dataNode="dn3,dn2,dn1" rule="mod-long"/>
name:逻辑表的名字
primaryKey:逻辑表的主键默认为ID(不区分大小写)
dataNode:定义当前逻辑表的数据分片来源
代码语言:javascript复制使用mycat实现数据库读写分离,故障转移的高可用结构
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql"
dbDriver="native" switchType="1" slaveThreshold="100">
switchType(默认为1)可以-1也可以1,为1时支持故障转移,当主节点
发生故障宕机后会在writeHost中进行故障转移,实现数据库的高可用
writeType(默认为0)在当前dataHost的主从结构中,写操作只会在第一个writeHost中
进行。writeType=1:1.5版本后的mycat不推荐使用,因为在新版本中,读逻辑分离
开来由balance控制,如果writeType=1覆盖掉读逻辑,读写都随机在所有
的writeHost中完成,写操作会随机的在所有的writeHost中进行
读逻辑控制:(balance)
balance可以有0,1,2,3四个值当balance的值为0时(默认值也是0)不开启读的
分离,所有读操作都在第一个writeHost中进行。当balance的值为1时,开启读的分离
所有读操作大量分散在除第一个writeHost之外的所有writeHost和readHost中,当并发量
很大时,第一个writeHost也会分担一部分。当balance的值为2时读操作随机到所有的host
中执行。当balance的值为3时,所有的读操作都会分散到readHost中去,如果不存在readHost
则读操作会到第一个writeHost中去执行。
一般来讲,常见的读写分离配置为writeType=0,balance=1
使用mycat实现数据库的分布式结构(分片表格)
可以在schema.xml中的table标签中的dataNode属性中配置不同的数据分片
再根据rule属性配置相应的分片的匹配规则从而达到数据库的分布式的效果
rule.xml
为了实现mycat在不同数据分片中的数据一致性,mycat在rule.xml中定义了很多分片计算的规则。
全局表
因为mycat不支持跨分片查询,所以当有了多表联查的情景出现时 可能因为关联的表分散在两个不同的分片,可能出现数据丢失的 情况。当被关联的表的量不是很大,变化也不是很频繁的数据稳定 的情况下,可以把这张表放在所有的分片中,这张表就叫做全局表
全局表的特点
数据稳定,变化不大,量不大的工具字典表。
全局表的实现
在schema.xml的文件中定义逻辑表的时候不是用rule属性 改为 type="global"即可。
代码语言:javascript复制<table name="cat_test" primaryKey="ID" type="global"
dataNode="dn1,dn2"/>
ER分片表
思考全局表的概念,如果被关联的的表的量很大,数据变动很大 怎么办被关联的表还能用作全局表吗,如果可以,那么我们将表 进行水平拆分放在不同的库还有什么意义?所以就有了ER分片表的 概念,连接ER分片表之前要先理解什么是主表,什么是子表,子表 的数据是基于主表而存在,假设有两张表,一张表是北京市的父亲表 (父亲id,父亲姓名),另外一张表是北京市的儿子表(儿子id,儿子姓名) 为了使两张表产生关联,父亲id作为主键放到儿子表中。这时候儿子 表是基于父亲表存在的,如果父亲表没有了,就没有了关联的意义, 父亲表就是主表,儿子表就是子表,在使用分片计算逻辑时,对父亲 id进行运算就OK了,这就是ER分片表的设计思路。
ER分片表的实现
在schema.xml文件中使用如下配置定义逻辑表即可
代码语言:javascript复制<schema name="mycatdb" checkSQLschema="true" sqlMaxLimit="100">
<!--mstest.user demo01-->
<table name="father" primaryKey="father_id" dataNode="dn1,dn2"
rule="father-child"> <!在rule.xml中将hash一致性算法的逻辑复置一份即可>
<childTable name="child"
primaryKey="ID" joinKey="father_id" parentKey="father_id"/>
</table>
</schema>