赞
踩
目录
传统的将数据集中存储至单一节点的解决方案,在性能、可用性和运维成本这三方面已经难于满足海量数据的场景。
从性能方面来说,由于关系型数据库大多采用 B+ 树类型的索引,在数据量超过阈值的情况下,索引深度的增加也将使得磁盘访问的 IO 次数增加,进而导致查询性能的下降; 同时,高并发访问请求也使得集中式数据库成为系统的最大瓶颈。
数据分片指按照某个维度将存放在单一数据库中的数据分散地存放至多个数据库或表中以达到提升性能瓶颈以及可用性的效果。 数据分片的有效手段是对关系型数据库进行分库和分表。分库和分表均可以有效的避免由数据量超过可承受阈值而产生的查询瓶颈。 除此之外,分库还能够用于有效的分散对数据库单点的访问量;分表虽然无法缓解数据库压力,但却能够提供尽量将分布式事务转化为本地事务的可能,一旦涉及到跨库的更新操作,分布式事务往往会使问题变得复杂。 使用多主多从的分片方式,可以有效的避免数据单点,从而提升数据架构的可用性。
Apache ShardingSphere 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。
Apache ShardingSphere 设计哲学为 Database Plus,旨在构建异构数据库上层的标准和生态。 它关注如何充分合理地利用数据库的计算和存储能力,而并非实现一个全新的数据库。 它站在数据库的上层视角,关注它们之间的协作多于数据库自身。
https://shardingsphere.apache.org/
ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
ShardingSphere-Proxy 定位为透明化的数据库代理端,通过实现数据库二进制协议,对异构语言提供支持。 目前提供 MySQL 和 PostgreSQL 协议,透明化数据库操作,对 DBA 更加友好。
ShardingSphere-JDBC 采用无中心化架构,与应用程序共享资源,适用于 Java 开发的高性能的轻量级 OLTP 应用; ShardingSphere-Proxy 提供静态入口以及异构语言的支持,独立于应用程序部署,适用于 OLAP 应用以及对分片数据库进行管理和运维的场景。
Apache ShardingSphere 是多接入端共同组成的生态圈。 通过混合使用 ShardingSphere-JDBC 和 ShardingSphere-Proxy,并采用同一注册中心统一配置分片策略,能够灵活的搭建适用于各种场景的应用系统,使得架构师更加自由地调整适合于当前业务的最佳系统架构。
数据分片指按照某个维度将存放在单一数据库中的数据分散地存放至多个数据库或表中以达到提升性能瓶颈以及可用性的效果。 数据分片的有效手段是对关系型数据库进行分库和分表。分库和分表均可以有效的避免由数据量超过可承受阈值而产生的查询瓶颈。 除此之外,分库还能够用于有效的分散对数据库单点的访问量;分表虽然无法缓解数据库压力,但却能够提供尽量将分布式事务转化为本地事务的可能,一旦涉及到跨库的更新操作,分布式事务往往会使问题变得复杂。 使用多主多从的分片方式,可以有效的避免数据单点,从而提升数据架构的可用性。
通过分库和分表进行数据的拆分来使得各个表的数据量保持在阈值以下,以及对流量进行疏导应对高访问量,是应对高并发和海量数据系统的有效手段。 数据分片的拆分方式又分为垂直分片和水平分片。
按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用。 在拆分之前,一个数据库由多个数据表构成,每个表对应着不同的业务。而拆分之后,则是按照业务将表进行归类,分布到不同的数据库中,从而将压力分散至不同的数据库。 下图展示了根据业务需要,将用户表和订单表垂直分片到不同的数据库的方案。
垂直分片往往需要对架构和设计进行调整。通常来讲,是来不及应对互联网业务需求快速变化的;而且,它也并无法真正的解决单点瓶颈。 垂直拆分可以缓解数据量和访问量带来的问题,但无法根治。如果垂直拆分之后,表中的数据量依然超过单节点所能承载的阈值,则需要水平分片来进一步处理。
水平分片又称为横向拆分。 相对于垂直分片,它不再将数据根据业务逻辑分类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个分片仅包含数据的一部分。 例如:根据主键分片,偶数主键的记录放入 0 库(或表),奇数主键的记录放入 1 库(或表),如下图所示。
水平分片从理论上突破了单机数据量处理的瓶颈,并且扩展相对自由,是数据分片的标准解决方案。
表是透明化数据分片的关键概念。 Apache ShardingSphere 通过提供多样化的表类型,适配不同场景下的数据分片需求。
相同结构的水平拆分数据库(表)的逻辑名称,是 SQL 中表的逻辑标识。 例:订单数据根据主键尾数拆分为 3 张表,分别是 t_order_0
,t_order_2,
t_order_3
,他们的逻辑表名为 t_order。
在水平拆分的数据库中真实存在的物理表。 即上个示例中的 t_order_0
,t_order_2,
t_order_3
数据库设计
数据库:test
订单表:t_order_0,t_order_1,t_order_2
- CREATE TABLE `t_order_0` (
- `order_id` bigint(200) NOT NULL,
- `order_no` varchar(100) DEFAULT NULL,
- `create_name` varchar(50) DEFAULT NULL,
- `price` decimal(10,2) DEFAULT NULL,
- PRIMARY KEY (`order_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-
- CREATE TABLE `t_order_1` (
- `order_id` bigint(200) NOT NULL,
- `order_no` varchar(100) DEFAULT NULL,
- `create_name` varchar(50) DEFAULT NULL,
- `price` decimal(10,2) DEFAULT NULL,
- PRIMARY KEY (`order_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-
- CREATE TABLE `t_order_2` (
- `order_id` bigint(200) NOT NULL,
- `order_no` varchar(100) DEFAULT NULL,
- `create_name` varchar(50) DEFAULT NULL,
- `price` decimal(10,2) DEFAULT NULL,
- PRIMARY KEY (`order_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
application.yml配置
- spring:
- main:
- allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
- profiles:
- active: sharding-master-slave
- server:
- port: 8808
-
- mybatis:
- mapper-locations: classpath:mapper/*.xml
- typeAliasesPackage: org.example.test.entity
- configuration:
- map-underscore-to-camel-case: true
application-sharding-master-slave.yml配置
- spring:
- shardingsphere:
- datasource:
- names: m1 #配置库的名字,随意
- m1: #配置目前m1库的数据源信息
- type: com.alibaba.druid.pool.DruidDataSource
- driverClassName: com.mysql.jdbc.Driver
- url: jdbc:mysql://localhost:3306/test?useUnicode=true
- username: root
- password: 123456
- sharding:
- tables:
- t_order: # 指定t_order表的数据分布情况,配置数据节点
- actualDataNodes: m1.t_order_$->{0..2}
- tableStrategy:
- inline: # 指定t_order表的分片策略,分片策略包括分片键和分片算法
- shardingColumn: order_id
- algorithmExpression: t_order_$->{order_id % 3}
- keyGenerator: # 指定t_order表的主键生成策略为SNOWFLAKE
- type: SNOWFLAKE #主键生成策略为SNOWFLAKE
- column: order_id #指定主键
- props:
- sql:
- show: true
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Order实体类代码如下
- @Data
- public class Order {
-
- private Long orderId;
-
- private String orderNo;
-
- private String createName;
-
- private BigDecimal price;
- }
- @Mapper
- public interface OrderMapper {
-
- /**
- * 新增数据
- */
- int insert(Order order);
-
- Order queryById(Long orderId);
- }
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="org.example.test.mapper.OrderMapper">
-
- <resultMap type="org.example.test.entity.Order" id="orderMap">
- <result property="orderId" column="order_id" />
- <result property="orderNo" column="order_no"/>
- <result property="createName" column="create_name"/>
- <result property="price" column="price"/>
- </resultMap>
-
- <!--查询单个-->
- <select id="queryById" resultMap="orderMap">
- select
- order_id, orderNo, createName,price
- from t_order
- where id = #{orderId}
- </select>
-
-
- <!--新增所有列-->
- <insert id="insert" keyProperty="orderId" useGeneratedKeys="true">
- insert into t_order(order_no,create_name, price)
- values (#{orderNo},#{createName},#{price})
- </insert>
- </mapper>
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
OrderTest 单元测试
- @Slf4j
- @RunWith(SpringRunner.class)
- @SpringBootTest(classes = ExampleWeb.class)
- public class OrderTest {
-
- @Resource
- private OrderMapper orderMapper;
-
- @Test
- public void insert() {
- for (int i = 1; i <= 30; i++) {
- Order order = new Order();
- order.setOrderNo(i + "");
- order.setCreateName("test" + i);
- order.setPrice(BigDecimal.valueOf(i));
- orderMapper.insert(order);
-
- System.out.println(JSON.toJSONString(order));
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
application-sharding.yml 配置信息
spring: shardingsphere: datasource: names: ds0,ds1 #配置库的名字,随意 ds0: #配置目前m1库的数据源信息 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ds_0?useUnicode=true username: root password: 123456 ds1: #配置目前m1库的数据源信息 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ds_1?useUnicode=true username: root password: 123456 sharding: tables: t_order: # 指定t_order表的数据分布情况,配置数据节点 actualDataNodes: ds$->{0..1}.t_order_$->{0..3} database-strategy: inline: # 指定t_order表的分片策略,分片策略包括分片键和分片算法 shardingColumn: order_no algorithmExpression: ds$->{order_no % 2} tableStrategy: inline: # 指定t_order表的分片策略,分片策略包括分片键和分片算法 shardingColumn: order_no algorithmExpression: t_order_$->{order_no % 4} keyGenerator: # 指定t_order表的主键生成策略为SNOWFLAKE type: SNOWFLAKE #主键生成策略为SNOWFLAKE column: order_id #指定主键 props: sql: show: true
- <?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 http://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.3.4.RELEASE</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
-
- <groupId>org.example</groupId>
- <artifactId>sharding</artifactId>
- <version>1.0-SNAPSHOT</version>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-configuration-processor</artifactId>
- </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>com.alibaba</groupId>
- <artifactId>druid</artifactId>
- <version>1.2.1</version>
- </dependency>
-
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>5.1.44</version>
- </dependency>
-
- <!--添加mybatis分页插件支持 根据需求可要可不要-->
- <dependency>
- <groupId>com.github.pagehelper</groupId>
- <artifactId>pagehelper</artifactId>
- <version>5.2.0</version>
- </dependency>
-
- <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- <version>3.11</version>
- </dependency>
-
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>3.4.3.3</version>
- </dependency>
-
- <dependency>
- <groupId>org.apache.shardingsphere</groupId>
- <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
- <version>4.1.0</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <version>2.7.9</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.13.1</version>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>2.0.14</version>
- </dependency>
- </dependencies>
- </project>
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
spring: shardingsphere: datasource: names: ds0,ds1 #配置库的名字,随意 ds0: #配置目前m1库的数据源信息 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ds_0?useUnicode=true username: root password: 123456 ds1: #配置目前m1库的数据源信息 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ds_1?useUnicode=true username: root password: 123456 sharding: tables: myOrder: # 指定t_order表的数据分布情况,配置数据节点 actualDataNodes: ds$->{0..1}.t_order_$->{0..3} database-strategy: inline: # 指定t_order表的分片策略,分片策略包括分片键和分片算法 shardingColumn: order_id algorithmExpression: ds$->{order_id % 2} tableStrategy: inline: # 指定t_order表的分片策略,分片策略包括分片键和分片算法 shardingColumn: order_id algorithmExpression: t_order_$->{order_id % 4} keyGenerator: # 指定t_order表的主键生成策略为SNOWFLAKE type: SNOWFLAKE #主键生成策略为SNOWFLAKE column: order_id #指定主键 props: sql: show: true server: port: 8808
- @Data
- @TableName("myOrder")
- public class Order {
-
- @TableId(type = IdType.ASSIGN_ID)
- private Long orderId;
-
- private Integer orderNo;
-
- private String createName;
-
- private BigDecimal price;
- }
- public interface OrderMapper extends BaseMapper<Order> {
-
- }
- @Slf4j
- @RunWith(SpringRunner.class)
- @SpringBootTest(classes = ExampleWeb.class)
- public class OrderTest {
-
- @Resource
- private OrderMapper orderMapper;
-
- @Test
- public void insert() throws Exception {
- ExecutorService executorService = Executors.newFixedThreadPool(30);
- for (int i = 1; i <= 30; i++) {
- executorService.execute(() -> {
- Order order = new Order();
- order.setOrderNo(111);
- order.setCreateName("test" );
- order.setPrice(BigDecimal.valueOf(1));
- orderMapper.insert(order);
-
- System.out.println(String.format(">>>>>>>>>>>>>>>>>>>>>>>>>>%s,%s", order.getOrderId(), order.getOrderId() % 4));
- });
-
- }
-
- Thread.sleep(10000);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
十亿用户的系统,用户有多种登录方式,可以使用手机号、账号、邮箱、昵称等登录,这样的表结构应该怎样设计?登录流程大致是怎样的?
当出现多种登录方式的时候,就意味着一个用户对应的账号可能会有若干个。现在可能用手机和昵称登录,以后就可能用邮箱登录,甚至将来还可能通过微信、QQ、微博等第三方渠道登录。
我们最先想到的是每增加一种登录方式就新增一个字端,
字段 | 字段描述 |
id | Long |
name | 登录名 |
mobile | 手机号码 |
电子邮箱 |
但这样会产生以下问题:
当用户登录的时候,我们需要根据用户的登录类型,先要知道去查找用户表的哪个字段才可以进行登录逻辑判断。例如,用户登录用手机号了,我们就要知道去表里查找对应的 mobile 字段去校验登录;登录用邮箱了,我们就要知道去表里查找对应的 email 字段才可以。这样做,代码逻辑会很复杂。
再增加一种登录方式的时候,我们还得给数据库的表里再增加一个字段,同时还得修改登录的代码。这显然违反了我们设计模式中的开闭原则。而且这种修改很容易造成线上 bug。每增加一种登录方式,就新增一种流程,成本有点过高了。
我们设计的表,必须易扩展
加记录比加字段要更容易扩展
因此方案如下:
创建一张用户登录 user_login表,专门用来处理登录。当新增登录类型的时候,只需要考虑增加一条记录即可:记录登录类型、登录名称以及相关密码,同时有个 user_id 字段,去和用户表做关联。
字段名称 | 字段描述 |
id | Long |
name | 用户名 |
type | 1:登录名 2:手机号码 3:邮箱 |
password | 密码(多个用户名可共用一个密码,也可以存储token) |
user_id | 用户表ID |
用户表user存储基础信息
字段名称 | 字段描述 |
id | 用户ID主键 |
nick_name | 用户昵称 |
user_logo | 用户头像 |
user_names | 存储和user_id关联的账号 |
这样设计后,很明显就做到了易扩展。
假如我自己有两种登录方式,user_login 表的数据:
id | name | type | password | user_id |
1 | xiyangyang | 1 | 123456 | 1 |
2 | xiyangyang@qq.com | 3 | 123456 | 1 |
用户表(User)的数据:
id | nick_name | user_logo | user_names |
1 | 喜声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】 推荐阅读 相关标签 Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。 |