数据库拆分主要有两种方式:垂直拆分和水平拆分
1:就是将一张表的字段拆分为两个两个表字段之和,它们之间的关系 类似上下级的关系 类比于垂直拆分,可以将使用频率高字段放在一张表中,将使用频率低的放入另外一张表中,这样有助于提高并发性
2.:水平拆分 就是一张表的数据过多,需要将一张表的数据放到多个库中的多个表中。这样做的目的是提高存储容量。
分表分库之,怎么查询过程
1: 整合数据库中间件 mycat 或者 shadingjdbc
2: 客户端代码中将表的名称替换成虚拟表的名称(虚拟表的名称是被我们数据库中间件改
写的),客户端发送 JDBC 语句 会被数据库中间件进行拦截
3: 获取到 条件后面的 分片字段 如果获取成功 则根据该
分片字段 计算 直接定位到该表----只会查询一次。
4: Sql 后面没有带上条件 分片字段 对每张表都会做操作,在将该结果
给数据库中间件整合后返回给客户端。
查询的过程中最好带上分片字段,不然对每张都会查询一次。
//首先,需要在mysql数据库建好你要分的表,例如我这里是0-7共8个order表,t_order_0到t_order_7
DROP TABLE IF EXISTS t_order0;
CREATE TABLE `t_order_0` (
`id` bigint(20) unsigned NOT NULL COMMENT '主键',
`price` decimal(10,2) NOT NULL COMMENT '单价',
`user_id` bigint(20) unsigned NOT NULL COMMENT '用户id',
`status` varchar(16) DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单0表';
DROP TABLE IF EXISTS t_order1;
CREATE TABLE `t_order_1` (
`id` bigint(20) unsigned NOT NULL COMMENT '主键',
`price` decimal(10,2) NOT NULL COMMENT '单价',
`user_id` bigint(20) unsigned NOT NULL COMMENT '用户id',
`status` varchar(16) DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单0表';
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>shardingjdbc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shardingjdbc</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring:
shardingsphere:
datasource:
names: ds0
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/ds0
username: root
password: root
sharding:
tables:
t_order:
table-strategy:
inline:
sharding-column: user_id
algorithm-expression: t_order_$->{user_id % 8}
## 雪花算法生成的id
key-generator:
column: id
type: SNOWFLAKE
## 打印sql
props:
sql:
show: true
分表分库策略
定义一个全局的 id。该全局 id 特点:有序且递增 推荐序列(雪花算法SNOWFLAKE)
分表分库之后分页如何查询
核心点 还是看分片字段 如果 sql 语句有带上分片字段的话
则只会对一张表来进行分页查询。
如果 sql 语句没有带上 分片字段的话 则会对每张表来做分页查询
将该结果交给我们的数据库中间件来整合 二次分页之后返回给客户端。
从sharding-jdbc看如何解决雪花算法的时钟回拨问题
1:什么是时钟回拨
我们的硬件时钟可能会因为各种原因变得不准( 快了 或 慢了 ),此时就需要 ntp 服务来做时间校准,做校准的时候就会发生服务器时钟的 跳跃 或者 回拨 的问题
2:雪花算法如何解决时钟回拨
服务器时钟回拨会导致产生重复的 ID,SNOWFLAKE 方案中对原有雪花算法做了改进,增加了一个最大容忍的时钟回拨毫秒数。
如果时钟回拨的时间超过最大容忍的毫秒数阈值,则程序直接报错;如果在可容忍的范围内,默认分布式主键生成器,会等待时钟同步到最后一次主键生成的时间后再继续工作。
最大容忍的时钟回拨毫秒数,默认值为 0,可通过属性 max.tolerate.time.difference.milliseconds 设置。
# 最大容忍的时钟回拨毫秒数
spring.shardingsphere.sharding.tables.t_order.key-generator.max.tolerate.time.difference.milliseconds=5
MySQL什么情况下不应建或少建索引
不适合使用索引的情况
1:在where条件中(包括group by以及order by)里用不到的字段不需要创建索引,索引的价值是快速定位,如果起不到定位的字段通常是不需要创建索引的。
2:数据量小的表最好不要使用索引
如果表中记录太少,比如少于1000个,那么是不需要创建索引的。表记录太少,是否创建索引对查询效率的影响并不大。甚至说,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化的效果。(了解:比如我们在一个表记录比较少的表上建立一个二级索引,那么查询的时候还需要经过索引,然后再进行回表那么这样消耗的时间可能比直接查询消耗的时间会更多)
3:有大量重复数据的列上不要建立索引
在条件表达式中经常用到的不同值较多的列上建立索引,但字段中如果有大量重复数据,也不用创建索引。比如在学生表中的性别字段上只有男和女两个不同的值,因此无须建立索引。如果建立索引,不但不会提高查询效率,反而会严重降低数据更新速度
索引的价值是帮你快速定位。如果想要定位的数据有很多,那么索引就失去了它的使用价值,比如通常情况下的性别字段。
说明:当数据重复度大,比如高于10%的时候,也不需要对这个字段使用索引
4:避免对经常更新的表创建过多的索引
①频繁更新的字段不一定要创建索引。因为更新数据的时候,也需要更新索引,如果索引太多,在更新索引的时候会造成负担,从而影响效率。
②避免对经常更新的表创建过多的索引,并且索引中的列尽可能少。此时,虽然提高了查询速度,同时却会降低更新 表的速度。
5:不建议使用无序的值作为索引
例如身份证、uuid(在索引比较时需要转为ASCII,并且插入时可能会造成页分裂,这些了解即可)、md5、hash、无序字符串等。
6:删除不再使用或者很少使用的索引
表中的数据被大量更新或者数据的使用方式被改变后原有的一些索引可能不再需要。数据库管理员应该定期找出这些索引,将他们删除,从而减少索引对更新操作的影响。
7:不要定义冗余或者重复的索引
①冗余索引
有时候有意或者无意的就对同一个列创建了多个索引,比如index(a,b,c)其相当于index(a) index(a,b) index(a.b,c) .冗余索引只会增加维护的成本,并不会对搜索有什么好处。
②重复索引
某个字段既是主键、又给它定义一个唯一索引,还给它定义一个普通索引,可是主键本身就会生成聚簇索引,所以定义的唯一索引和普通索引是重复的,这种情况应该避免
合创建索引的情况
1:字段的数值有唯一性的限制
具有唯一索引的字段,即使是组合字段,也必须建成唯一索引。唯一索引对 insert 的速度损耗可以忽略,但是提高的查找速度是明显的。
2:频繁作为 where 查询条件的字段
3:经常GROUP BY 或 ORDER BY的列
4:UPDATE、DELETE 的 WHERE 条件列
5:DISTINCT 字段需要创建索引
6:多表 JOIN 连接操作时,创建索引注意事项
首先,连接表的数量尽量不要超过三张,因为每增加一张表就相当于增加了一次嵌套的循环。数量级增长会非常快,严重影响查询的效率。
其次,对 WHERE 条件创建索引, 因为 WHERE 才是对数据过滤的条件。如果数据量非常大的情况下,没有 WHERE 条件过滤是非常可怕的。
最后,对用于连接的字段创建索引,并且该字段在多张表中的类型必须一致。比如 course_id 在两张表中都为 int(11) 类型,而不能一个为 int 另一个为varchar 类型。
7:使用列的类型小的创建索引
8:使用字符串前缀创建索引
在 VARCHAR 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。MySQL的LEFT()函数是一个字符串函数,它返回具有指定长度的字符串的左边部分。
LEFT(str, length);
9:区分度高(散列度高)的列适合作为索引
10:使用越频繁的列要放到联合索引的越左侧
11:在多个字段都要创建索引的情况下,联合索引优于单值索引
在实际工作中,我们也需要注意平衡。索引的数目不是越多越好。我们需要限制每张表上的索引数量,建议单张表索引数量不超过6个,原因如下:
1:每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大
2:索引会印象 INSERT, DELETE, UPDATE 等语句的性能,因为表中数据更改的同时,索引也会调整和更新,造成负担
3:优化器在选择如何优化查询时,会根据统一信息,对每一个可以用到的索引来进行评估。以生成出一个最好的计划。如果同时有多个索引都可以用于查询时,会增加Mysql优化器生成执行计划时间,降低查询性能。
自增和UUid差异
在Innodb中,采用的是B+数索引。Innodb的存储结构,是聚簇索引。对于聚簇索引顺序主键和随机主键的对效率的影响很大。
自增是顺序主键存储,查找和插入都很方便(插入会按顺序插到前一个的后面),但UUid是无序的,通过计算获得的hashcode也会是无序的(是按照hashcode选择存储位置)。
所以对于他的查找效率很低,而且因为他是无序的,他的插入有可能会插到前面的数据中,会造成很多其他的操作,很影响性能或者很多存储空间因为没有顺序的存储而被空缺浪费。