MyBatis-Plus的插⼊操作-数据库分库分表-MP的主键策略
⽬录
⼀插⼊操作
@SpringBootTest
public class CRUDTests {
@Autowired
private UrMapper urMapper;
@Test
public void testInrt(){
Ur ur = new Ur();
ur.tName("cakin24");
ur.tEmail("");
ur.tAge(18);
//返回值:影响的⾏数
int result = urMapper.inrt(ur);
System.out.println("影响的⾏数:" + result); // 影响的⾏数
System.out.println("ur id:" + ur.getId()); // id⾃动回填
}
}
测试结果:
JDBC Connection [HikariProxyConnection@775911842 sql.cj.jdbc.ConnectionImpl@1460c81d] will not be managed by Spring
==> Preparing: INSERT INTO ur ( id, name, age, email, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ? )
==> Parameters: 1327500521547415554(Long), cakin24(String), 18(Integer), (String), 2020-11-14 14:36:02.763(Timestamp), 2020-11-14 1 <== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.ssion.defaults.DefaultSqlSession@63fdffcd]
影响的⾏数:1
ur id:1327500521547415554
注意:数据库插⼊id值默认为:全局唯⼀id
⼆数据库分库分表策略
背景
随着业务规模的不断扩⼤,需要选择合适的⽅案去应对数据规模的增长,以应对逐渐增长的访问压⼒和数据量。
数据库的扩展⽅式主要包括:业务分库、主从复制,数据库分表。
1 业务分库
业务分库指的是按照业务模块将数据分散到不同的数据库服务器。
例如,⼀个简单的电商⽹站,包括⽤户、商品、订单三个业务模块,我们可以将⽤户数据、商品数据、订单数据分开放到三台不同的数据库
服务器上,⽽不是将所有数据都放在⼀台数据库服务器上。这样的就变成了3个数据库同时承担压⼒,系统的吞吐量⾃然就提⾼了。
虽然业务分库能够分散存储和访问压⼒,但同时也带来了新的问题,接下来我进⾏详细分析。
join 操作问题:业务分库后,原本在同⼀个数据库中的表分散到不同数据库中,导致⽆法使⽤ SQL 的 join 查询。
事务问题:原本在同⼀个数据库中不同的表可以在同⼀个事务中修改,业务分库后,表分散到不同的数据库中,⽆法通过事务统⼀修改。成本问题:业务分库同时也带来了成本的代价,本来 1 台服务器搞定的事情,现在要 3 台,如果考虑备份,那就是 2 台变成了 6 台。
2 主从复制和读写分离
读写分离的基本原理是将数据库读写操作分散到不同的节点上。读写分离的基本实现是:
数据库服务器搭建主从集群,⼀主⼀从、⼀主多从都可以。
数据库主机负责读写操作,从机只负责读操作。
数据库主机通过复制将数据同步到从机,每台数据库服务器都存储了所有的业务数据。
业务服务器将写操作发给数据库主机,将读操作发给数据库从机。
注意:这⾥⽤的是“主从集群”,⽽不是“主备集群”。“从机”的“从”可以理解为“仆从”,仆从是要帮主⼈⼲活的,“从机”是需要提供读数据的功能的;⽽“备机”⼀般被认为仅仅提供备份功能,不提供访问功能。所以使⽤“主从”还是“主备”,是要看场景的,这两个词并不是完全等同。
3 数据库分表
将不同业务数据分散存储到不同的数据库服务器,能够⽀撑百万甚⾄千万⽤户规模的业务,但如果业务继续发展,同⼀业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如,淘宝的⼏亿⽤户数据,如果全部存放在⼀台数据库服务器的⼀张表中,肯定是⽆法满⾜性能要求的,此时就需要对单表数据进⾏拆分。
单表数据拆分有两种⽅式:垂直分表和⽔平分表。⽰意图如下:
单表进⾏切分后,是否要将切分后的多个表分散在不同的数据库服务器中,可以根据实际的切分效果
来确定。如果性能能够满⾜业务要求,是可以不拆分到多台数据库服务器的,毕竟我们在上⾯业务分库的内容看到业务分库也会引⼊很多复杂性的问题。分表能够有效地分散存储压⼒和带来性能提升,但和分库⼀样,也会引⼊各种复杂性:
3.1 垂直分表
垂直分表适合将表中某些不常⽤且占了⼤量空间的列拆分出去。
例如,前⾯⽰意图中的 nickname 和 description 字段,假设我们是⼀个婚恋⽹站,⽤户在筛选其他⽤户的时候,主要是⽤ age 和 x 两个字段进⾏查询,⽽ nickname 和 description 两个字段主要⽤于展⽰,⼀般不会在业务查询中⽤到。description 本⾝⼜⽐较长,因此我们可以将这两个字段独⽴到另外⼀张表中,这样在查询 age 和 x 时,就能带来⼀定的性能提升。
3.2 ⽔平分表
⽔平分表适合表⾏数特别⼤的表,有的公司要求单表⾏数超过 5000 万就必须进⾏分表,这个数字可以作为参考,但并不是绝对标准,关键还是要看表的访问性能。对于⼀些⽐较复杂的表,可能超过 1000 万就要分表了;⽽对于⼀些简单的表,即使存储数据超过 1 亿⾏,也可以不分表。
但不管怎样,当看到表的数据量达到千万级别时,作为架构师就要警觉起来,因为这很可能是架构的
性能瓶颈或者隐患。
⽔平分表相⽐垂直分表,会引⼊更多的复杂性,例如数据id:
3.2.1 主键⾃增:
以最常见的⽤户 ID 为例,可以按照 1000000 的范围⼤⼩进⾏分段,1 ~ 999999 放到表 1中,1000000 ~ 1999999 放到表2中,以此类推。
复杂点:分段⼤⼩的选取。分段太⼩会导致切分后⼦表数量过多,增加维护复杂度;分段太⼤可能会导致单表依然存在性能问题,⼀般建议分段⼤⼩在 100 万⾄ 2000 万之间,具体需要根据业务选取合适的分段⼤⼩。
优点:可以随着数据的增加平滑地扩充新的表。例如,现在的⽤户是 100 万,如果增加到 1000 万,只需要增加新的表就可以了,原有的数据不需要动。
缺点:分布不均匀,假如按照 1000 万来进⾏分表,有可能某个分段实际存储的数据量只有 100万 条,⽽另外⼀个分段实际存储的数据量有 900 万条。
3.2.2 Hash
同样以⽤户 ID 为例,假如我们⼀开始就规划了 10 个数据库表,路由算法可以简单地⽤ ur_id % 10 的值来表⽰数据所属的数据库表编号,ID 为 985 的⽤户放到编号为 5 的⼦表中,ID 为 10086 的⽤户放到编号为 6 的字表中。
复杂点:初始表数量的选取。表数量太多维护⽐较⿇烦,表数量太少⼜可能导致单表性能存在问题。
优点:表分布⽐较均匀。
缺点:扩充新的表很⿇烦,所有数据都要重分布。
3.2.3 雪花算法——分布式ID⽣成器
雪花算法是由Twitter公布的分布式主键⽣成算法,它能够保证不同表的主键的不重复性,以及相同表的主键的有序性。
核⼼思想:
长度共64bit(⼀个long型)。
⾸先是⼀个符号位,1bit标识,由于long基本类型在Java中是带符号的,最⾼位是符号位,正数是0,负数是1,所以id⼀般是正数,最⾼位是0。
41bit时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于69.73年。
10bit作为机器的ID(5个bit是数据中⼼,5个bit的机器ID,可以部署在1024个节点)。
12bit作为毫秒内的流⽔号(意味着每个节点在每毫秒可以产⽣ 4096 个 ID)。
优点:整体上按照时间⾃增排序,并且整个分布式系统内不会产⽣ID碰撞,并且效率较⾼。
三 MP的主键策略
1 ASSIGN_ID
MyBatis-Plus默认的主键策略是:ASSIGN_ID (使⽤了雪花算法)
@TableId(type = IdType.ASSIGN_ID)
private String id;
2 AUTO ⾃增策略
需要在创建数据表的时候设置主键⾃增
实体字段中配置 @TableId(type = IdType.AUTO)
@TableId(type = IdType.AUTO)
private String id;
要想影响所有实体的配置,可以设置全局主键配置
# 全局设置主键⽣成策略
mybatis-plus.global-config.db-config.id-type=auto