大部分时候,需要通过shell
脚本批量处理一些数据,在分布式环境下,数据库表的主键存储的都是分布id,通过Java代码生成。
shell脚本都是通过mysql
命令生成insert
语句,以前生成insert
语句时,我都是先select MAX(id) from table
赋值到MAX_ID
,然后拼接,类似于
max_id_sql="select MAX(id) from table";
MAX_ID="$(query ${max_id_sql})";
echo "insert into table (id, col1,col2) values" > insert.sql
count=0;
while read -r line
do
((count++))
col1="$(echo ${line} | cut -f1)";
col2="$(echo ${line} | cut -f2)";
echo "(${MAX_ID} + ${count}, ${col1}, ${col2})" >> insert.sql
done < datas;
这样的弊端是要自己计算,且id
不符合规律,代码量还不小,同时写多张表难管理,抽空完成了一个shell
版的Snowflake-id
生成脚本
#!/bin/bash
# 定义起始时间戳(2024-01-01 00:00:00 UTC)时间越小,生成的id越大
readonly __BEGIN_EPOCH=$(date -d "2000-01-01 00:00:00" +%s%3N)
# 从MAC地址中提取MACHINE_ID
readonly __MACHINE_ID=$(ip link show | grep -Po 'link/ether \K[0-9a-f:]{17}' | head -n 1 | tr -d ':' | cut -c1-8 | xxd -r -p | od -An -t u4 | tr -d ' ' | awk '{print $1 % 1024}')
# 从IP地址中提取DATACENTER_ID
readonly __DATACENTER_ID=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n 1 | cut -d'.' -f3 | awk '{print $1 % 1024}')
# 序列号初始化为0
_SN_SEQUENCE=0
# 上一次的时间戳
_SN_LAST_TIMESTAMP=0
# 🔒文件
readonly _SN_LOCKFILE="/tmp/snowflake-id.lock"
# 生成Snowflake ID的函数
next-id() {
exec 200>> "${_SN_LOCKFILE}";
flock -w 1 200
# 获取当前时间戳(毫秒)
local current_timestamp=$(date +%s%N | cut -b1-13)
# 如果当前时间戳小于上次的时间戳,则报错
if [ "${current_timestamp}" -lt "${_SN_LAST_TIMESTAMP}" ]; then
echo "Clock error : offset : $((_SN_LAST_TIMESTAMP - current_timestamp)) ms" >&2
flock -u 200;
return 1
fi
# 如果时间戳相同,则递增序列号, 重复时使用
# local timestamp_diff=$((current_timestamp - _SN_LAST_TIMESTAMP))
# if [ "${timestamp_diff}" -le 10 ]; then
if [ "${current_timestamp}" -eq "${_SN_LAST_TIMESTAMP}" ]; then
_SN_SEQUENCE=$(( (_SN_SEQUENCE + 1) & 4095 )) # 12位,最大4095
if [ "$_SN_SEQUENCE" -eq 0 ]; then
# 序列号溢出,等待下一毫秒
sleep 0.001
current_timestamp=$(date +%s%N | cut -b1-13)
fi
else
_SN_SEQUENCE=0
fi
# 更新上次的时间戳
_SN_LAST_TIMESTAMP=$current_timestamp
# 计算偏移时间戳
local time_diff=$((current_timestamp - __BEGIN_EPOCH))
# 检查时间戳是否超过41位的限制
local max_time_diff=$(( (1 << 41) - 1 ))
if [ "${time_diff}" -gt "${max_time_diff}" ]; then
echo "Time over flow : ${time_diff} > ${max_time_diff}" >&2
flock -u 200;
return 2
fi
local lastGenerateId=$(((time_diff << 22) | (__DATACENTER_ID << 17) | (__MACHINE_ID << 7) | _SN_SEQUENCE));
flock -u 200;
echo "${lastGenerateId}";
}
# # 测试生成多个Snowflake ID
# for i in {1..10}; do
# # next-id
# # echo "${lastGenerateId}";
# echo "$(next-id)"
# done
使用时直接调用即可
#!/bin/bash
. snowflake-id.sh
id="$(next-id)"
现在机器上测试,通过后再使用。
可能会出现重复的问题,重复时,将next-id
函数的最后两行修改
lastGenerateId=$(((time_diff << 22) | (__DATACENTER_ID << 17) | (__MACHINE_ID << 7) | _SN_SEQUENCE));
flock -u 200;
在调用时先调用函数,再从全局变量lastGenerateId
获取,这样的好处是不会重复,但只能在一个shell进程中使用
next-id;
echo "${lastGenerateId}";
如果仍然重复,可将前面的注释放开
# 如果时间戳相同,则递增序列号, 重复时使用
local timestamp_diff=$((current_timestamp - _SN_LAST_TIMESTAMP))
if [ "${timestamp_diff}" -le 10 ]; then
# if [ "${current_timestamp}" -eq "${_SN_LAST_TIMESTAMP}" ]; then
_SN_SEQUENCE=$(( (_SN_SEQUENCE + 1) & 4095 )) # 12位,最大4095
if [ "$_SN_SEQUENCE" -eq 0 ]; then
# 序列号溢出,等待下一毫秒
sleep 0.001
current_timestamp=$(date +%s%N | cut -b1-13)
fi
else
_SN_SEQUENCE=0
fi
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。