背景
我们为什么要进行分库分表?
在电商系统中,当一张表的数据达到几千万时,查询一次所花的时间会变长。这时候,如果有联合查询的话,可能会卡死在那儿,甚至把系统给拖垮。而分库分表的目的就在于此:减小数据库的负担,提高数据库的效率,缩短查询时间。权衡过多个框架的利弊后,我们最终选择使用Sharding-JDBC来进行分库分表。
Sharding-JDBC简介
Sharding-jdbc定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据。
从官网的架构图中,可以看出我们只需要将Sharding-JDBC引入到项目中就好了。
Sharding-JDBC的使用
接下来,以b2b2c电商系统Javashop为例,具体说明sharding jdbc的应用:
一、分库分表的准备
在修改配置文件之前,我们应该想好我们的分片策略,包括:
1、要用几个数据库来分片
2、相应的表要分几张表
在本次分库分表中,以es_order表为例,将原始数据库javashop拆分为javashop0,javashop1两个数据库。将原始es_order表拆分为es_order0,es_order1两张表。
表中分片字段如下:
CREATE TABLE `es_order0` ( `order_id` bigint(20) NOT NULL COMMENT '主键ID', `trade_sn` varchar(20) DEFAULT NULL COMMENT '交易编号', 其他字段略) CREATE TABLE `es_order1` ( `order_id` bigint(20) NOT NULL COMMENT '主键ID', `trade_sn` varchar(20) DEFAULT NULL COMMENT '交易编号', 其他字段略)
二、引入maven依赖
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>4.1.0</version> </dependency>
三、配置分片策略
定义数据库的分片策略application.yaml:
spring: profiles: include: order #分库分表配置 shardingsphere: #配置sql是否显示输出,调试用,生产环境需关闭 props: sql: show: true sharding: #定义默认数据源为:ds0 default-data-source-name: ds0 #定义分库的数据源 datasource: #这里配置所有数据源的名字 names: ds0,ds1 ds0: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://ip:3306/default_database?useUnicode=true&characterEncoding=utf8&autoReconnect=true username: root password: 123456 ds1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.2.110:3306/javashop02?useUnicode=true&characterEncoding=utf8&autoReconnect=true username: root password: 123456
定义表分片策略修改application-order.yml:
spring: #分库分表配置 shardingsphere: sharding: tables: #交易表(用trade sn 分库,用trade_id分表) es_trade: actual-data-nodes: ds$->{0..1}.es_trade$->{0..1} database-strategy: inline: sharding-column: trade_sn algorithm-expression: ds$->{ new Long(trade_sn) % 2} table-strategy: inline: sharding-column: trade_id algorithm-expression: es_trade$->{trade_id % 2}
通过上述方式我们分别将商品,会员,订单进行了分库分表。
问题与解决方案
如果您以为就是这么简单就完成了那么就大错特错了。我们遇到的问题如下:
主键自增问题
因为当使用分库分表等功能之后,就不能再依赖数据库自带的主键生成机制了,一方面主键ID不能重复,另外需要在新增之前就知道主键ID,才能保证ID能够均匀分布到不同的数据库或数据表中,所以要使用一个合理的主键生成策略。
我们的解决方案就是在做insert语句的时候,使用snowflake发号器生成主键。snowflake 是常见的id(编号)生成算法,由时间戳+业务id+机器id+序列号组合而成,在电商系统中,用于订单号的生成、支付单号的生成等等。本发号器主要解决在容器化的部署情况时,自动扩容时保持机器id的唯一性。关于snowflake详细说明请看《java 商城系统源码分享-snowflake发号器》的文章
统一封装的insert方法如下:
public void insert(String table, Object po) { Long id = snCreator.create(getSubCode(table)); //设置刚刚主键值到 thread local lastIdThreadLocal.set(id); Map poMap = new HashedMap(); ColumnMeta columnMeta = ReflectionUtil.getColumnMeta(po); poMap.put(columnMeta.getPrimaryKeyName(), id); //这里就是将插入的时候使用雪花发号器作为主键,您们可以根据自己的业务进行修改。 如下略: }
使用snowflake发号器带来的类型问题
因为雪花发号器的类型为Long类型我们在未分库分表之前数据库使用的id类型为int(11),所以我们需要将所有涉及到分库分表id类型类型修改为bigint(20)。当然还有java代码中接收数据的实体类。
使用snowflake号器带来的精度丢失问题
因为js支持数字的最大精度为16位以下,那么我们的雪花发号器的位数为17为。好尴尬啊,后来我们统一将Long类型的数据转为String类型。代码如下:
@Configuration public class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer customJackson() { return new Jackson2ObjectMapperBuilderCustomizer() { @Override public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) { //定义Long型的Json 值转换,转换为String型 //为了适配javascript 不支持Long型的精度 jacksonObjectMapperBuilder.serializerByType(Long.class,new JsonSerializer(){ @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(String.valueOf(value)); } }); } }; } }
使用相同字段进行分库分表的问题
相同字段进行分库分表,就是我们用主键id进行分库,然后再用主键id进行分表。如果我插入100条订单数据的话,那么javashop0数据库中es_order0表有50条id为奇数的数据es_order1表中的数据为0条。javashop1数据库中的es_order0表0条数据es_order1表中50条数据。这显然不是我们所希望看到的。所以我们在分库的时候使用与2取模的形式分库,分表的时候我们将id右移2位在此进行与2取模。那么得到的结果才是我们想要的。具体配置如下:
spring: #分库分表配置 shardingsphere: sharding: tables: #商品表(用member_id分库,用member_id分表) es_member: actual-data-nodes: ds$->{0..1}.es_member$->{0..1} database-strategy: inline: sharding-column: member_id algorithm-expression: ds$->{member_id% 2} table-strategy: inline: sharding-column: member_id #因为都是使用同一个字段进行分库分表的, #所以要右移两位之后取模以保证每个表中的数据平均 algorithm-expression: es_member$->{(member_id>>2)% 2}
Sharding-jdbc不支持的sql
官网提供不支持的sql如下:
sql语句 | 原因 |
INSERT INTO tbl_name (col1, col2, …) VALUES(1+2, ?, …) | VALUES语句不支持运算表达式 |
INSERT INTO tbl_name (col1, col2, …) SELECT col1, col2, … FROM tbl_name WHERE col3 = ? | INSERT … SELECT |
SELECT COUNT(col1) as count_alias FROM tbl_name GROUP BY col1 HAVING count_alias > ? | HAVING |
SELECT * FROM tbl_name1 UNION SELECT * FROM tbl_name2 | UNION |
SELECT * FROM tbl_name1 UNION ALL SELECT * FROM tbl_name2 | UNION ALL |
SELECT * FROM ds.tbl_name1 | 包含schema |
SELECT SUM(DISTINCT col1), SUM(col1) FROM tbl_name | 详见DISTINCT支持情况详细说明 |
SELECT * FROM tbl_name WHERE to_date(create_time, ‘yyyy-mm-dd’) = ? | 会导致全路由 |
SELECT SUM(DISTINCT col1), SUM(col1) FROM tbl_name | 同时使用普通聚合函数和DISTINCT聚合函数 |
这个问题的话我们的解决方案是优化我们的sql,将这些不支持的sql语句使用其他的逻辑实现在这里就不一一阐述了。
易族智汇(javashop)原创文章
相关推荐
第五次演进中数据库按业务分库,即根据业务的不同,将不同的业务数据存放在不同的数据库中,降低了不同业务之间的耦合度,提高了系统的扩展性和维护性。 第六次演进是把大表拆分为小表,这样做可以减少单个表的数据...
本文旨在探讨数据挖掘技术在电子商务领域的应用,并重点分析Web数据挖掘技术在电商中的具体实践。 #### 二、Web数据挖掘技术概述 Web数据挖掘是指从互联网上收集的数据中提取有用信息的过程。随着大数据时代的到来...
在本教程中,我们将深入探讨如何使用React全家桶来开发一个完整的电商系统。React作为一款流行的JavaScript库,已经成为构建现代Web应用的首选工具之一。它以其组件化、虚拟DOM和高效的更新策略等特性赢得了开发者们...
【文件名称列表】:jshop,这个名字很可能代表了这个开源B2C电商系统的名称,可能是系统的主要代码库或者安装包。JShop(假设是系统的名字)可能是一个基于Java的电商平台框架,包含了一系列的Java类、配置文件、...
- **Java作为主要开发语言**:Java凭借其强大的生态系统、丰富的框架支持和跨平台特性,在电商系统开发中占据主导地位。 - **Spring Boot/Spring Cloud**:Spring Boot简化了Java应用的开发和配置过程;Spring Cloud...
在校园电商系统中,Redis可以用来存储热点数据,如热门商品、用户会话等,提高数据读取速度,降低数据库负载,从而提升整体系统的响应速度。 Nginx是一款高性能的HTTP和反向代理服务器,也是邮件代理服务器。在本...
同时,数据库层面也可能需要分库分表策略,如ShardingSphere,来分散读写压力。 4. **数据一致性**:在分布式环境中,保持数据的一致性是一大挑战。CAP理论指出,不能同时满足一致性、可用性和分区容忍性。常见的...
首先,我们要明确.NET框架在电商系统中的作用。.NET是由微软公司开发的一套全面的开发平台,它提供了丰富的类库和工具,用于构建各种类型的Web应用程序,包括电商系统。.NET框架的强类型、面向对象的特性,使得代码...
大数据挖掘技术近年来在电商市场分析和决策中的应用变得愈发重要。本文将从大数据挖掘的概况出发,分析电商市场中应用大数据挖掘的策略,并提供相关指导。首先,了解大数据挖掘的概念是应用这一技术的前提。大数据...
开源电商系统是一种基于互联网技术,以开放源代码的形式提供给用户免费使用和修改的电子商务平台。这类系统通常采用敏捷开发模式,由全球各地的开发者共同维护和更新,以实现更高效、更稳定、更安全的运营环境。在本...
商品分类和信息管理是电商系统的基石。Vue.js的组件化特性使得可以将商品列表、分类筛选等模块封装为独立的组件,方便复用和维护。同时,后端API接口返回的商品数据可以通过axios等HTTP库进行异步请求,实现数据的...
【电商后台管理系统】是现代电商平台运营的核心组成部分,其目的是通过高效、便捷的管理方式提升工作效率,确保信息的实时性,从而在竞争激烈的市场环境中占据优势。这篇【课程设计】主要探讨了电商后台管理系统的...
【大数据在B2C电商中的应用】 大数据在B2C电子商务行业中扮演着至关重要的角色,通过收集、处理和分析海量的用户数据,企业能够提供更加个性化、精准的服务,提升用户体验,促进销售。以下将详细讨论大数据在B2C...
下面将详细阐述这些技术及其在电商系统中的应用。 ### 1. Spring框架 Spring是Java领域的一个核心框架,它提供了依赖注入(DI)和面向切面编程(AOP)的功能。在电商系统中,Spring负责管理各个组件(如服务、DAO)...
本研究聚焦于设计并实现一款基于Python的电商导购APP,旨在解决用户在海量商品中寻找心仪商品的难题,同时也为商家提供一个高效的营销渠道。该APP通过Python编写的网络爬虫从主流电商平台(如淘宝、天猫等)抓取数据...
在“基于Vue的电商后台管理系统”中,Vue.js 起到了核心作用,构建了一个高效、可维护的管理界面。 1. **Vue.js 的核心特性** - **虚拟DOM**:Vue.js 使用虚拟DOM来提高性能和效率,避免了直接操作DOM带来的性能...
在本文中,我们将深入探讨H5移动电商系统的特性、优势以及它如何助力电子商务业务。 一、H5移动电商系统的特性 1. 跨平台兼容性:基于HTML5的系统能在不同的移动设备和操作系统上运行,包括iOS、Android和Windows ...
本文主要探讨了如何利用Python及其机器学习库Scikit-Learn来实施聚类分析,尤其是应用在电商客户细分的场景中。文章首先对Python语言进行简单介绍,然后着重介绍了K-Means聚类算法,并对聚类结果的评估方法进行了...
Java以其稳定性和跨平台特性在企业级应用中广泛应用,Spring Boot和Spring Cloud等框架常用于构建微服务架构的电商系统,提供服务发现、负载均衡、熔断、降级等功能。 5. **电商系统设计**:一个完整的电商系统需要...