#1. 数据分片概述
数据库分布式核心内容无非就是数据切分(Sharding),以及切分后对数据的定位
数据切分本质:数据切分实际上就是将数据分散存储到多个数据库中,使单一数据库中的数据量变小,通过扩充主机的数量缓解单一数据库的性能问题,从而达到提升数据库操作性能的目的
数据切分的两种方式:
- 纵向(垂直)切分
- 横向(水平)切分
##1.1 垂直切分
垂直切分的种类:
- 垂直分库
- 垂直分表
垂直分库:就是根据业务耦合性,将关联度低的不同表存储在不同的数据库。它类似微服务拆分,按不同的业务分类进行独立划分模块,每一个微服务模块使用单独的数据库
垂直分表:基于数据库中的"列"进行,某个字段较多,可以新建一张拓展表,将不经常或字段长度较大的字段拆分出去到拓展表中。在字段很多的情况下(如一个大表有100个字段),通过"大表拆小表",便于开发和维护
注:MySQL底层是通过数据页存储的,一条记录占用空间过大会导致跨页,造成额外的性能开销。另外数据库以行为单位将数据加载到内存中,这样表中字段长度较短且访问频率较高,内存能加载更多的数据,命中率更高,减少了磁盘IO
垂直切分的优点:
- 解决业务系统层面的耦合,业务明确清晰
- 与微服务系统治理类似,也能对不同业务数据进行分级管理、维护、监控、拓展等
- 高并发场景下,垂直切分一定程度上提升IO、数据库连接数、单机硬件资源的瓶颈
缺点:
- 部分表无法join,只能通过接口聚合方式解决,提高了并发的复杂度
- 分布式事务处理复杂
- 依然存在单表数据量过大的问题(需要进行水平拆分)
##1.2 水平拆分
当一个应用难以再细粒度的垂直切分,或者切分后数据量行数巨大,存在单库读写、存储性能瓶颈,这时候就需要进行水平切分
水平切分分类:
- 库内分表
- 分库分表
这个分类根据表内数据内在的逻辑关系,将同一个表按不同条件分散到多个数据库或者多个表中,每个表中只包含一部分数据,从而使得单表数据量变小,提升性能
库内分表:在本数据库内部进行分表,只能解决单一表数据量过大的问题。因为没有将表发布到不同机器的数据库中,因此对于减轻MySQL数据库压力来说,帮助不是很大
分库分表:在生产环境中最常用的方式,将大表中的数据按照指定规则进行切分到到子表,表分布到不同的数据库中
水平切分的优点:
- 不存在单库数据量过大、高并发的性能瓶颈,提高系统稳定性和负载能力
- 应用端改造小,不需要拆分业务模块
缺点:
- 跨分片的事务一致性难以保证
- 跨库的join关联查询性能较差
- 数据多次拓展难度和维护量极大
#2. MyCat垂直拆分
##2.1 案例场景
垂直拆分的含义:一种是按照不同的表(或者Schema)来切分到不同的数据库之上,垂直分库按照表的不同服务进行拆分,垂直分表按照表的字段进行拆分
现在一个外卖系统,它后端数据存储在单库中,现在对这个单库数据进行垂直拆分
现在按照服务区分垂直拆分成三个库:
- 用户系统库(user)
- 订单系统库(order)
- 餐品系统库(cuisine)
##2.2 准备工作
准备三台服务器分别配置好MySQL并导入对应的系统库:
IP
|
MySQL |
MyCat
|
192.168.68.133
|
用户系统库(user)
|
安装配置
|
192.168.68.134
|
订单系统库(order)
|
无
|
192.168.68.151
|
餐品系统库(cuisine)
|
无
|
#导入文件到Linux系统命令 rz #导入数据到数据库 source sql文件位置
##2.3 MyCat配置
① schema配置文件:
<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <!--逻辑库--> <schema name="TAKEAWAY_DB" checkSQLschema="true" sqlMaxLimit="100"> <!--逻辑表--> <table name="address_book" dataNode="dn1" primaryKey="id" /> <table name="employee" dataNode="dn1" primaryKey="id" /> <table name="user" dataNode="dn1" primaryKey="id" /> <table name="orders" dataNode="dn2" primaryKey="id"/> <table name="order_detail" dataNode="dn2" primaryKey="id"/> <table name="category" dataNode="dn3" primaryKey="id"/> <table name="dish" dataNode="dn3" primaryKey="id"/> <table name="dish_flavor" dataNode="dn3" primaryKey="id"/> <table name="setmeal" dataNode="dn3" primaryKey="id"/> <table name="setmeal_dish" dataNode="dn3" primaryKey="id"/> <table name="shopping_cart" dataNode="dn3" primaryKey="id"/> </schema> <!--数据节点,绑定dataHost数据源和database数据库--> <dataNode name="dn1" dataHost="localhost1" database="user" /> <dataNode name="dn2" dataHost="localhost2" database="order_tb" /> <dataNode name="dn3" dataHost="localhost3" database="cuisine" /> <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <!--心跳检测--> <heartbeat>select user()</heartbeat> <!--writeHost数据写入节点,也就是数据源--> <writeHost host="hostM1" url="jdbc:mysql://192.168.68.133:3306" user="root" password="123456789"> </writeHost> </dataHost> <dataHost name="localhost2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="jdbc:mysql://192.168.68.134:3306" user="root" password="123456789"> </writeHost> </dataHost> <dataHost name="localhost3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="jdbc:mysql://192.168.68.151:3306" user="root" password="123456789"> </writeHost> </dataHost> </mycat:schema>
② server.xml配置文件:
<user name="root" defaultAccount="true"> <property name="password">123456789</property> <property name="schemas">TAKEAWAY_DB</property> </user> <user name="user"> <property name="password">123456789</property> <property name="schemas">TAKEAWAY_DB</property> <property name="readOnly">true</property> </user>
启动MyCat查看TAKEAWAY_DB的表:检查是否配置成功
mysql> show databases; +-------------+ | DATABASE | +-------------+ | TAKEAWAY_DB | +-------------+ 1 row in set (0.01 sec) mysql> use TAKEAWAY_DB; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show tables; +-----------------------+ | Tables in TAKEAWAY_DB | +-----------------------+ | address_book | | category | | dish | | dish_flavor | | employee | | orders | | order_detail | | setmeal | | setmeal_dish | | shopping_cart | | user | +-----------------------+ 11 rows in set (0.01 sec)
##2.4 测试
① 分别查询user表、order表、setmeal_dish表的数据
mysql> select * from user; Empty set (0.01 sec) mysql> select * from orders; Empty set (0.01 sec) mysql> select * from setmeal_dish; +---------------------+---------------------+---------------------+--------------+----------+--------+------+-----------------------+-----------------------+---------------------+---------------------+------------+ | id | setmeal_id | dish_id | name | price | copies | sort | create_time | update_time | create_user | update_user | is_deleted | +---------------------+---------------------+---------------------+--------------+----------+--------+------+-----------------------+-----------------------+---------------------+---------------------+------------+ | 1415580119052894209 | 1415580119015145474 | 1397862198033297410 | 老火靓汤 | 49800.00 | 1 | 0 | 2021-07-15 15:52:55.0 | 2021-07-15 15:52:55.0 | 1415576781934608386 | 1415576781934608386 | 0 | | 1415580119061282817 | 1415580119015145474 | 1413342036832100354 | 北冰洋 | 500.00 | 1 | 0 | 2021-07-15 15:52:55.0 | 2021-07-15 15:52:55.0 | 1415576781934608386 | 1415576781934608386 | 0 | | 1415580119069671426 | 1415580119015145474 | 1413385247889891330 | 米饭 | 200.00 | 1 | 0 | 2021-07-15 15:52:55.0 | 2021-07-15 15:52:55.0 | 1415576781934608386 | 1415576781934608386 | 0 | +---------------------+---------------------+---------------------+--------------+----------+--------+------+-----------------------+-----------------------+---------------------+---------------------+------------+ 3 rows in set (0.00 sec)
② 插入数据到employee表、setmeal_dish表
mysql> INSERT INTO `employee` VALUES ('2', '普通用户', 'KXJ', 'e10adc3949ba59abbe56e057f20f883e', '13812312312', '1', '110101199001010047', '1', '2022-7-15 17:20:07', '2022-7-15 17:20:07', '1', '1'); Query OK, 1 row affected (0.05 sec) OK! mysql> INSERT INTO `setmeal_dish` VALUES ('14155801191231231', '1415123312122145474', '1397862198033297410', '蛙来了', '49800.00', '1', '0', '2022-07-15 15:52:55', '2022-07-15 15:52:55', '1415576781934608386', '1415576781934608386', '0'); Query OK, 1 row affected (0.00 sec) OK!
③ 跨库分片连接查询
mysql> SELECT o.id,o.number,o.status,o.pay_method,o.amount,a.consignee,a.sex,a.detail FROM orders o,address_book a WHERE o.address_book_id=a.id; ERROR 1064 (HY000): invalid route in sql, multi tables found but datanode has no intersection sql:SELECT o.id,o.number,o.status,o.pay_method,o.amount,a.consignee,a.sex,a.detail FROM orders o,address_book a WHERE o.address_book_id=a.id
允许多表跨库查询时报错,原因是当前的SQL语句涉及到跨库的join操作
##2.5 全局表解决跨库分片查询
之前在进行orders表和address_book表进行连查时,因为order表属于order_tb库、address_book属于user库
使用全局表来解决跨库问题:把address_book设置成全局表
全局表配置步骤
- 把需要设置的全局表导入到各个节点数据库
- 在schema.xml进行全局表配置
① 全局表导入到各个节点数据库
mysql> source /root/user.sql Query OK, 0 rows affected (0.00 sec) ………… Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> show tables; +--------------------+ | Tables_in_order_tb | +--------------------+ | address_book | | employee | | order_detail | | orders | | user | +--------------------+ 5 rows in set (0.00 sec)
② 配置:在上面的schema.xml进行一部分修改即可
<!--逻辑表--> <table name="address_book" dataNode="dn1,dn2,dn3" type="global" primaryKey="id" /> <table name="employee" dataNode="dn1,dn2,dn3" type="global" primaryKey="id" /> <table name="user" dataNode="dn1,dn2,dn3" type="global" primaryKey="id" />
配置成功全局表之后再次进行查询:
mysql> SELECT o.id,o.number,o.status,o.pay_method,o.amount,a.consignee,a.sex,a.detail FROM orders o,address_book a WHERE o.address_book_id=a.id; +------+----------------------+--------+------------+--------+-----------+------+-----------+ | id | number | status | pay_method | amount | consignee | sex | detail | +------+----------------------+--------+------------+--------+-----------+------+-----------+ | 1 | e1398090003262255106 | 2 | 1 | 19.20 | ?? | 1 | ????????? | +------+----------------------+--------+------------+--------+-----------+------+-----------+ 1 row in set (0.01 sec)
#3. MyCat水平拆分
##3.1 案例场景
水平拆分的含义:根据表中数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面
需求:现在有一张外卖系统的菜品表(dish),因为数据量很大,需要进行水平拆分,将数据分别保持到三个dataNode中
准备好三台服务器和垂直拆分是一样的
##3.2 前期准备
IP
|
MySQL
|
MyCat
|
192.168.68.133
|
cuis(餐品系统库) |
安装配置
|
192.168.68.134
|
cuis(餐品系统库)
|
无
|
192.168.68.151
|
cuis(餐品系统库)
|
无
|
① 在三个节点数据库中新建一个cuis库:
CREATE DATABASE cuis;
② 再分别在库中新建dish表:
CREATE TABLE `dish` ( `id` bigint(20) NOT NULL COMMENT '主键', `name` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '菜品名称', `category_id` bigint(20) NOT NULL COMMENT '菜品分类id', `price` decimal(10,2) DEFAULT NULL COMMENT '菜品价格', `code` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '商品码', `image` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '图片', `description` varchar(400) COLLATE utf8_bin DEFAULT NULL COMMENT '描述信息', `status` int(11) NOT NULL DEFAULT '1' COMMENT '0 停售 1 起售', `sort` int(11) NOT NULL DEFAULT '0' COMMENT '顺序', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime NOT NULL COMMENT '更新时间', `create_user` bigint(20) NOT NULL COMMENT '创建人', `update_user` bigint(20) NOT NULL COMMENT '修改人', `is_deleted` int(11) NOT NULL DEFAULT '0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE, UNIQUE KEY `idx_dish_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='菜品管理';
##3.3 MyCat的配置
① schema.xml配置:rule分片规则为取模
<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <!--逻辑库--> <schema name="TAKEAWAY_DB" checkSQLschema="true" sqlMaxLimit="100"> <!--逻辑表--> <table name="dish" dataNode="dn1,dn2,dn3" primaryKey="id" rule="mod-long"/> </schema> <!--数据节点,绑定dataHost数据源和database数据库--> <dataNode name="dn1" dataHost="localhost1" database="cuis" /> <dataNode name="dn2" dataHost="localhost2" database="cuis" /> <dataNode name="dn3" dataHost="localhost3" database="cuis" /> <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <!--心跳检测--> <heartbeat>select user()</heartbeat> <!--writeHost数据写入节点,也就是数据源--> <writeHost host="hostM1" url="jdbc:mysql://192.168.68.133:3306" user="root" password="123456789"> </writeHost> </dataHost> <dataHost name="localhost2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="jdbc:mysql://192.168.68.134:3306" user="root" password="123456789"> </writeHost> </dataHost> <dataHost name="localhost3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="jdbc:mysql://192.168.68.151:3306" user="root" password="123456789"> </writeHost> </dataHost> </mycat:schema>
<user name="root" defaultAccount="true"> <property name="password">123456789</property> <property name="schemas">TAKEAWAY_DB</property> </user> <user name="user"> <property name="password">123456789</property> <property name="schemas">TAKEAWAY_DB</property> <property name="readOnly">true</property> </user>
##3.4 测试
启动MyCat客户端并插入10条数据:
INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('1', '毛氏红烧肉', '1397844263642378242', '6800.00', '123412341234', '0a3b3288-3446-4420-bbff-f263d0c02d8e.jpg', '毛氏红烧肉毛氏红烧肉,确定不来一份?', '1', '0', '2021-05-27 09:40:19', '2021-05-27 09:40:19', '1', '1', '0'); INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('2', '组庵鱼翅', '1397844263642378242', '4800.00', '123412341234', '740c79ce-af29-41b8-b78d-5f49c96e38c4.jpg', '组庵鱼翅,看图足以表明好吃程度', '1', '0', '2021-05-27 09:41:19', '2021-05-27 09:41:19', '1', '1', '0'); INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('3', '霸王别姬', '1397844263642378242', '12800.00', '123412341234', '057dd338-e487-4bbc-a74c-0384c44a9ca3.jpg', '还有什么比霸王别姬更美味的呢?', '1', '0', '2021-05-27 09:43:08', '2021-05-27 09:43:08', '1', '1', '0'); INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('4', '全家福', '1397844263642378242', '11800.00', '23412341234', 'a53a4e6a-3b83-4044-87f9-9d49b30a8fdc.jpg', '别光吃肉啦,来份全家福吧,让你长寿又美味', '1', '0', '2021-05-27 09:44:08', '2021-05-27 09:44:08', '1', '1', '0'); INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('5', '邵阳猪血丸子', '1397844263642378242', '13800.00', '1246812345678', '2a50628e-7758-4c51-9fbb-d37c61cdacad.jpg', '看,美味不?来嘛来嘛,这才是最爱吖', '1', '0', '2021-05-27 09:45:12', '2021-05-27 09:45:12', '1', '1', '0'); INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('6', '口味蛇', '1397844263642378242', '16800.00', '1234567812345678', '0f4bd884-dc9c-4cf9-b59e-7d5958fec3dd.jpg', '爬行界的扛把子,东兴-口味蛇,让你欲罢不能', '1', '0', '2021-05-27 09:46:23', '2021-05-27 09:46:23', '1', '1', '0'); INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('7', '辣子鸡丁', '1397844303408574465', '8800.00', '2346812468', 'ef2b73f2-75d1-4d3a-beea-22da0e1421bd.jpg', '辣子鸡丁,辣子鸡丁,永远的魂', '1', '0', '2021-05-27 09:49:16', '2021-05-27 09:49:16', '1', '1', '0'); INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('8', '麻辣兔头', '1397844303408574465', '19800.00', '123456787654321', '2a2e9d66-b41d-4645-87bd-95f2cfeed218.jpg', '麻辣兔头的详细制作,麻辣鲜香,色泽红润,回味悠长', '1', '0', '2021-05-27 09:52:24', '2021-05-27 09:52:24', '1', '1', '0'); INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('9', '蒜泥白肉', '1397844303408574465', '9800.00', '1234321234321', 'd2f61d70-ac85-4529-9b74-6d9a2255c6d7.jpg', '多么的有食欲啊', '1', '0', '2021-05-27 09:54:30', '2021-05-27 09:54:30', '1', '1', '0'); INSERT INTO `dish`(id,name,category_id,price,code,image,description,status,sort,create_time,update_time,create_user,update_user,is_deleted) VALUES ('10', '鱼香肉丝', '1397844303408574465', '3800.00', '1234212321234', '8dcfda14-5712-4d28-82f7-ae905b3c2308.jpg', '鱼香肉丝简直就是我们童年回忆的一道经典菜,上学的时候点个鱼香肉丝盖饭坐在宿舍床上看着肥皂剧,绝了!现在完美复刻一下上学的时候感觉', '1', '0', '2021-05-27 09:55:13', '2021-05-27 09:55:13', '1', '1', '0');
结果:按照取模均匀的分布再各个存储节点
Comments | NOTHING
Warning: Undefined variable $return_smiles in /www/wwwroot/wql_luoqin_ltd/wp-content/themes/Sakura/functions.php on line 1109