点击进入我的个人博客
在采用分库分表的数据库结构设计时,往数据库中新增数据(insert)不能在通过自增id来保证id唯一了,因为分表两个同样的表在不同服务器上自增id会重复,所有必须通过手动添加id来保证id的唯一性,snowflake (雪花)算法(twitter出品)就是用来生成唯一主键值很好的选择
文章目录
一、分库分表设计1.分库2.分表3.分库分表插件
二、雪花算法1.概述2.IdWorker工具类(分布式id生成器)3.分布式id生成器的使用
一、分库分表设计
1.分库
就是为每个模块单独建立一个数据库,在数据不多的情况下,数据库可以集中在同一个服务器上,可以节约成本,当某个数据库数据比较多时,则可以把该数据库单独迁移到一台服务器上提高性能
2.分表
当某个数据库数据非常多单台服务器也不能承载这么多数据时,则可以在新的服务器创建一样数据库和表,进行数据存储,但是分表之后要解决数据存储和查询等问题,比如新存入一个数据要存到哪个服务器上mysql支持的数据是百万级别,而orcal支持的是千万级别,比如当mysql数据的索引超过百万时,性能方面会下降严重
3.分库分表插件
mycatsharding jdbc
二、雪花算法
1.概述
41bit 的时间戳可以支持该算法使用到2082年10bit 的工作机器id可以支持1024台机器序列号支持 1毫秒产生4096个自增序列id整体上按照时间自增排序整个分布式系统内不会产生 ID碰撞每秒能够产生 26万ID左右
2.IdWorker工具类(分布式id生成器)
package entity
.util
;
import java
.lang
.management
.ManagementFactory
;
import java
.net
.InetAddress
;
import java
.net
.NetworkInterface
;
public class IdWorker {
private final static long twepoch
= 1288834974657L
;
private final static long workerIdBits
= 5L
;
private final static long datacenterIdBits
= 5L
;
private final static long maxWorkerId
= -1L
^ (-1L
<< workerIdBits
);
private final static long maxDatacenterId
= -1L
^ (-1L
<< datacenterIdBits
);
private final static long sequenceBits
= 12L
;
private final static long workerIdShift
= sequenceBits
;
private final static long datacenterIdShift
= sequenceBits
+ workerIdBits
;
private final static long timestampLeftShift
= sequenceBits
+ workerIdBits
+ datacenterIdBits
;
private final static long sequenceMask
= -1L
^ (-1L
<< sequenceBits
);
private static long lastTimestamp
= -1L
;
private long sequence
= 0L
;
private final long workerId
;
private final long datacenterId
;
public IdWorker(){
this.datacenterId
= getDatacenterId(maxDatacenterId
);
this.workerId
= getMaxWorkerId(datacenterId
, maxWorkerId
);
}
public IdWorker(long workerId
, long datacenterId
) {
if (workerId
> maxWorkerId
|| workerId
< 0) {
throw new IllegalArgumentException(String
.format("worker Id can't be greater than %d or less than 0", maxWorkerId
));
}
if (datacenterId
> maxDatacenterId
|| datacenterId
< 0) {
throw new IllegalArgumentException(String
.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId
));
}
this.workerId
= workerId
;
this.datacenterId
= datacenterId
;
}
public synchronized long nextId() {
long timestamp
= timeGen();
if (timestamp
< lastTimestamp
) {
throw new RuntimeException(String
.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp
- timestamp
));
}
if (lastTimestamp
== timestamp
) {
sequence
= (sequence
+ 1) & sequenceMask
;
if (sequence
== 0) {
timestamp
= tilNextMillis(lastTimestamp
);
}
} else {
sequence
= 0L
;
}
lastTimestamp
= timestamp
;
long nextId
= ((timestamp
- twepoch
) << timestampLeftShift
)
| (datacenterId
<< datacenterIdShift
)
| (workerId
<< workerIdShift
) | sequence
;
return nextId
;
}
private long tilNextMillis(final long lastTimestamp
) {
long timestamp
= this.timeGen();
while (timestamp
<= lastTimestamp
) {
timestamp
= this.timeGen();
}
return timestamp
;
}
private long timeGen() {
return System
.currentTimeMillis();
}
protected static long getMaxWorkerId(long datacenterId
, long maxWorkerId
) {
StringBuffer mpid
= new StringBuffer();
mpid
.append(datacenterId
);
String name
= ManagementFactory
.getRuntimeMXBean().getName();
if (!name
.isEmpty()) {
mpid
.append(name
.split("@")[0]);
}
return (mpid
.toString().hashCode() & 0xffff) % (maxWorkerId
+ 1);
}
protected static long getDatacenterId(long maxDatacenterId
) {
long id
= 0L
;
try {
InetAddress ip
= InetAddress
.getLocalHost();
NetworkInterface network
= NetworkInterface
.getByInetAddress(ip
);
if (network
== null
) {
id
= 1L
;
} else {
byte[] mac
= network
.getHardwareAddress();
id
= ((0x000000FF & (long) mac
[mac
.length
- 1])
| (0x0000FF00 & (((long) mac
[mac
.length
- 2]) << 8))) >> 6;
id
= id
% (maxDatacenterId
+ 1);
}
} catch (Exception e
) {
System
.out
.println(" getDatacenterId: " + e
.getMessage());
}
return id
;
}
}
3.分布式id生成器的使用
创建bean对象
@Bean
public IdWorker
createIdWorker(){
return new IdWorker (1,1);
}
在service层生成分布式id,并且在调用dao插入数据之前替换原来的id
public void save(Article article
) {
String id
= idWorker
.nextId
( ) + "";
article
.setId
( id
);
articleDao
.insert
( article
);
}