仿12306校招项目业务三(用户注册)

2024-02-26 23:36

本文主要是介绍仿12306校招项目业务三(用户注册),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

用户表结构

原本的表结构如下

由于用户量大,采用分库分表:

分库分表设计

根据系统设计的假设,12306 的注册用户规模约为 10 亿,每年新增用户约 1000 万。在用户数据分库或分表之前,我们需要先考虑拆分成多少个库或表才能达到最优性能。为了进行这样的决策,我们可以预估单个表的最大数据量。根据过去的经验,通常我们会选择 2000 万作为一个经验值。这个数据量既不会过小,同时又能保证增删改查等操作相对流畅。

根据当前用户表的数据量为 10 亿,并且每年新增 1000 万用户,预估未来系统的生命周期较长,数据量大概会达到 30 亿左右。基于这个数据量,我们预估单表的数据量在2000 万左右,因此需要分大约 150 张表来容纳这些数据。

在进行分库分表容量评估时,我们通常会尽可能多地进行评估。这样做的好处是,即使每张表的数据量不多,也能及早发现拆分后是否存在数据问题,以便及时进行调整和优化。此外,需要特别指出的是,我们对表数据量考虑的阈值相对较小,这是因为我们的系统具备良好的可扩展性,可以轻松应对大量的数据增长。因此,基于这种情况的分库分表策略,即使在几百年后,这个分库分表依然能够处理数据,并且不会出现性能问题。这为我们的系统提供了稳定可靠的性能障。

选择分库分表中的分片键(Sharding Key)是一个关键决策,它直接影响了分库分表的性能和可扩展性。以下是一些选择分片键的关键因素:

1. 访问频率:选择分片键应考虑数据的访问频率。将经常访问的数据放在同一个分片上,可以提高查询性能和降低跨分片查询的开销。

2. 数据均匀性:分片键应该保证数据的均匀分布在各个分片上,避免出现热点数据集中在某个分片上的情况。

3. 业务关联性:分片键应该与业务关联紧密,这样可以避免跨分片查询和跨库事务的复杂性。

4. 数据不可变:一旦选择了分片键,它应该是不可变的,不能随着业务的变化而频繁修改。

基于以上考虑,我们选择使用 username 作为分片键。

分库分表实现

使用 ShardingSphere 分库分表操作,可以查看官网进行一些前置条件理解:

数据分片 :: ShardingSphere

1. 引入 ShardingSphere 依赖

<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.3.2</version>
</dependency>

2. 定义分片规则

spring:application:name: index12306-user${unique-name:}-servicedatasource:driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriverurl: jdbc:shardingsphere:classpath:shardingsphere-config.yaml

3. 用户分片配置

因为 12306 更多的是向大家演示分库分表,所以分 2 个库以及对应业务 16 张表。

shardingsphere-config.yaml:

