日志服务新功能发布(2)--弹性伸缩(Merge/Split)

本文介绍了日志服务中弹性伸缩(Merge/Split)功能,可在流量突增时快速调整分区数量,确保数据处理效率并节约成本。适用于视频网站等应用场景。

在之前的文章《日志服务(原SLS)新功能发布(1)--支持保序写入和消费》中,我们提到了Shard支持Key映射的特性,通过这个特性能够支持对序有需求的应用场景。今天我们给大家介绍一个在削峰填谷或流量突增情况下的功能:弹性伸缩。在生产中我们往往会面临峰值和低值的情况,也会遇到因业务层映射不均衡,导致某一个分区(shard)有非常大流量的场景,弹性伸缩(Merge/Split)就是为此设计的利器。

使用弹性伸缩的应用场景

场景1(视频类):根据峰值、底值弹性扩容,控制成本

用户A是一个视频类网站,晚8点-22点是一天的高峰,会产生大量的点击和访问日志。小A使用日志服务一个logstore进行点击日志收集与消费。

  • 在白天时使用一个Shard(Shard0)分区(写5MB/S、读10MB/S)
  • 当晚间高峰时对该分区进行分裂(Split),分区变为2个分区Shard1,Shard2服务能力(写10MB/S,读20MB/S)
  • 当高峰期过后,通过合并(Merge)将两个分区调整一个分区的服务能力(Shard3),以控制成本

screenshot

场景2(日志保序处理):将不同实例映射到不同分区,调整分区处理能力

用户B是一位程序员,通过日志服务logstore采集数据库节点日志。小B管理的数据库有大有小,大的每分钟产生1MB/S 数据,小的也就0.5 KB。由于数据库日志是需要保序处理的,因此小B通过Hash方式将各数据库映射到唯一的Shard上,在消费时保证保序列。

  • 根据映射规则,DB1-DB3被 hash到 shard1上,DB4-5被映射到Shard2,相安无事
  • 有一天数据库实例DB2, DB3流量突然产生的变化,突破了Shard1处理能力(写5MB/S,读10MB/S)
  • 小B通过分析,决定把DB2,DB3进行拆分,于是根据映射Hash方式调整了Shard1,变成2个新的Shard(Shard3, Shard4),Shard3 服务DB1,DB3, Shard4 服务DB2,流量终于均衡了

screenshot

值得一提的是,伸缩操作都是ms级完成,并且过程是对用户服务是没有任何影响。

关于弹性伸缩(Merge/Split)详细说明

分区是什么

logstore下分为若干个分区,每个分区由两个md5组成的左闭右开的区间组成,每个区间的范围不会相互覆盖,并且所有的区间的范围合并起来就是md5的整个取值范围。logstore的日志必定保存在某一个分区上。

screenshot

以图为例,为了简化说明,这里假设md5的取值范围是00 到ff。这个logstore共有4个分区,范围分别是[00,40),[40,80),[80,C0),[C0,ff)。当写入日志时,用户指定一个md5的key是5f,那么这个请求会落在第1号分区上;如果用户指定md5是8c,那么该请求的数据会落到第2号shard上。
分区的状态有两种,一种是readwrite,可以读写;另一种是readonly,只能读数据,不能写数据。
分区的范围如何划分。创建logstore时,指定分区个数,会自动平均划分整个md5的范围。之后可以通过分裂和合并操作来扩容和缩容分区个数。

logstore需要多少个分区

根据logstore实际的流量,每个分区能够处理5M/s的写数据,和10M/s的读数据。根据流量计算出来需要多少个分区。

当写入的API持续报错403或者500错误时,通过logstore云监控查看流量,判断是否需要增加分区。

当流量变小时,为了节约企业成本,可以通过合并操作减少分区。

分裂和合并操作

分裂操作是把一个readwrite的分区分裂成两个readwrite的分区,同时自己变成readonly的分区。分裂操作是扩容logstore流量的手段。

screenshot

以图为例,1号分区原来的范围是[40,80),指定以60为分界点,把整个范围分成了两份[40,60),[60,80),形成两个新的分区4号分区和5号分区。1号分区变成红色的readonly状态。 在分裂之前写到1号分区上的数据仍然留在1号分区上可供读取,分裂完成后,1号分区不再接收数据,落到40到80之间的数据会选择性的落到4号分区或者5号分区,比如5f这个md5,会写入到4号分区上。

合并操作和分裂操作相反,是把两个相邻的readwrite的分区合并成一个readwrite分区,同时原来的两个分区变成readonly状态。

