深圳哪家做网站最好,鞍山市人力资源招聘信息网,wordpress新写文章会卡死,东莞网站建设十大品牌乐观锁、雪花算法、MyBatis-Plus多数据源e雪花算法2、乐观锁a场景b乐观锁与悲观锁c模拟修改冲突d乐观锁实现流程eMybatis-Plus实现乐观锁七、通用枚举a数据库表添加字段sexb创建通用枚举类型c配置扫描通用枚举d测试九、多数据源1、创建…
乐观锁、雪花算法、MyBatis-Plus多数据源e雪花算法2、乐观锁a场景b乐观锁与悲观锁c模拟修改冲突d乐观锁实现流程eMybatis-Plus实现乐观锁七、通用枚举a数据库表添加字段sexb创建通用枚举类型c配置扫描通用枚举d测试九、多数据源1、创建数据库及表2、引入依赖3、配置多数据源4、创建用户service5、创建商品service6、测试申明 未经许可禁止以任何形式转载若要引用请标注链接地址。 全文共计9445字阅读大概需要3分钟 更多学习内容 欢迎关注我的个人公众号不懂开发的程序猿 相关阅读
MyBatis-Plus入门案例
MyBatis-Plus基本CRUD
MyBatis-Plus分页插件和MyBatisX插件
e雪花算法
背景
需要选择合适的方案去应对数据规模的增长以应对逐渐增长的访问压力和数据量。
数据库的扩展方式主要包括业务分库、主从复制数据库分表。
数据库分表
将不同业务数据分散存储到不同的数据库服务器能够支撑百万甚至千万用户规模的业务但如果业务 继续发展同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如淘宝的几亿用户数据 如果全部存放在一台数据库服务器的一张表中肯定是无法满足性能要求的此时就需要对单表数据进 行拆分。
单表数据拆分有两种方式垂直分表和水平分表。示意图如下 垂直分表
垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。
例如前面示意图中的 nickname 和 description 字段假设我们是一个婚恋网站用户在筛选其他用 户的时候主要是用 age 和 sex 两个字段进行查询而 nickname 和 description 两个字段主要用于展 示一般不会在业务查询中用到。description 本身又比较长因此我们可以将这两个字段独立到另外 一张表中这样在查询 age 和 sex 时就能带来一定的性能提升。
水平分表
水平分表适合表行数特别大的表有的公司要求单表行数超过 5000 万就必须进行分表这个数字可以 作为参考但并不是绝对标准关键还是要看表的访问性能。对于一些比较复杂的表可能超过 1000 万就要分表了而对于一些简单的表即使存储数据超过 1 亿行也可以不分表。
但不管怎样当看到表的数据量达到千万级别时作为架构师就要警觉起来因为这很可能是架构的性 能瓶颈或者隐患。
水平分表相比垂直分表会引入更多的复杂性例如要求全局唯一的数据id该如何处理 主键自增 ①以最常见的用户 ID 为例可以按照 1000000 的范围大小进行分段1 ~ 999999 放到表 1中 1000000 ~ 1999999 放到表2中以此类推。
②复杂点分段大小的选取。分段太小会导致切分后子表数量过多增加维护复杂度分段太大可能会 导致单表依然存在性能问题一般建议分段大小在 100 万至 2000 万之间具体需要根据业务选取合适 的分段大小。
③优点可以随着数据的增加平滑地扩充新的表。例如现在的用户是 100 万如果增加到 1000 万 只需要增加新的表就可以了原有的数据不需要动。
④缺点分布不均匀。假如按照 1000 万来进行分表有可能某个分段实际存储的数据量只有 1 条而 另外一个分段实际存储的数据量有 1000 万条。 取模 ①同样以用户 ID 为例假如我们一开始就规划了 10 个数据库表可以简单地用 user_id % 10 的值来 表示数据所属的数据库表编号ID 为 985 的用户放到编号为 5 的子表中ID 为 10086 的用户放到编号 为 6 的子表中。
②复杂点初始表数量的确定。表数量太多维护比较麻烦表数量太少又可能导致单表性能存在问题。
③优点表分布比较均匀。
④缺点扩充新的表很麻烦所有数据都要重分布。 雪花算法 雪花算法是由Twitter公布的分布式主键生成算法它能够保证不同表的主键的不重复性以及相同表的 主键的有序性。
①核心思想
长度共64bit一个long型。
首先是一个符号位1bit标识由于long基本类型在Java中是带符号的最高位是符号位正数是0负 数是1所以id一般是正数最高位是0。
41bit时间截(毫秒级)存储的是时间截的差值当前时间截 - 开始时间截)结果约等于69.73年。
10bit作为机器的ID5个bit是数据中心5个bit的机器ID可以部署在1024个节点。
12bit作为毫秒内的流水号意味着每个节点在每毫秒可以产生 4096 个 ID。 ②优点整体上按照时间自增排序并且整个分布式系统内不会产生ID碰撞并且效率较高。
2、乐观锁
a场景 一件商品成本价是80元售价是100元。老板先是通知小李说你去把商品价格增加50元。小 李正在玩游戏耽搁了一个小时。正好一个小时后老板觉得商品价格增加到150元价格太 高可能会影响销量。又通知小王你把商品价格降低30元。 此时小李和小王同时操作商品后台系统。小李操作的时候系统先取出商品价格100元小王 也在操作取出的商品价格也是100元。小李将价格加了50元并将10050150元存入了数据 库小王将商品减了30元并将100-3070元存入了数据库。是的如果没有锁小李的操作就 完全被小王的覆盖了。 现在商品价格是70元比成本价低10元。几分钟后这个商品很快出售了1千多件商品老板亏1 万多。 b乐观锁与悲观锁 上面的故事如果是乐观锁小王保存价格前会检查下价格是否被人修改过了。如果被修改过 了则重新取出的被修改后的价格150元这样他会将120元存入数据库。 如果是悲观锁小李取出数据后小王只能等小李操作完之后才能对价格进行操作也会保证 最终的价格是120元。 c模拟修改冲突
数据库中增加商品表
CREATE TABLE product
(
id BIGINT(20) NOT NULL COMMENT 主键ID,
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT 商品名称,
price INT(11) DEFAULT 0 COMMENT 价格,
VERSION INT(11) DEFAULT 0 COMMENT 乐观锁版本号,
PRIMARY KEY (id)
);添加数据
INSERT INTO t_product (id, NAME, price) VALUES (1, 外星人笔记本, 100);添加实体
package com.jerry.mybatisplus.entity;import lombok.Data;Data
public class Product {private Long id;private String name;private Integer price;private Integer version;
}添加mapper
Mapper
public interface ProductMapper extends BaseMapperProduct {
}测试 Autowiredprivate ProductMapper productMapper;Testpublic void testConcurrentUpdate() {//1、小李Product p1 productMapper.selectById(1L);System.out.println(小李取出的价格 p1.getPrice());//2、小王Product p2 productMapper.selectById(1L);System.out.println(小王取出的价格 p2.getPrice());//3、小李将价格加了50元存入了数据库p1.setPrice(p1.getPrice()50);int result1 productMapper.updateById(p1);System.out.println(小李修改结果 result1);//4、小王将商品减了30元存入了数据库p2.setPrice(p2.getPrice()-30);int result2 productMapper.updateById(p2);System.out.println(小王修改结果 result2);//最后的结果Product p3 productMapper.selectById(1L);//价格覆盖最后的结果70System.out.println(最后的结果 p3.getPrice());}d乐观锁实现流程 数据库中添加version字段 取出记录时获取当前version SELECT id,name,price,version FROM product WHERE id1SELECT id,name,price,version FROM product WHERE id1 UPDATE product SET priceprice50, versionversion 1 WHERE id1 AND
version1eMybatis-Plus实现乐观锁
修改实体类
package com.jerry.mybatisplus.entity;import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
Data
public class Product {private Long id;private String name;private Integer price;Versionprivate Integer version;
}添加乐观锁插件配置
MapperScan(com.jerry.mybatisplus.mapper)
Configuration
public class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();//添加分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//添加乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}优化流程 Testpublic void testConcurrentVersionUpdate() {//小李取数据Product p1 productMapper.selectById(1L);//小王取数据Product p2 productMapper.selectById(1L);//小李修改 50p1.setPrice(p1.getPrice() 50);int result1 productMapper.updateById(p1);System.out.println(小李修改的结果 result1);//小王修改 - 30p2.setPrice(p2.getPrice() - 30);int result2 productMapper.updateById(p2);System.out.println(小王修改的结果 result2);if (result2 0) {//失败重试重新获取version并更新p2 productMapper.selectById(1L);p2.setPrice(p2.getPrice() - 30);result2 productMapper.updateById(p2);}System.out.println(小王修改重试的结果 result2);//老板看价格Product p3 productMapper.selectById(1L);System.out.println(老板看价格 p3.getPrice());}七、通用枚举 表中的有些字段值是固定的例如性别男或女此时我们可以使用MyBatis-Plus的通用枚举 来实现 a数据库表添加字段sex b创建通用枚举类型
package com.jerry.mybatisplus.enums;import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;
Getter
public enum SexEnum {MALE(0, 男),FEMALE(1, 女);EnumValueprivate final Integer sex;private final String sexName;SexEnum(Integer sex, String sexName) {this.sex sex;this.sexName sexName;}
}c配置扫描通用枚举
# 配置MyBatis日志
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:table-prefix:id-type: auto# 配置扫描通用枚举type-enums-package: com.jerry.mybatisplus.enumsd测试 Testpublic void testSexEnum() {User user new User();user.setName(Enums);user.setAge(18);user.setEmail(enumqq.com);user.setSex(SexEnum.FEMALE);userMapper.insert(user);}九、多数据源 适用于多种场景纯粹多库、 读写分离、 一主多从、 混合模式等 目前我们就来模拟一个纯粹多库的一个场景其他场景类似 场景说明 我们创建两个库分别为mybatis_plus以前的库不动与mybatis_plus_1新建将 mybatis_plus库的product表移动到mybatis_plus_1库这样每个库一张表通过一个测试用例 分别获取用户数据与商品数据如果获取到说明多库模拟成功 1、创建数据库及表 创建数据库mybatis_plus_1和表product CREATE DATABASE mybatis_plus_1 /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use mybatis_plus_1;
CREATE TABLE product
(
id BIGINT(20) NOT NULL COMMENT 主键ID,
name VARCHAR(30) NULL DEFAULT NULL COMMENT 商品名称,
price INT(11) DEFAULT 0 COMMENT 价格,
version INT(11) DEFAULT 0 COMMENT 乐观锁版本号,
PRIMARY KEY (id)
);添加测试数据 INSERT INTO product (id, NAME, price) VALUES (1, 外星人笔记本, 100);删除mybatis_plus库product表 use mybatis_plus;
DROP TABLE IF EXISTS product;2、引入依赖 dependencygroupIdcom.baomidou/groupIdartifactIddynamic-datasource-spring-boot-starter/artifactIdversion3.5.0/version/dependency3、配置多数据源 说明注释掉之前的数据库连接添加新配置 spring:# 配置数据源信息datasource:dynamic:# 设置默认的数据源或者数据源组,默认值即为masterprimary: master# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源strict: falsedatasource:master:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezoneGMT%2B8characterEncodingutf-8useSSLfalseusername: rootpassword: rootslave_1:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_plus_1?serverTimezoneGMT%2B8characterEncodingutf-8useSSLfalseusername: rootpassword: root4、创建用户service
UserService
package com.jerry.mybatisplus.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.jerry.mybatisplus.pojo.User;/*** UserService继承IService模板提供的基础功能*/
public interface UserService extends IServiceUser {
}UserServiceImpl
package com.jerry.mybatisplus.service;import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jerry.mybatisplus.mapper.UserMapper;
import com.jerry.mybatisplus.pojo.User;
import org.springframework.stereotype.Service;
/*** ServiceImpl实现了IService提供了IService中基础功能的实现* 若ServiceImpl无法满足业务需求则可以使用自定的UserService定义方法并在实现类中实现*/Service
DS(master)
public class UserServiceImpl extends ServiceImplUserMapper, User implements UserService{
}5、创建商品service
ProductService
package com.jerry.mybatisplus.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.jerry.mybatisplus.entity.Product;public interface ProductService extends IServiceProduct {
}ProductServiceImpl
package com.jerry.mybatisplus.service;import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jerry.mybatisplus.entity.Product;
import com.jerry.mybatisplus.mapper.ProductMapper;
import org.springframework.stereotype.Service;Service
DS(slave_1)
public class ProductServiceImpl extends ServiceImplProductMapper, Product implements ProductService{
}6、测试
Autowired
private UserService userService;
Autowired
private ProductService productService;Test
public void testDynamicDataSource(){//通用mapper的查询是以select开头通用service的查询是以get开头System.out.println(userService.getById(1L));System.out.println(productService.getById(1L));
}结果 1、都能顺利获取对象则测试成功 2、如果我们实现读写分离将写操作方法加上主库数据源读操作方法加上从库数据源自动切换是不是就能实现读写分离