dataSources:ds_0:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://127.0.0.1:3306/12306_user_0?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghaiusername: rootpassword: rootds_1:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://127.0.0.1:3306/12306_user_1?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghaiusername: rootpassword: rootrules:- !SHARDINGtables:t_user:actualDataNodes: ds_${0..1}.t_user_${0..31}databaseStrategy:standard:shardingColumn: usernameshardingAlgorithmName: user_database_hash_modtableStrategy:standard:shardingColumn: usernameshardingAlgorithmName: user_table_hash_modt_passenger:actualDataNodes: ds_${0..1}.t_passenger_${0..31}databaseStrategy:standard:shardingColumn: usernameshardingAlgorithmName: passenger_database_hash_modtableStrategy:standard:shardingColumn: usernameshardingAlgorithmName: passenger_table_hash_modt_user_mail:actualDataNodes: ds_${0..1}.t_user_mail_${0..31}databaseStrategy:standard:shardingColumn: mailshardingAlgorithmName: t_user_mail_database_hash_modtableStrategy:standard:shardingColumn: mailshardingAlgorithmName: t_user_mail_table_hash_modt_user_phone:actualDataNodes: ds_${0..1}.t_user_phone_${0..31}databaseStrategy:standard:shardingColumn: phoneshardingAlgorithmName: t_user_phone_database_hash_modtableStrategy:standard:shardingColumn: phoneshardingAlgorithmName: t_user_phone_table_hash_modshardingAlgorithms:user_database_hash_mod:type: CLASS_BASEDprops:sharding-count: 32table-sharding-count: 16strategy: standardalgorithmClassName: org.opengoofy.index12306.framework.starter.database.algorithm.sharding.CustomDbHashModShardingAlgorithmpassenger_database_hash_mod:type: CLASS_BASEDprops:sharding-count: 32table-sharding-count: 16strategy: standardalgorithmClassName: org.opengoofy.index12306.framework.starter.database.algorithm.sharding.CustomDbHashModShardingAlgorithmt_user_mail_database_hash_mod:type: CLASS_BASEDprops:sharding-count: 32table-sharding-count: 16strategy: standardalgorithmClassName: org.opengoofy.index12306.framework.starter.database.algorithm.sharding.CustomDbHashModShardingAlgorithmt_user_phone_database_hash_mod:type: CLASS_BASEDprops:sharding-count: 32table-sharding-count: 16strategy: standardalgorithmClassName: org.opengoofy.index12306.framework.starter.database.algorithm.sharding.CustomDbHashModShardingAlgorithmpassenger_table_hash_mod:type: HASH_MODprops:sharding-count: 32t_user_mail_table_hash_mod:type: HASH_MODprops:sharding-count: 32t_user_phone_table_hash_mod:type: HASH_MODprops:sharding-count: 32user_table_hash_mod:type: HASH_MODprops:sharding-count: 32- !ENCRYPTtables:t_user:columns:id_card:cipherColumn: id_cardencryptorName: common_encryptorphone:cipherColumn: phoneencryptorName: common_encryptormail:cipherColumn: mailencryptorName: common_encryptoraddress:cipherColumn: addressencryptorName: common_encryptort_passenger:columns:id_card:cipherColumn: id_cardencryptorName: common_encryptorphone:cipherColumn: phoneencryptorName: common_encryptorqueryWithCipherColumn: trueencryptors:common_encryptor:type: AESprops:aes-key-value: d6oadClrrb9A3GWo
props:sql-show: true

缓存穿透解决

缓存穿透是指在使用缓存系统时,恶意或频繁地请求一个不存在于缓存中的数据,导致每次请求都需要查询数据库或其他数据存储系统,从而绕过了缓存的效果,严重影响系统性能。这种情况通常发生在恶意攻击、大量请求缓存中不存在的数据或缓存数据过期后的高并发访问。

缓存穿透会导致以下问题:

1. 频繁的查询数据库或其他数据存储系统,增加了数据库负载,降低了系统的吞吐量。

2. 大量的缓存不存在的数据请求可能会导致缓存服务器的内存被耗尽,影响其他正常的缓存操作。

3. 用户体验下降,因为请求的数据无法从缓存中获取,导致响应时间延长。

采用布隆过滤器结合缓存的方式来解决缓存穿透问题

用户注册接口

用户注册接口整体流程如下所示:

责任链模式

12306 系统中,我们定义责任链模式分为三步,确定方法执行入参、定义当前业务责任链接口以及具体实施验证责任链执行器。

1)确定方法执行入参

因为我们是用户注册接口,验证的也是用户提交的数据,直接复用该实体即可。

@Data
public class UserRegisterReqDTO {/*** 用户名*/private String username;/*** 密码*/private String password;/*** 真实姓名*/private String realName;/*** 证件类型*/private Integer idType;/*** 证件号*/private String idCard;/*** 手机号*/private String phone;/*** 邮箱*/private String mail;/*** 旅客类型*/private Integer userType;/*** 审核状态*/private Integer verifyState;/*** 邮编*/private String postCode;/*** 地址*/private String address;/*** 国家/地区*/private String region;/*** 固定电话*/private String telephone;
}

