雪花算法
数据库的扩展方式主要包括:业务分库,主从复制,数据库分表
数据库分表
如果业务持续发展,同一业务的单表数据也会达到数据库服务器的处理瓶颈;例如淘宝几亿用户数据,因此需要对单表数据进行拆分。
垂直分表
列也比较多,查询量比较大的时候,通常把经常被查询的字段 和 数据量比较大的字段,拆分到不同的表中,比如age,sex 主要是查询使用,nickname昵称字段和描述字段主要用于展示使用而且本身还比较长,可以将后面两个字段独立到另一个表中去,这样查询的age 和 sex时 能带来一定的性能提升,相当于对表垂直切了一刀,把非主要查询的这两个字段独立出去。也就是把大字段或一般查询用不到的字段分离到另外一张表中。user user_info 用 id进行关联,user id字段自增长,user_info id字段不设策略,user的id是啥 我就是啥
主键一对一关联;
水平分表
适合表行数特别大的表,数据量特别大的时候,也没有明确查询字段之类的需求,关键还要看表的访问性能,一些比较复杂的表,可能超过1000万就要分表了,按数据分表,多少万条数据在一个表上,多少行数据在一个表上,相当于横着切一刀 分出去。水平分表相比垂直分表会引入更多的复杂性。
复杂性体现:主键自增问题 如果分2张表,按照主键范围存在不同的表上,比如1~1千万,1千万零一~2千万,分别在两张表上,如果满了 扩展容易,再加即可,实现了动态扩展,各表内数据量有多有少,现在一般都是负载均衡,会导致服务器访问不均衡。如果解决均衡问题,可以将表的主键起始id分表设为1、 2、 3 步长为3,这样就均衡了,但是扩展又会出问题,如果扩展又会出现数据迁移问题,重新分配布局。两种方式扩展都可能会遇到之前的表里面有数据被删除,然后后面的数据主键自增已经占用了新的主键,扩展的id并不是理论上应该存的id,而是之前的表可能已经用掉了。会不安全
解决办法:
Hash:取模运算,放到哪一个表中,要求初始表数量确定,表数量太多维护比较麻烦,太少又可能导致单表性能出现问题,表分布会比较均匀,但是扩展会很麻烦,所有数据都要重分布,uuid不能和mysql的聚簇索引一起用,不利于mysql查询优化
雪花算法snow flake(分布式id占19个符号位):mybatis-plus中默认的主键策略就是雪花算法
分布式主键生成算法,它能够保证不同表的主键不重复性,以及相同表的主键的有序性
核心思想:
长度64bit一个Long型,分四个部分:
1.第一个位置,符号位,为0,表示为正数,一般都不会用负数
2.第二个位置,时间戳位,41bit,存储的是差值,约定于69.73年,这样生成的时间都是不一样的
3.第三个位置,5bit是数据中心(相当于机房代码),5bit机器ID(表示数据中心存储数据的机器,可以部署在1024个节点,相当于1024台机器),
4.第四个位置,12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),支持并发量很大
mybatis-plus 默认识别 名字叫“id”的字段,自动进行雪花算法插入数据库id,如果数据库是uid实体类也是uid字段, 或者其他则识别不了,无法进行默认的雪花算法插入主键到数据库,会报错。所以此时需要在实体类的字段上加@TableId 告诉mybatis-plus这个字段是id 字段(数据库中是uid);
@TableName(value="t_user")
public class User {
//默认雪花算法 也可以设置
// @TableId(type= IdType.ASSIGN_ID) private String id; 也可以
@TableId
private Long uid;
或者,数据库名字叫uid,实体类叫做id,也无法完成映射,所以需要加value属性,如同@TableName注解
@TableName(value="t_user")
public class User {
//@TableId(value = "uid",type = IdType.AUTO) 主键自增策略
@TableId(value = "uid")
private Long id;
private String name;
private int age;
private String email;xxxxxxxxxx @TableId(value = "uid")private Long id;private String name;private int age;private String email;
业务中id为空,数据库中也没有设置id;id是主键,不能为空,所以在插入的时候就会报错。如果所有的表都要求主键自增,可以在配置文件中设置
#设置全局主键自增 mybatis-plus.global-config.db-config.id-type=auto
新增两个字段 ,如果类中的列名为驼峰命名
private LocalDateTime createTime; private LocalDateTime updateTime; //在数据库中字段为:create_time update_time mybatis-plus 会自动把驼峰进行转换
如果数据库中的字段名跟实体类不一致,需要用@TableField(value="")
@TableId(value = "uid",type = IdType.AUTO) private Long id; @TableField(value = "username") private String name; private int age; private String email;
数据库字段可进行自动填充,创建和更新时间 可以用CURRENT_TIMESTAMP 系统自动管理,更新字段,再勾上默认根据当前时间戳进行更新,这样管理就不需要每次业务里面涉及。也可以在业务层进行设计,自动填充功能,需要在实体类加注解来标识。
@TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) //插入或更新的时候自动填充 private LocalDateTime updateTime; //但是填充什么呢?需要定义实现自动填充功能 实现元对象处理器接口
@Slf4j
@Component
public class MyMeatObjectHandler implements metaObjectHandler {
@Override
public void insertFill(metaObject metaObject) {
log.info("insert自动填充....");
//插入数据的时候 识别到注解 @TableField(fill = FieldFill.INSERT) 就会进入这个方法来执行
this.strictInsertFill(metaObject,"createTime", LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject,"updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(metaObject metaObject) {
log.info("update自动填充。。。");
//更新的时候
this.strictUpdateFill(metaObject,"updateTime",LocalDateTime.class,LocalDateTime.now());
}
}
//判断当前对象自动填充属性是否包含 当前属性
boolean hasAuthor = metaObject.hasSetter("author");
//如果 有setter的方法 有这个author字段 进行自动填充author
if(hasAuthor) {
log.info("insert author属性");
this.strictInsertFill(metaObject, "author", String.class, "石头");
}
年龄也自动填充
@TableField(fill = FieldFill.INSERT) private Integer age;
log.info("age 填充为18");
//对age进行自动填充 当没传age的时候填充18进去
Object age = this.getFieldValByName("age", metaObject);
//如果业务层赋值了就不用去填充,如果没有赋值去进行填充
if(age==null){
this.strictInsertFill(metaObject, "age", Integer.class, 18);
}
@TableLogic 逻辑删除
物理删除:真实删除
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录使用场景,可以进行数据恢复
数据库中此字段 一般默认类型tinyint类型,默认1为已删除,0未删除
在数据库中创建逻辑删除状态列 同时实体字段一般前面不要加is有些框架识别可能出问题
//private Integer deleted; //逻辑删除字段 0表示false 1表述true @TableLogic @TableField(value = "is_deleted") private Boolean deleted;
执行记录 需要数据库中此字段 值为0
Preparing: UPDATE t_user SET is_deleted=1 WHERe uid=? AND is_deleted=0 ==> Parameters: 1499759447969390597(Long)<== Updates: 0
//自定义 1为未删除 -1为已删除 需要去配置 @TableLogic @TableField(value = "is_deleted") private Integer deleted;
mybatis-plus.global-config.db-config.logic-delete-field=deleted mybatis-plus.global-config.db-config.logic-delete-value=-1 mybatis-plus.global-config.db-config.logic-not-delete-value=1
分页插件
1.添加配置类 config包 可以将配置全部写在这个包内
springspring-boot
@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
//可将包扫描写在这个类里面 也可以写在启动类上
@Configuration
@MapperScan("mybatisplus.mapper")
public class MybatisPConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
针对mysql的分页配置类 写好了。进行测试
@SpringBootTest
public class IntercepterTest {
@Resource
private UserMapper userMapper;
@Test
public void test1(){
//新建分页参数对象 查询第几页,每页5条记录
Page userPage = new Page<>(2, 5);
//这两个对象是一个对象 参数为页对象 及qureyMapper条件构造器 先传null
//Page userPage1 = userMapper.selectPage(userPage, null);
//System.out.println("确认是否是一个对象"+(userPage==userPage1)); true
//优化为
userMapper.selectPage(userPage, null);
//当前页码下的所有记录
List users = userPage.getRecords();
users.forEach(System.out::println);
}
}
long total = userPage.getTotal();
System.out.println("总数+"+total);
boolean bn = userPage.hasNext();
System.out.println("下一页??"+bn);
boolean bp = userPage.hasPrevious();
System.out.println("上一页?"+bp);
在xml中如何使用配置分页?
//@Repository public interface UserMapper extends baseMapper{ //自定义一个方法 具体的xml实现在resources下面的mapper文件中 //扩展mapper List selectAllByName(String name); //第一个参数为分页对象 第二个为查询条件 根据年龄来查询用户 并分页展示 IPage selectPageVo(IPage> page, Integer age);
xml中不需要去进行 分页,只需要写条件即可,我们传递了page对象进去,MP会自动的去读取参数中的配置对象,且会作为整个查询语句的后缀,然后执行sql
@Test
public void selectByAge(){
Page userPage = new Page<>(2,6);
IPage userIPage = userMapp er.selectPageVo(userPage, 18);
List users = userIPage.getRecords();
users.forEach(System.out::println);
}
执行语句为:自动追加
Total: 1==> Preparing: select uid,name,age,email from t_user where age > ? LIMIT ?,?==> Parameters: 18(Integer), 6(Long), 6(Long)<== Columns: uid, name, age, email<== Row: 1500133241527472131, 建国2, 22, jiangu2o@qq.com<== Row: 1500133241527472134, 建国更新, 100, jiangu2o@qq.com<== Row: 1500133241527472139, 观海200, 200, jianguo5@qq.com



