引言
I 预备知识
SQL 层面:SQL 的生命周期、权限管理、count(*) 的底层原理、底层的排序原理、连表原理。
存储引擎层面: InnoDB 存储引擎的底层架构、索引的算法、事务的原理、锁机制、隔离机制、幻读
1.1 mysql 各字段类型存储文本信息的最大值
- text,最大65535字节
- mediumtext,最大16777215字节
- longtext,2的32次方减1个,即4294967295个字节
1.2 date_sub的用法
语法:date_sub(date,interval expr type),函数从日期减去指定的时间间隔,
例子:删除时间小于昨天的数据
delete from car_viol where `create` < date_sub(curdate(), interval 1 day);
1.3 innodb_io_capacity 参数
MySQL 数据库并不是直接根据硬盘的性能来调节其 write 速度,而是靠 innodb_io_capacity 参数来告诉 MySQL 数据库磁盘的性能。
1.4 过程和函数
过程(procedure)又叫存储过程(stored procedure),是一个有名称的PL/SQL程序块 。
过程相当于java中的方法, 它注重的是实现某种业务功能 。
函数(function)也相当于java中的方法,它 注重计算并且总是有返回结果 。
过程和函数都是能够永久存储在数据库中的程序代码块,应用时通过调用执行 。
https://blog.csdn.net/z929118967/article/details/128313118
1.5 查询mysql有没有开启时间功能的支持
/*查看事件功能是否开启*/
show variables like 'event_scheduler';
开启事件功能
set global event_scheduler = on;
# off 代表关,on 代表开
II 定时任务
利用MySql的事件机制完成定时任务: 在指定的时间调用指定的存储过程。
event机制是mysql5.1版本开始引入的,这意味着版本低于5.1的可能无法使用
2.1 实现定时操作的功能
- 创建了一个存储过程,它可以删除时间小于昨天的数据
create procedure del_car_v()
begin
delete from car_v where `create` < date_sub(curdate(), interval 1 day);
end
- 创建一个事件,让事件按照某种规则去调用存储过程。事件创建好以后就会立刻执行一次,并且一般是默认开启的。
从2023年1月2日零点开始每隔一天自动调用之前写好的存储过程。
create event `e_update_user_ticket`
on schedule every 1 day starts '2023-01-02 00:00:00' # 1 day 代表一天一次, 2 year(2年一次)。
on completion not preserve enable do call del_car_v();
2.2 控制某个事件的运行状态
/*开启事件*/
alter event 事件名 on completion preserve enable;
/*关闭事件*/
alter event 事件名 on completion preserve disable;
III 雪花ID
3.1 自增id作为主键的优缺点
优点:查询效率更快
数据库会选择表的主键作为聚集索引(B+Tree),mysql 在底层是以数据页为单位来存储数据的。InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的,所以主键有序比无序查询效率要快。
缺点:
- 容易导致主键重复
比如导入旧数据时,线上又有新的数据新增,这时就有可能在导入时发生主键重复的异常。为了避免导入数据时出现主键重复的情况,要选择在应用停业后导入旧数据,导入完成后再启动应用。显然这样会造成不必要的麻烦。而UUID作为主键就不用担心这种情况。
- 不利于数据库的扩展
当采用自增id时,分库分表也会有主键重复的问题。UUID则不用担心这种问题。
3.2 雪花ID
自增id会担心主键重复,UUID不能保证有序性,而雪花ID既是有序的,又是唯一的。
snowflake
是Twitter开源的分布式ID生成算法,结果是64bit的Long类型的ID,有着全局唯一和有序递增的特点。
- 最高位是符号位:因为生成的 ID 总是正数,始终为0。
- 41位的时间序列:精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
- 10位的机器标识:10位的长度最多支持部署1024个节点。
12位的计数序列号:序列号即一系列的自增ID,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。
缺点:强依赖机器时钟,如果机器上时钟回拨,有可能会导致主键重复的问题。
3.3 Java实现雪花ID
public class SnowflakeIdWorker {
/**
* 开始时间:2020-01-01 00:00:00
*/
private final long beginTs = 1577808000000L;
private final long workerIdBits = 10;
/**
* 2^10 - 1 = 1023
*/
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long sequenceBits = 12;
/**
* 2^12 - 1 = 4095
*/
private final long maxSequence = -1L ^ (-1L << sequenceBits);
/**
* 时间戳左移22位
*/
private final long timestampLeftOffset = workerIdBits + sequenceBits;
/**
* 业务ID左移12位
*/
private final long workerIdLeftOffset = sequenceBits;
/**
* 合并了机器ID和数据标示ID,统称业务ID,10位
*/
private long workerId;
/**
* 毫秒内序列,12位,2^12 = 4096个数字
*/
private long sequence = 0L;
/**
* 上一次生成的ID的时间戳,同一个worker中
*/
private long lastTimestamp = -1L;
public SnowflakeIdWorker(long workerId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("WorkerId必须大于或等于0且小于或等于%d", maxWorkerId));
}
this.workerId = workerId;
}
public synchronized long nextId() {
long ts = System.currentTimeMillis();
if (ts < lastTimestamp) {
throw new RuntimeException(String.format("系统时钟回退了%d毫秒", (lastTimestamp - ts)));
}
// 同一时间内,则计算序列号
if (ts == lastTimestamp) {
// 序列号溢出
if (++sequence > maxSequence) {
ts = tilNextMillis(lastTimestamp);
sequence = 0L;
}
} else {
// 时间戳改变,重置序列号
sequence = 0L;
}
lastTimestamp = ts;
// 0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000000 00 - 00000000 0000
// 左移后,低位补0,进行按位或运算相当于二进制拼接
// 本来高位还有个0<<63,0与任何数字按位或都是本身,所以写不写效果一样
return (ts - beginTs) << timestampLeftOffset | workerId << workerIdLeftOffset | sequence;
}
/**
* 阻塞到下一个毫秒
*
* @param lastTimestamp
* @return
*/
private long tilNextMillis(long lastTimestamp) {
long ts = System.currentTimeMillis();
while (ts <= lastTimestamp) {
ts = System.currentTimeMillis();
}
return ts;
}
public static void main(String[] args) {
SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(7);
for (int i = 0; i < 10; i++) {
long id = snowflakeIdWorker.nextId();
System.out.println(id);
}
}
}
其他实现例子:
- 美团开源的Leaf:https://github.com/Meituan-Dianping/Leaf
- 百度开源的UidGenerator: https://github.com/baidu/uid-generator
3.4 应用
ID生成策略、订单号生成