2)定义业务责任链接口

public interface UserRegisterCreateChainFilter<T extends UserRegisterReqDTO> extends AbstractChainHandler<UserRegisterReqDTO> {@Overridedefault String mark() {return UserChainMarkEnum.USER_REGISTER_FILTER.name();}
}

3)定义责任链业务具体处理器。

在定义责任链处理器时,需要注意使用  getOrder 排序接口来决定组件的执行顺序。通常情况下,处理效率高且在内存中执行的验证策略应该优先执行,而需要涉及交互操作(例如 Redis 等)的处理策略则放在后面执行。

举例来说,假设用户没有传递身份证号,在这种情况下,首先执行验证用户名的操作是非常浪费的,因为后续还需要验证参数必填性,这样就多了一次查询缓存的无用耗时。尽管性能损耗可能不会很高,但这种无用的损耗应该尽量避免。

因此,通过合理地使用  getOrder 排序接口,我们可以优化责任链的执行顺序,使得处理效率高的操作优先执行,避免不必要的性能损耗,从而提升整体处理性能和效率。

用户表相关新增

先来查看 12306 登录功能的原型截图及其对应的功能

在登录功能中,用户一栏明确标出可以使用用户名、邮箱或手机号中的任意一个搭配密码进行登录。需要强调的是,在分库分表中,我们是通过用户名进行分片的。因此,如果在查询用户信息时不带用户名,将会触发读扩散问题。

为了解决这个问题,我们引入了两张路由表:用户手机号表和用户邮箱表。这些表的核心字段是手机号和邮箱,以及它们对应的用户名。通过这样的设计,我们能够在用户登录时,灵活地使用手机号、邮箱或用户名来进行认证。

 其它操作

在用户注册后,该用户名将不再可用,因此我们需要将其添加到布隆过滤器中,以防止其他人在后续尝试注册时再次使用。

另外,关于用户注册文章中提到的用户名可复用问题,我们特别考虑了这一点。已经注销的用户名需要能够被其他用户再次使用,因此我们对可复用用户名进行了扩展设计。

这意味着,我们需要将对应的缓存和数据库表一并删除。一旦删除完成,后续用户将无法再通过这个扩展点使用该用户名。这样的设计保证了用户名的合理复用,同时确保已注销用户名的信息不会对后续用户产生影响。

这篇关于仿12306校招项目业务三(用户注册)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/750525

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

业务协同平台--简介

一、使用场景         1.多个系统统一在业务协同平台定义协同策略,由业务协同平台代替人工完成一系列的单据录入         2.同时业务协同平台将执行任务推送给pda、pad等执行终端,通知各人员、设备进行作业执行         3.作业过程中,可设置完成时间预警、作业节点通知,时刻了解作业进程         4.做完再给你做过程分析,给出优化建议         就问你这一套下

SpringBoot项目是如何启动

启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refresh 加载 IOC容器,加载所有的自动配置类,创建容器在这个过程

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免

2. 下载rknn-toolkit2项目

官网链接: https://github.com/airockchip/rknn-toolkit2 安装好git:[[1. Git的安装]] 下载项目: git clone https://github.com/airockchip/rknn-toolkit2.git 或者直接去github下载压缩文件,解压即可。

9.8javaweb项目总结

1.主界面用户信息显示 登录成功后,将用户信息存储在记录在 localStorage中,然后进入界面之前通过js来渲染主界面 存储用户信息 将用户信息渲染在主界面上,并且头像设置跳转,到个人资料界面 这里数据库中还没有设置相关信息 2.模糊查找 检测输入框是否有变更,有的话调用方法,进行查找 发送检测请求,然后接收的时候设置最多显示四个类似的搜索结果