如何进行分裂与合并

  • 通过API或SDK:SplitMerge
  • 通过管理控制台

弹性伸缩对上下游是否有影响

  • 对上游没有任何影响,透明
  • 对下游消费者而言需要感知Shard数目变化,我们推荐使用我们提供Storm Spout,Spark Stream Library或Client Library(Java、Python等用户),Client Library会根据消费实例数目与Shard数目做负载均衡,过程不丢不重数据
<think> 我们之前已经讨论了如何修复合服脚本中关于解压后目录重命名的问题。用户现在明确指出,脚本无法自动生成merge.1和merge.2文件(实际上是目录),并希望修复。 问题分析: 原脚本中解压后尝试移动`/data/database/merge/tc/201*`目录,但可能存在多个备份文件解压后产生多个目录,或者目录名称不完全是201开头的情况?此外,如果同时解压两个备份,第二次解压后移动会匹配到两个目录(第一个备份产生的目录和第二个备份产生的目录),导致第二次移动失败(因为mv命令不能同时移动多个目录到一个目标)。 我们之前的修复方案已经考虑到了这个问题:通过分别解压后立即重命名,并且每次只重命名第一个匹配的目录。但是用户仍然遇到了问题,可能是由于: 1. 解压后目录名不是以201开头(可能是其他格式,比如时间戳格式变化) 2. 解压后目录不在预期位置(比如解压到了子目录?) 3. 解压失败导致没有目录生成 因此,我们需要: 1. 确保解压成功,否则退出。 2. 解压后,进入目标目录(/data/database/merge/tc/)并查看新生成的目录。我们假设解压后会在当前目录下创建一个新目录(而不是多个目录)。 3. 分别对每个解压操作进行重命名,并且确保每次只处理一个目录。 修复后的步骤: 步骤一:清理并创建合并目录 ```bash # 确保目录存在 mkdir -p /data/database/merge/tc/ # 清理旧的merge目录(如果有) rm -rf /data/database/merge/tc/merge.1 rm -rf /data/database/merge/tc/merge.2 ``` 步骤二:解压第一个备份并重命名 ```bash # 解压S1备份 if ! tar zxvf /data/database/backup/dpcq_tc_S1/tc_S1_* -C /data/database/merge/tc/; then echo "S1备份解压失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 查找最新创建的目录(假设解压后会在tc目录下创建至少一个目录) # 我们使用时间戳来找到最新的目录(因为解压会创建新目录) latest_dir1=$(find /data/database/merge/tc/ -maxdepth 1 -type d -printf '%T@ %p\n' | sort -n | tail -1 | awk '{print $2}') # 如果找到了,重命名 if [ -n "$latest_dir1" ]; then mv "$latest_dir1" /data/database/merge/tc/merge.1 echo "已将目录 $latest_dir1 重命名为 merge.1" >>/home/tcdplog.txt else echo "未找到S1解压后的目录 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi ``` 步骤三:解压第二个备份并重命名 ```bash # 解压S2备份 if ! tar zxvf /data/database/backup/dpcq_tc_S2/tc_S2_* -C /data/database/merge/tc/; then echo "S2备份解压失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 再次查找最新创建的目录(此时排除刚刚重命名的merge.1,因为它是之前创建的) latest_dir2=$(find /data/database/merge/tc/ -maxdepth 1 -type d ! -path /data/database/merge/tc/merge.1 -printf '%T@ %p\n' | sort -n | tail -1 | awk '{print $2}') # 如果找到了,重命名 if [ -n "$latest_dir2" ]; then mv "$latest_dir2" /data/database/merge/tc/merge.2 echo "已将目录 $latest_dir2 重命名为 merge.2" >>/home/tcdplog.txt else echo "未找到S2解压后的目录 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi ``` 步骤四:等待合并目录生成(与之前相同) ```bash timeout=120 elapsed=0 while (( elapsed < timeout )) do if [[ -d "/data/database/merge/tc/merge.1" && -d "/data/database/merge/tc/merge.2" ]]; then echo "合并目录已生成 $(date +%F_%T)" >>/home/tcdplog.txt break else echo "等待合并目录生成... ${elapsed}s" >>/home/tcdplog.txt sleep 5 ((elapsed += 5)) fi done if (( elapsed >= timeout )); then echo "错误:合并目录生成超时 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi ``` 步骤五:停止服务并执行合服(与之前相同) ```bash cd /data/dpcq_tc_S1/server ./mgectl stop || { echo "S1服务停止失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 } cd /data/dpcq_tc_S2/server ./mgectl stop || { echo "S2服务停止失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 } cd /data/dpcq_tc_S1/server ./mgectl live merge || { echo "合服启动失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 } ``` 步骤六:监控合服日志(与之前相同) ```bash timeout_merge=3600 elapsed_merge=0 echo "开始监控合服进度 $(date +%F_%T)" >>/home/tcdplog.txt while (( elapsed_merge < timeout_merge )) do if grep -q "合服完毕,恭喜" /data/logs/dpcq_merge.log; then echo "合服成功完成 $(date +%F_%T)" >>/home/tcdplog.txt break else echo "合服进行中... ${elapsed_merge}s" >>/home/tcdplog.txt sleep 60 # 每60秒检查一次,减少IO压力 ((elapsed_merge += 60)) fi done if (( elapsed_merge >= timeout_merge )); then echo "错误:合服超时 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi echo "全部流程完成 $(date +%F_%T)" >>/home/tcdplog.txt ``` 完整修复后的脚本如下: ```bash #!/bin/bash # 创建目标目录确保存在 mkdir -p /data/database/merge/tc/ # 清空可能存在的旧merge目录 rm -rf /data/database/merge/tc/merge.1 rm -rf /data/database/merge/tc/merge.2 # 解压第一个备份并重命名 if ! tar zxvf /data/database/backup/dpcq_tc_S1/tc_S1_* -C /data/database/merge/tc/; then echo "S1备份解压失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 查找最新创建的目录(按时间戳) latest_dir1=$(find /data/database/merge/tc/ -maxdepth 1 -type d ! -path /data/database/merge/tc/ -printf '%T@ %p\n' | sort -n | tail -1 | awk '{print $2}') if [ -n "$latest_dir1" ]; then mv "$latest_dir1" /data/database/merge/tc/merge.1 echo "已将目录 $latest_dir1 重命名为 merge.1" >>/home/tcdplog.txt else echo "未找到S1解压后的目录 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 解压第二个备份并重命名 if ! tar zxvf /data/database/backup/dpcq_tc_S2/tc_S2_* -C /data/database/merge/tc/; then echo "S2备份解压失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 再次查找最新创建的目录(排除merge.1) latest_dir2=$(find /data/database/merge/tc/ -maxdepth 1 -type d ! -path /data/database/merge/tc/merge.1 ! -path /data/database/merge/tc/ -printf '%T@ %p\n' | sort -n | tail -1 | awk '{print $2}') if [ -n "$latest_dir2" ]; then mv "$latest_dir2" /data/database/merge/tc/merge.2 echo "已将目录 $latest_dir2 重命名为 merge.2" >>/home/tcdplog.txt else echo "未找到S2解压后的目录 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 等待合并目录生成(最多120秒) timeout=120 elapsed=0 while (( elapsed < timeout )) do if [[ -d "/data/database/merge/tc/merge.1" && -d "/data/database/merge/tc/merge.2" ]]; then echo "合并目录已生成 $(date +%F_%T)" >>/home/tcdplog.txt break else echo "等待合并目录生成... ${elapsed}s" >>/home/tcdplog.txt sleep 5 ((elapsed += 5)) fi done if (( elapsed >= timeout )); then echo "错误:合并目录生成超时 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 停止服务 cd /data/dpcq_tc_S1/server ./mgectl stop || { echo "S1服务停止失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 } cd /data/dpcq_tc_S2/server ./mgectl stop || { echo "S2服务停止失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 } # 开始合服 cd /data/dpcq_tc_S1/server ./mgectl live merge || { echo "合服启动失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 } # 监控合服进度(最长1小时) timeout_merge=3600 elapsed_merge=0 echo "开始监控合服进度 $(date +%F_%T)" >>/home/tcdplog.txt while (( elapsed_merge < timeout_merge )) do if grep -q "合服完毕,恭喜" /data/logs/dpcq_merge.log; then echo "合服成功完成 $(date +%F_%T)" >>/home/tcdplog.txt break else echo "合服进行中... ${elapsed_merge}s" >>/home/tcdplog.txt sleep 60 ((elapsed_merge += 60)) fi done if (( elapsed_merge >= timeout_merge )); then echo "错误:合服超时 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi echo "全部流程完成 $(date +%F_%T)" >>/home/tcdplog.txt ``` ### 说明: 1. 使用`find`命令按时间戳排序找到最新创建的目录(因为解压会立即创建目录),这样可以避免目录名不确定的问题。 2. 在查找第二个备份的目录时,排除了已经重命名的merge.1目录,确保找到的是第二个备份的目录。 3. 每一步都有错误检测和日志记录,便于排查问题。 ### 相关问题 §§相关问题§§ 1. 如何验证解压后的目录结构是否正确? 2. 如果备份文件名中包含空格或特殊字符,脚本应如何处理? 3. 合服过程中如何监控资源使用情况(CPU、内存、磁盘)? </think> ### 修复后的合服脚本 ```bash #!/bin/bash # 创建目标目录确保存在 mkdir -p /data/database/merge/tc/ # 清空可能存在的旧merge目录 rm -rf /data/database/merge/tc/merge.1 rm -rf /data/database/merge/tc/merge.2 # 解压第一个备份并重命名 if ! tar zxvf /data/database/backup/dpcq_tc_S1/tc_S1_* -C /data/database/merge/tc/; then echo "S1备份解压失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 查找并重命名解压后的目录(解决多个目录问题) found_dir1=$(find /data/database/merge/tc/ -maxdepth 1 -type d -name "20*" | head -1) if [ -n "$found_dir1" ]; then mv "$found_dir1" /data/database/merge/tc/merge.1 echo "已重命名S1目录: $found_dir1 → merge.1" >>/home/tcdplog.txt else echo "错误:未找到S1解压后的目录 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 解压第二个备份并重命名 if ! tar zxvf /data/database/backup/dpcq_tc_S2/tc_S2_* -C /data/database/merge/tc/; then echo "S2备份解压失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 查找并重命名解压后的目录(排除已处理的目录) found_dir2=$(find /data/database/merge/tc/ -maxdepth 1 -type d -name "20*" ! -path "/data/database/merge/tc/merge.1" | head -1) if [ -n "$found_dir2" ]; then mv "$found_dir2" /data/database/merge/tc/merge.2 echo "已重命名S2目录: $found_dir2merge.2" >>/home/tcdplog.txt else echo "错误:未找到S2解压后的目录 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 等待合并目录生成(最多120秒) timeout=120 elapsed=0 while (( elapsed < timeout )) do if [[ -d "/data/database/merge/tc/merge.1" && -d "/data/database/merge/tc/merge.2" ]]; then echo "合并目录已生成 $(date +%F_%T)" >>/home/tcdplog.txt break else echo "等待合并目录生成... ${elapsed}s" >>/home/tcdplog.txt sleep 5 ((elapsed += 5)) fi done if (( elapsed >= timeout )); then echo "错误:合并目录生成超时 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi # 停止服务 cd /data/dpcq_tc_S1/server ./mgectl stop || { echo "S1服务停止失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 } cd /data/dpcq_tc_S2/server ./mgectl stop || { echo "S2服务停止失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 } # 开始合服 cd /data/dpcq_tc_S1/server ./mgectl live merge || { echo "合服启动失败 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 } # 监控合服进度(最长1小时) timeout_merge=3600 elapsed_merge=0 echo "开始监控合服进度 $(date +%F_%T)" >>/home/tcdplog.txt while (( elapsed_merge < timeout_merge )) do if grep -q "合服完毕,恭喜" /data/logs/dpcq_merge.log; then echo "合服成功完成 $(date +%F_%T)" >>/home/tcdplog.txt break else echo "合服进行中... ${elapsed_merge}s" >>/home/tcdplog.txt sleep 60 ((elapsed_merge += 60)) fi done if (( elapsed_merge >= timeout_merge )); then echo "错误:合服超时 $(date +%F_%T)" >>/home/tcdplog.txt exit 1 fi echo "全部流程完成 $(date +%F_%T)" >>/home/tcdplog.txt ``` ### 主要修复点 1. **目录查找优化**: - 使用更通用的通配符 `20*` 替代 `201*`,兼容不同年份的备份目录[^1] - 添加目录存在性检查:`if [ -n "$found_dir1" ]` 确保目录存在才重命名 - 排除已处理目录:`! -path "/data/database/merge/tc/merge.1"` 避免重复处理 2. **错误处理增强**: ```bash if ! tar zxvf ...; then echo "错误信息" exit 1 fi ``` 解压失败时立即终止脚本并记录错误 3. **精确目录定位**: - 使用 `find` 命令精确查找目录:`-maxdepth 1 -type d` 确保只搜索当前层级 - `head -1` 确保只处理第一个匹配的目录,避免多个目录冲突 4. **日志可追溯性**: - 记录实际移动的目录路径:`已重命名S1目录: $found_dir1 → merge.1` - 所有日志添加时间戳 `$(date +%F_%T)` - 错误情况明确标识:"错误:" 前缀 ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值