短连服务crud(第十八章/十九章/二十章/二十一章)海量数据处理-商用短链
第十八章 短链服务-业务需求和短链码解决方案讲解
第1集 短链服务介绍和应用场景讲解
简介: 短链服务介绍和应用场景讲解
- 什么是短链服务
- 业务背景:为啥需要短链
- 公司电商产品推广、业务活动页、广告落地页 缺少实时【数据反馈和渠道效果分析】
- 老项目业务推广【没人维护,无法做埋点】需要统计效果
- APP和营销活动发送营销短信链接过长,【浪费短信发送费用】
- 国内【反垄断后】微信、抖音、淘宝 流量互通,很多知识付费公司需要做 私域流量、社群运营
- 可以对外做产品输出,实现商业化能力增加公司营收
- 积累终端数据和人群数据,为公司未来产品人群做策略助力
- 更多。。。。
- 短链组成
- 协议://短链域名/短链码
- 最简单的方式
- 一个短链编码,去数据库select * from table where code =XXX,返回给用户就行
第2集 需求出发-带你详细一个短链的生命周期
简介: 需求出发带你详细一个短链的生命周期
- 创建者和访问者
- 创建者(UI图大体类似哈)
- 流量包管理
- 免费流量包
- 付费流量包
- 分组管理
- 新增分组
- 删除分组
- 修改分组
- 查看分组下的短链
- 流量包管理
- 短链管理
- 创建短链
- 目标地址
- 短链标题
- 短链域名
- 所属分组
- 有效期
- 创建短链
- 删除短链
- 修改短链
- 查看短链
- 访问PV、UV
- 地域分布
- 时间分布
- 来源分布
- …
- 访问者
- 访问短链
- 跳转目标站点
第3集 短链服务生成短链URL的问题你能想到多少
简介: 短链URL生成服务里面的问题你能想到多少
- 短链码如何生成
- 需要解决的问题(自己是技术Leader,能否想到这些问题,并解决。做架构设计之前,要想清楚)
- 问题一:长链的关系和短链的关系
- 一对一?
- 一对多?
- 多对多?
- 问题二:前端访问短链是如何跳转到对应的页面的?
- 问题三:短链码是如何生成的
- 知道几种方式?
- 问题四:SaaS类型业务,数据量有多大,是否要分库分表
- 问题五:如果分库分表,PartitionKey是哪个?使用怎样的策略
- 问题六:如果分库分表,访问短链怎么知道具体是哪个库哪个表?
- 问题七:如果分库分表,怎么查看某个账号创建的全部短链?
- 问题一:长链的关系和短链的关系
第4集 短链服务问题解决方案讲解-业务关系+跳转问题
简介: 短链服务问题解决方案讲解-业务关系+跳转问题
问题一:长链的关系和短链的关系是一对一还是一对多?
- 一个长链,在不同情况下,生成的短网址应该不一样,才不会造成冲突
- 多渠道推广下,也可以区分统计不同渠道的效果质量
- 所以是 一个短链接只能对应一个长链接,当然一个长链接可以对应多个短链接
问题二:前端访问短链是如何跳转到对应的页面的?
服务端转发
- 由服务器端进行的页面跳转,刚学Servlet时, 从OneServlet中转发到TwoServlet
- 地址栏不发生变化,显示的是上一个页面的地址
- 请求次数:只有1次请求
- 转发只能在同一个应用的组件之间进行,不可以转发给其他应用的地址
request.getRequestDispatcher("/two").forward(request, response);
- 页面的跳转-重定向
- 由浏览器端进行的页面跳转
- 重定向涉及到3xx状态码,访问跳转是301还是302,301和302代表啥意思?
- 301 是永久重定向
- 会被浏览器硬缓存,第一次会经过短链服务,后续再访问直接从浏览器缓存中获取目标地址
- 302 是临时重定向
- 不会被浏览器硬缓存,每次都是会访问短链服务
- 短地址一经生成就不会变化,所以用 301 是同时对服务器压力也会有一定减少
- 但是如果使用了 301,无法统计到短地址被点击的次数
- 所以选择302虽然会增加服务器压力,但是有很多数据可以获取进行分析
- 选择使用302,这个也可以对违规推广的链接进行实时封禁
- 301 是永久重定向
第5集 短链服务问题解决方案讲解-短链码生成解决方案《上》
简介: 短链服务问题解决方案讲解-短链码生成解决方案《上》
问题三:短链码如何是如何生成的
短链码特点
- 生成性能强劲
- 碰撞概率低
- 避免重复
- 恶意猜测
- 业务规则安全
方式
- 自增ID
- 利用插入数据库,利用数据库自增id
- 把自增id转成62进制作为短链码
- 短链码的长度不固定,随着 id 变大,短链码长度也增长
- 可以指定从某个长度开始增长,到百亿、千亿数量
- 转换工具:https://tool.lu/hexconvert/
- 是否存在重复: 不重复
- 但短链码是有序的递增,存在【业务数据安全】问题
MD5内容压缩
- 长链接做md5加密
43E08496,9E5CF455,E6D2D2B3,3407A6D2
- 加密串查询是否已经生成过短链接
- 如果已经存在,则拼接时间戳再MD5加密,插入数据库
- 如果不存在则把长链接、长链接加密串插入数据库
- 取MD5后 最后1 个 8 位字符串作为短链码
- 是否存在重复: 存在碰撞(重复)可能
- 是有损压缩算法,数据量超大情况碰撞概念越大
- 比如 【小滴课堂-老王的女友】有300多个,每再多1个,再同一天生日的概率越大,就更加复杂
第6集 【重要】敏感数据+自增ID暴露的商业秘密
简介: 敏感数据讲解+自增ID暴露的商业秘密
什么是数据脱敏
- 也叫数据的去隐私化,在给定脱敏规则和策略的情况下,对敏感数据比如
手机号
、身份证
等信息,进行转换或者修改的一种技术手段,防止敏感数据直接在不可靠的环境下使用和泄露、撞库等 - 技术分两类
- 静态数据脱敏
- 将生产数据导出,进行对外发送或者给开发、测试人员等
- 动态数据脱敏
- 程序直接连接生产数据的场景,如运维人员在运维的工作中直接连接生产数据库进行运维
- 客服人员在生产中通过后台查询的个人信息
- 静态数据脱敏
数据库业务规则安全:自增ID暴露的商业秘密
背景
作为后端开发人员肯定离不开数据库设计,尽管你知道数据安全、接口安全、网络安全,但你也很大可能不小心暴露的公司的核心机密。
【做空上市公司的股票】如果你有炒股,那你知道如果这个数据库设计漏洞被他人知道,就可以做空一个上司公司的股票的不?
【摧毁对手的利器】如果一个公司在是靠业务数据来说话的,如果被他人知道,在核心的时间点,被披露出来,那融资可能凉凉,企业可能面临倒闭。
在某天一个竞争对手 老王 做竞品调研的时候,注册了你公司的应用
【暴露了总用户量】
通过业务接口抓包分析(假定数据没做加密或者加密被破解了),被他发现了自增id这个事情,他好几个号注
册了,编号都是一百多万,每次id都是自增1, 用户量最多100多万,就此暴雷了。
经过有经验的人知道不能对外暴露id这个事情,但是总有关联的业务,或者其他不知道的人员开发了对应
的功能,比如 订单表、记录表、收藏表或者其他和user_id有关联的,只要他操作业务,接口有返回user_id,就容易出问题了。
【暴露了每日拉新数据】
如果是用户自增id,老王 肯定不单停留这里这么简单,假如他想看这个产品每日新增的用户有多少。
毕竟拉新这个指标是很多公司都离不开的,比如通过投放广告、买量业务等等去获取新用户。
老王想看下这个公司每天新增用户量大不大,他就今天注册一个看id多少,明天再注册一个看id多少;
通过一段时间内去统计每天递增的值,老王就可以推断出平台新增用户大概是多少了,公司的拉新能力如何。
【暴露了平台商品数量-订单数量】
同样的思路,去爬取电商平台的商品,爬取平台最大的商品id, 第二天再次爬取,持续一段时间。就可以可以推断出新品发布数量。
同样的思路,去抓取订单接口,看最大id是多少, 第二天再次爬取,持续一段时间。就可以推断出这个公司的每天订单量有多大。
结语:
- 看到这里,你觉得你公司的自增id敢不敢乱用,是不是不但只是技术那么简单了。
- 合格的架构,不单止某个技术厉害,更要考虑和业务-商业上的结合。
- 正常的业务表,会用自增id,但是也会加个业务id,比如下面的
CREATE TABLE `USER` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`biz_id` varchar(64) DEFAULT NULL COMMENT '业务id',
`name` varchar(128) DEFAULT NULL COMMENT '昵称',
`pwd` varchar(124) DEFAULT NULL COMMENT '密码',
`head_img` varchar(524) DEFAULT NULL COMMENT '头像',
`phone` varchar(64) DEFAULT '' COMMENT '手机号',
`login_type` int(10) DEFAULT NULL COMMENT '登录类型',
`email` varchar(128) DEFAULT NULL COMMENT '邮箱',
`sex` tinyint(2) DEFAULT '1' COMMENT '0表示女,1表示男',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`roles` varchar(11) DEFAULT NULL COMMENT '1,2,3,数字权限,逗号分隔',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=100000 DEFAULT CHARSET=utf8;
增加了 biz_id,这个就是业务id, 如果有关联,则用 biz_id进行关联并返回,这个可以是varchar类型,long雪花算法.
其实最靠谱的就是,不要把有业务规则的id暴露给用户,不止id字段,类似的敏感字段都是
第7集 短链服务问题解决方案讲解-短链码生成解决方案《下》
简介: 短链URL服务问题解决方案讲解-短链码生成解决方案《下》
哈希算法:将一个元素映射成另一个元素
- 加密哈希,如SHA256、MD5(上集讲了)
- 非加密哈希,如MurMurHash,CRC32
MurMurHash
Murmur哈希是一种非加密散列函数,适用于一般的基于散列的查找。
它在2008年由Austin Appleby创建,在Github上托管,名为“SMHasher” 的测试套件。
它也存在许多变种,所有这些变种都已经被公开。
该名称来自两个基本操作,乘法(MU)和旋转(R)--来自百科
是一种【非加密型】哈希函数且【随机分布】特征表现更良好
由于是非加密的哈希函数,性能会比MD5强
再很多地方都用到比如Guava、Jedis、HBase、Lucence等
存在两个版本
- MurmurHash2(产生32位或64位值)
- MurmurHash3(产生32位或128位值)
数据量
- MurmurHash的 32 bit 能表示的最大值近 43 亿的10进制
- 满足多数业务,如果接近43亿则冲突概率大
- 产品目标【超理想情况】
首年日活用户: 10万
首年日新增短链数据:10万*50 = 500万
年新增短链数:500万 * 365天 = 18.2亿
年新增用户数:50万/1年
年营收目标: 10万付费用户 * 客单价200元 = 2千万
新增短链:50条/用户每日
- MurMurHash得到的数值是10进制,一般会转化为62进制进行缩短
- 例子
- 10进制:1813342104
- 转62进制:1YIB7i
- https://tool.lu/hexconvert/
- 例子
- 常规短链码是6~8位数字+大小写字母组合
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
6 位 62 进制数可表示 568 亿个短链(62的6次方,每位都有62个可能,如果扩大位数到7位,则可以支持3万5200亿)
- MurmurHash的 32 bit 满足多数业务 43亿
- 拼接上库-表位则可以表示更多数据(后续会讲分库分表的,库表位)
- 7位则可以到到 43亿 * 62 = 2666亿
- 8位则可以到到 2666亿 * 62 = 1.65万亿条数据
- 结合短链过期数据归档,理论上满足未来全部需求了
- 数据库存储
- 单表1千万 * 62个库 * 62表 = 384亿数据
第十九章 短链服务-Murmur哈希算法封装组件
第1集 Guava框架里面的Murmur哈希算法测试
简介: Guava框架里面的Murmur哈希算法测试
- Guava框里里面自带Murmur算法
- 单元测试
@Test
public void testMurmurHash() {
for (int i = 0; i < 50; i++) {
int num1 = random.nextInt(1000000);
int num2 = random.nextInt(1000000);
int num3 = random.nextInt(1000000);
String originalUrl = num1 + "xdclass+" + num2 + ".net" + num3;
long murmur3_32 = Hashing.murmur3_32().hashUnencodedChars(originalUrl).padToLong();
System.out.println("murmur3_32="+murmur3_32);
}
}
- CommonUtil工具类
/**
* murmur hash算法
* @param param
* @return
*/
public static long murmurHash32(String param){
long murmur32 = Hashing.murmur3_32().hashUnencodedChars(param).padToLong();
return murmur32;
}
第2集 短链生成组件ShortLinkComponent封装
简介: 短链生成组件ShortLinkComponent封装
- 创建短链组件类 ShortLinkComponent
/**
* 创建短链
* @param originalUrl
* @return db编码+6位短链编码
*/
public String createShortLinkCode(String originalUrl){
long murmur32 = CommonUtil.murmurHash32(originalUrl);
//转62进制
String shortLinkCode = encodeToBase62(murmur32);
return code;
}
- 10进制转62进制
private static final String CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* 10进制转62进制
* @param num
* @return
*/
private static String encodeToBase62(long num) {
//StringBuffer:线程安全; StringBuilder:线程不安全
StringBuffer sb = new StringBuffer();
do {
int i = (int) (num % 62);
sb.append(CHARS.charAt(i));
num /= 62;
// num = num/ 62;
} while (num > 0);
String value = sb.reverse().toString();
return value;
}
第3集 组件ShortLinkComponent测试和疑惑解答
简介: 组件ShortLinkComponent测试和疑惑解答
- 单元测试
/**
* 测试短链平台
*/
@Test
public void testCreateShortLink() {
Random random = new Random();
for (int i = 0; i < 100; i++) {
int num1 = random.nextInt(10);
int num2 = random.nextInt(1000000);
int num3 = random.nextInt(1000000);
String originalUrl = num1 + "xdclass" + num2 + ".net" + num3;
String shortLinkCode = shortLinkComponent.createShortLinkCode(originalUrl);
log.info("originalUrl:" + originalUrl + ", shortLinkCode=" + shortLinkCode);
}
}
- 为什么要用62进制转换,不是64进制?
- 62进制转换是因为62进制转换后只含数字+小写+大写字母
- 而64进制转换会含有/、+这样的符号(不符合正常URL的字符)
- 10进制转62进制可以缩短字符,如果我们要6位字符的话,已经有560亿个组合了
- 看业务情况有些短链也会加入其它特殊字符
- 短链固定6位?肯定不是的,后续会进行分库分表改造
第二十章 短链服务-数据库表建立和业务代码开发
第1集 数据库表模型讲解-短链分组和短链
简介: 数据库表模型讲解-短链分组和短链
- 关系
- 一个账号有多个分组
- 一个分组下有多个短链
- 短链-分组
CREATE TABLE `link_group` (
`id` bigint unsigned NOT NULL,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '组名',
`account_no` bigint DEFAULT NULL COMMENT '账号唯一编号',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
- 短链
CREATE TABLE `short_link` (
`id` bigint unsigned NOT NULL ,
`group_id` bigint DEFAULT NULL COMMENT '组',
`title` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '短链标题',
`original_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '原始url地址',
`domain` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '短链域名',
`code` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '短链压缩码',
`sign` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '长链的md5码,方便查找',
`expired` datetime DEFAULT NULL COMMENT '过期时间,长久就是-1',
`account_no` bigint DEFAULT NULL COMMENT '账号唯一编号',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`del` int unsigned NOT NULL COMMENT '0是默认,1是删除',
`state` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '状态,lock是锁定不可用,active是可用',
`link_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '链接产品层级:FIRST 免费青铜、SECOND黄金、THIRD钻石',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
第2集 MybatisPlus逆向工具生成短链服务相关java对象
简介: MybatisPlus逆向工具生成短链服务相关java对象
- 配置代码
public class MyBatisPlusGenerator {
public static void main(String[] args) {
//1. 全局配置
GlobalConfig config = new GlobalConfig();
// 是否支持AR模式
config.setActiveRecord(true)
// 作者
.setAuthor("二当家小D")
// 生成路径,最好使用绝对路径,window路径是不一样的
//TODO TODO TODO TODO
.setOutputDir("/Users/xdclass/Desktop/demo/src/main/java")
// 文件覆盖
.setFileOverride(true)
// 主键策略
.setIdType(IdType.AUTO)
.setDateType(DateType.ONLY_DATE)
// 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
.setServiceName("%sService")
//实体类结尾名称
.setEntityName("%sDO")
//生成基本的resultMap
.setBaseResultMap(true)
//不使用AR模式
.setActiveRecord(false)
//生成基本的SQL片段
.setBaseColumnList(true);
//2. 数据源配置
DataSourceConfig dsConfig = new DataSourceConfig();
// 设置数据库类型
dsConfig.setDbType(DbType.MYSQL)
.setDriverName("com.mysql.cj.jdbc.Driver")
//TODO TODO TODO TODO
.setUrl("jdbc:mysql://120.79.150.146:3306/dcloud_link?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai")
.setUsername("root")
.setPassword("xdclass.net168");
//3. 策略配置globalConfiguration中
StrategyConfig stConfig = new StrategyConfig();
//全局大写命名
stConfig.setCapitalMode(true)
// 数据库表映射到实体的命名策略
.setNaming(NamingStrategy.underline_to_camel)
//使用lombok
.setEntityLombokModel(true)
//使用restcontroller注解
.setRestControllerStyle(true)
// 生成的表, 支持多表一起生成,以数组形式填写
//TODO TODO TODO TODO
.setInclude("link_group","short_link");
//4. 包名策略配置
PackageConfig pkConfig = new PackageConfig();
pkConfig.setParent("net.xdclass")
.setMapper("mapper")
.setService("service")
.setController("controller")
.setEntity("model")
.setXml("mapper");
//5. 整合配置
AutoGenerator ag = new AutoGenerator();
ag.setGlobalConfig(config)
.setDataSource(dsConfig)
.setStrategy(stConfig)
.setPackageInfo(pkConfig);
//6. 执行操作
ag.execute();
System.out.println("======= 小滴课堂 Done 相关代码生成完毕 ========");
}
}
- 导入生成好的代码
- model (为啥不放common项目,如果是确定每个服务都用到的依赖或者类才放到common项目)
- mapper 类接口拷贝
- resource/mapper文件夹 xml脚本拷贝
- controller
- service 不拷贝
第3集 同学遇到的坑-配置文件修改-yml转properties
简介: 配置文件修改-yml转properties
- SpringBoot的配置文件有两种
- 一种是properties结尾的
- 一种是yaml或者yml文件结尾的
- 如果同时存在properties和yml, application.properties里面的属性就会覆盖里application.yml的属性
- yml的注意点
- yml中缩进一定不能使用TAB,否则会报很奇怪的错误
- yml每个的冒号后面一定都要加一个空格
- 第一个是yml是支持中文内容的,properties想使用中文需要unicode编码
- 账号服务配置转换
- 在线转换工具:https://www.toyaml.com
server.port=8001
spring.application.name=dcloud-account
#----------服务注册和发现--------------
spring.cloud.nacos.discovery.server-addr=120.79.150.146:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
#-------redis连接配置-------
spring.redis.client-type=jedis
spring.redis.host=120.79.150.146
spring.redis.password=xdclass.net
spring.redis.port=6379
spring.redis.jedis.pool.max-active=100
spring.redis.jedis.pool.max-idle=100
spring.redis.jedis.pool.min-idle=100
spring.redis.jedis.pool.max-wait=60000
#-------分库分表数据源配置-------
spring.shardingsphere.datasource.names=ds0
spring.shardingsphere.datasource.ds0.connectionTimeoutMilliseconds=30000
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.idleTimeoutMilliseconds=60000
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://120.79.150.146:3306/dcloud_account?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds0.maintenanceIntervalMilliseconds=30000
spring.shardingsphere.datasource.ds0.maxLifetimeMilliseconds=1800000
spring.shardingsphere.datasource.ds0.maxPoolSize=50
spring.shardingsphere.datasource.ds0.minPoolSize=50
spring.shardingsphere.datasource.ds0.password=xdclass.net168
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.props.sql.show=true
# 指定traffic表的数据分布情况,配置数据节点,行表达式标识符使用 ${...} 或 $->{...},但前者与 Spring 本身的文件占位符冲突,所以在 Spring 环境中建议使用 $->{...}
spring.shardingsphere.sharding.tables.traffic.actual-data-nodes=ds0.traffic_$->{0..1}
#水平分表策略+行表达式分片
spring.shardingsphere.sharding.tables.traffic.table-strategy.inline.algorithm-expression=traffic_$->{ account_no % 2 }
spring.shardingsphere.sharding.tables.traffic.table-strategy.inline.sharding-column=account_no
#id生成策略
spring.shardingsphere.sharding.tables.traffic.key-generator.column=id
spring.shardingsphere.sharding.tables.traffic.key-generator.props.worker.id=${workId}
spring.shardingsphere.sharding.tables.traffic.key-generator.type=SNOWFLAKE
#----------sms短信配置--------------
sms.app-code=6999d4df3e7d48028470bbe517169a8d
sms.template-id=M72CB42894
#----------阿里云OSS配置--------------
aliyun.oss.endpoint=oss-cn-321.aliyuncs.com
aliyun.oss.access-key-id=312
aliyun.oss.access-key-secret=123
aliyun.oss.bucketname=dcloud-link
- 短链服务相关配置文件(复制账号微服务配置修改)
第4集 短链分组管理-CURD接口开发实战《上》
简介: 短链分组管理-CURD接口开发实战《上》
- 配置登录拦截器
- 新增接口
- 删除接口
第5集 短链分组管理-CURD接口开发实战《下》
简介: 短链分组管理-CURD接口开发实战《下》
- 查询详情
- 查询用户全部分组
- 更新分组接口
第6集 短链分组管理-水平分库分表配置实战《青铜玩法》
简介: 短链分组管理-水平分库分表配置实战
- 需求
- 未来2年,短链平台累计5百万用户
- 短链组:一个用户30个组,就是1.5亿个组
- 单表不超过1千万数据,需要分15张表
- 进一步延伸,进行水平分库分表,比如 2库、4库、8库、16库
- 一个库一张表
- 需要降低单表数据量,进行水平分库分表
- 分库数量:线上分16库,本地分2库即可
- 分片key: account_no,查询维度都是根据account_no进行查询
- 分片策略:行表达式分片策略 InlineShardingStrategy
- 未来2年,短链平台累计5百万用户
- 配置
#----------短链组,策略:水平分库,不水平分表--------------
# 先进行水平分库, 水平分库策略,行表达式分片
spring.shardingsphere.sharding.tables.link_group.database-strategy.inline.sharding-column=account_no
spring.shardingsphere.sharding.tables.link_group.database-strategy.inline.algorithm-expression=ds$->{account_no % 2}
第二十一章 短链服务分库分表-如何做到扩容免数据迁移《黄金玩法》
第1集 短链服务-ShortLink分库分表解决方案讲解《青铜》
简介: 短链服务-ShortLink分库分表解决方案讲解
- 前言
- 因为有些参数是和购买大课支付订单绑定,每周动态更新,小滴课堂官网开通权限
- 所以买了盗版的同学没法实操重要环节的内容。
- 如果发现你买了盗版,帮我举报。联系我这边这边大课会有特殊折扣优惠
- 加我微信,都是学习的同学,小的付出带来涨薪和技术增长,6分之1以上月薪即可回本
- 群里的学习氛围-技术讨论也是盗版没法给到的
短链分库分表-小滴课堂老王 同学想到的
- 老王同学:老师,很容易的,前面学了那么多直接用Inline进行分库分表
数据量预估
首年日活用户: 10万
首年日新增短链数据:10万*50 = 500万
年新增短链数:500万 * 365天 = 18.2亿
往高的算就是100亿,支撑3年
分库分表策略
分库分表
- 16个库, 每个库64个表,总量就是 1024个表
分片键:短链码 code
- 比如 g1.fit/92AEva 的短链码 92AEva
分库分表算法:短链码进行hash取模
库ID = 短链码hash值 % 库数量
表ID = 短链码hash值 / 库数量 % 表数量
优点
- 保证数据较均匀的分散落在不同的库、表中,可以有效的避免热点数据集中问题,
- 分库分表方式清晰易懂
问题
- 扩容不是很方便,需要数据迁移
- 需要一次性建立16个库, 每个库64个表,总量就是 1024个表,浪费资源
第2集 短链服务-分库分表扩容免数据迁移解决方案讲解《黄金玩法》
简介: 短链服务-分库分表免迁移扩容解决方案讲解《黄金玩法》
- 需要解决的问题
- 数据量增加,扩容避免迁移数据或者免迁移
- 前期数据量不多,不浪费库表系统资源
- 分库分表:16个库, 每个库64个表,总量就是 1024个表
- 讨论区直达
- 强调一遍
- 一定要自己多思考,我说的也不一定对,留坑也说不准
- 要反驳我的内容,才能提高自己的能力
- 真心要求大家提高自己的思考能力,不能我说啥就听啥
- 举一反三,才是咱们小滴课堂出来的同学,真正靠技术说话的人
- 短链码
- 比如 g1.fit/92AEva 的短链码 92AEva
- 如何做?
- 从短链码入手-增加库表位
- 类似案例-阿里这边商品订单号-里面也包括了库表信息(规则不能说)
第3集 短链服务-分库分表相关库表建立
简介: 短链服务-分库分表相关库表建立
- 为啥能做到免迁移扩容?
- A92AEva1
- 由于短链码的前缀和后缀是是固定的,所以扩容也不影响旧的数据
- 类似的免迁移扩容策略还有哪些?
- 时间范围分库分表
- id范围分库分表
- 三个库
- 一个库两个表
- properties配置多库表
spring.shardingsphere.datasource.names=ds0,ds1,dsa
#ds0配置
spring.shardingsphere.datasource.ds0.connectionTimeoutMilliseconds=30000
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.idleTimeoutMilliseconds=60000
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://120.79.150.146:3306/dcloud_link_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.maintenanceIntervalMilliseconds=30000
spring.shardingsphere.datasource.ds0.maxLifetimeMilliseconds=1800000
spring.shardingsphere.datasource.ds0.maxPoolSize=50
spring.shardingsphere.datasource.ds0.minPoolSize=50
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=xdclass.net168
#ds1配置
spring.shardingsphere.datasource.ds1.connectionTimeoutMilliseconds=30000
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.idleTimeoutMilliseconds=60000
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://120.79.150.146:3306/dcloud_link_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.maintenanceIntervalMilliseconds=30000
spring.shardingsphere.datasource.ds1.maxLifetimeMilliseconds=1800000
spring.shardingsphere.datasource.ds1.maxPoolSize=50
spring.shardingsphere.datasource.ds1.minPoolSize=50
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=xdclass.net168
#dsa配置
spring.shardingsphere.datasource.dsa.connectionTimeoutMilliseconds=30000
spring.shardingsphere.datasource.dsa.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.dsa.idleTimeoutMilliseconds=60000
spring.shardingsphere.datasource.dsa.jdbc-url=jdbc:mysql://120.79.150.146:3306/dcloud_link_a?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.dsa.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.dsa.maintenanceIntervalMilliseconds=30000
spring.shardingsphere.datasource.dsa.maxLifetimeMilliseconds=1800000
spring.shardingsphere.datasource.dsa.maxPoolSize=50
spring.shardingsphere.datasource.dsa.minPoolSize=50
spring.shardingsphere.datasource.dsa.username=root
spring.shardingsphere.datasource.dsa.password=xdclass.net168
第4集 短链服务水平分库分表实战-标准分片策略-精准分片算法《上》
简介: 短链服务分库分表实战-标准分片策略-精准分片算法
- 水平分库-标准分片策略-精准分片算法 Ae23asa1
public class CustomDBPreciseShardingAlgorithm implements PreciseShardingAlgorithm<String> {
/**
* @param availableTargetNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
* @param shardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
//获取短链码第一位,即库位
String codePrefix = shardingValue.getValue().substring(0, 1);
for (String targetName : availableTargetNames) {
//获取库名的最后一位,真实配置的ds
String targetNameSuffix = targetName.substring(targetName.length() - 1);
//如果一致则返回
if (codePrefix.equals(targetNameSuffix)) {
return targetName;
}
}
//抛异常
throw new BizException(BizCodeEnum.DB_ROUTE_NOT_FOUND);
}
}
- 配置
#----------短链,策略:分库+分表--------------
# 先进行水平分库,然后再水平分表
spring.shardingsphere.sharding.tables.short_link.database-strategy.standard.sharding-column=code
spring.shardingsphere.sharding.tables.short_link.database-strategy.standard.precise-algorithm-class-name=net.xdclass.strategy.CustomDBPreciseShardingAlgorithm
第5集 短链服务水平分库分表实战-标准分片策略-精准分片算法《下》
简介: 短链服务分库分表实战-标准分片策略-精准分片算法
- 水平分表-标准分片策略-精准分片算法
public class CustomTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<String> {
/**
* @param availableTargetNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
* @param shardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
//获取逻辑表名
String targetName = availableTargetNames.iterator().next();
String value = shardingValue.getValue();
//短链码最后一位
String codePrefix = value.substring(value.length()-1);
//拼装actual table
return targetName + "_" + codePrefix;
}
}
- 配置文件
# 水平分表策略,自定义策略。 真实库.逻辑表
spring.shardingsphere.sharding.tables.short_link.actual-data-nodes=ds0.short_link,ds1.short_link,dsa.short_link
spring.shardingsphere.sharding.tables.short_link.table-strategy.standard.sharding-column=code
spring.shardingsphere.sharding.tables.short_link.table-strategy.standard.precise-algorithm-class-name=net.xdclass.strategy.CustomTablePreciseShardingAlgorithm
#id生成策略
spring.shardingsphere.sharding.tables.short_link.key-generator.column=id
spring.shardingsphere.sharding.tables.short_link.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.short_link.key-generator.props.worker.id=${workerId}
第6集 短链服务-短链码配置生成库表位实战
简介: 短链服务-短链码配置生成库表位实战
- 分库位
public class ShardingDBConfig {
/**
* 存储数据库位置编号
*/
private static final List<String> dbPrefixList = new ArrayList<>();
private static Random random = new Random();
//配置启用那些库的前缀
static {
dbPrefixList.add("0");
dbPrefixList.add("1");
dbPrefixList.add("a");
}
/**
* 获取随机的前缀
* @return
*/
public static String getRandomDBPrefix(){
int index = random.nextInt(dbPrefixList.size());
return dbPrefixList.get(index);
}
}
- 分表位
public class ShardingTableConfig {
/**
* 存储数据表位置编号
*/
private static final List<String> tableSuffixList = new ArrayList<>();
private static Random random = new Random();
//配置启用那些表的后缀
static {
tableSuffixList.add("0");
tableSuffixList.add("a");
}
/**
* 获取随机的后缀
* @return
*/
public static String getRandomTableSuffix(){
int index = random.nextInt(tableSuffixList.size());
return tableSuffixList.get(index);
}
}
- 短链码配置
String code = ShardingDBConfig.getRandomDBPrefix() + shortLinkCode + ShardingTableConfig.getRandomTablePrefix();
第7集 短链服务-Manager层模块CRUD开发
简介: 短链服务-Manager层模块CRUD开发
- 代码
public interface ShortLinkManager {
/**
* 新增域名
*
* @param shortLinkDO
* @return
*/
int addShortLink(ShortLinkDO shortLinkDO);
/**
* 根据短链码找内容
*
* @param shortLinkCode
* @return
*/
ShortLinkDO findByShortLinkCode(String shortLinkCode);
/**
* 根据短链码和accountNo删除
*
* @param shortLinkCode
* @param accountNo
* @return
*/
int del(String shortLinkCode, Long accountNo);
}
@Component
@Slf4j
public class ShortLinkManagerImpl implements ShortLinkManager {
@Autowired
private ShortLinkMapper shortLinkMapper;
@Override
public int addShortLink(ShortLinkDO shortLinkDO) {
return shortLinkMapper.insert(shortLinkDO);
}
@Override
public ShortLinkDO findByShortLinkCode(String shortLinkCode) {
ShortLinkDO shortLinkDO = shortLinkMapper.selectOne(new QueryWrapper<ShortLinkDO>().eq("code", shortLinkCode).eq("del", 0));
return shortLinkDO;
}
/**
* 逻辑删除
*
* @param shortLinkCode
* @param accountNo
* @return
*/
@Override
public int del(String shortLinkCode, Long accountNo) {
ShortLinkDO shortLinkDO = new ShortLinkDO();
shortLinkDO.setDel(1);
int rows = shortLinkMapper.update(shortLinkDO, new QueryWrapper<ShortLinkDO>()
.eq("code", shortLinkCode).eq("account_no", accountNo));
return rows;
}
}
第8集 短链服务-自定义分库分表策略单元测试实战
简介: 短链服务-自定义分库分表策略单元测试实战
单元测试
- 保存
@Autowired
private ShortLinkManager shortLinkManager;
/**
* 保存
*/
@Test
public void testSaveShortLink() {
Random random = new Random();
//for (int i = 0; i < 10; i++) {
int num1 = random.nextInt(10);
int num2 = random.nextInt(10000000);
int num3 = random.nextInt(10000000);
String originalUrl = num1 + "xdclass" + num2 + ".net" + num3;
String shortLinkCode = shortLinkComponent.createShortLinkCode(originalUrl);
ShortLinkDO shortLinkDO = new ShortLinkDO();
shortLinkDO.setCode(shortLinkCode);
shortLinkDO.setAccountNo(Long.valueOf(num3));
shortLinkDO.setSign(originalUrl);
shortLinkDO.setDel(0);
shortLinkManager.addShortLink(shortLinkDO);
//}
}
- 查找
@Test
public void testFind() {
ShortLinkDO shortLinkDO = shortLinkManager.findByShortLinkCode("03aAg0la");
log.info(shortLinkDO.toString());
}
第9集 加权负载均衡思想应用-数据库表扩容-数据不均匀问题解决方案
简介: 加权负载均衡思想应用-数据库表扩容-数据不均匀问题解决方案
- 问题
- 假如前期分三个库,一个库两个表,项目火爆,数据量激增,进行扩容
- 增加了新的数据库表位,会导致旧的库表比新的库表数据量多,且容易出现超载情况
Nginx加权负载均衡的应用(架构大课)
不同的库表位分配的概率不一样,类似的中间件应用场景有nginx
Nginx常见的负载均衡策略
节点轮询(默认)
weight 权重配置
- 简介:weight和访问比率成正比,数字越大,分配得到的流量越高
- 场景:服务器性能差异大的情况使用
upstream lbs {
server 192.168.159.133:8080 weight=5;
server 192.168.159.133:8081 weight=10;
}
- 加权解决方式(作业)
- 库表位可以使用对象形式,配置权重,避免数据倾斜、数据集中
- 编写算法,根据不同的,配置权重,不同的库表位配置不同的权重
- 加权配置,list重复添加出现的高频的库表位
- 是不是可以把这个亮点记录下
- 面试的时候再面试官前面说下项目难点和你的解决方案
- 你想到的解决方案,方便又清晰,还省服务器资源和避免了问题
- 业务量超过评估量,分库分表-二次扩容的时候避免数据迁移
- 不用一次性建立很多个库表,可以动态添加,节省服务器资源
- 使用加权库表位算法,解决扩容后数据倾斜不均匀问题
相关文章
- ububntu 搭建sss服务无法开机自启动
- DNS服务器配置不需要配置客户端_DNS服务使用哪个端口
- 架构师带你搞明白微服务进阶场景实战:服务之间的数据依赖问题
- 基于网络抓包实现K8S中微服务的应用级监控
- Spring Cloud(三):服务提供与调用详解编程语言
- MySQL与微服务架构的无缝集成:优化数据处理与提升业务效率的新策略(mysql微服务)
- MySQL发布最新数据更新,用户可以获得更多高效、稳定、安全的数据库服务。(mysql最新数据)
- 服务Linux系统快捷关闭HTTPd服务指南(linux关闭httpd)
- 响应Oracle服务无响应:现状与对策(oracle服务没有)
- Mysql服务运维:成就一个稳定的互联网世界(mysql 运维)
- Oracle 专用服务器为企业加快数据处理提供可靠服务(oracle 专用服务器)