在分析redis集群中大Key的时候,通常都采用分析rdb文件的方式;但是这种方式需要在每一台redis服务器上部署分析程序及分析脚本,而像salt之类的工具运维没有开放给我们使用,一台一台部署不好管理。正好我们的总redis规模不大,大概在200个集群左右,考虑到cluster集群,需要分析的redis实例数在300左右,所以就想着能不能通过scan的方式来进行。
通过scan命令扫描从库,将集群中key深度大于指定值的key扫描出来,将这些 key 放在一个slice中,取 top N,这就需要对 slice中的 key按照深度进行排序。300个实例说多不多,说少不少,排序算法的性能还是很重要的。快速排序针对小数据量排序性能很好,正好mysql在使用sort buffer进行排序时采用的是快速排序,这里就用go实现来复习一下快速排序算法。
func sortBigKeySlice(bigKeySlice []*bigKeyInstance) { if len(bigKeySlice) < 2 { return } sortBigKey(bigKeySlice, 0, len(bigKeySlice)) } //使用递归实现 func sortBigKey(bigKeySlice []*bigKeyInstance, low, high int) { if low >= high { return } p := partition(bigKeySlice, low, high) sortBigKey(bigKeySlice, low, p) sortBigKey(bigKeySlice, p + 1, high) }
//普通快速排序,对于普通快速排序,将第一个元素作为基准,小于该元素的放在左边,大于等于该元素的放在右边 func partition(bigKeySlice []*bigKeyInstance, low, high int) int{ //直接将第一个元素作为分隔值 pivotIns := bigKeySlice[low] //当前第一个元素点位作为标记点 pivotPos := low //去除第一个分割值,遍历元素,如果元素比分割值小,将标记点右移一位,交换元素的值,大于等于则继续比较下一个元素 for i := low + 1; i < high; i++ { if bigKeySlice[i].size < pivotIns.size { pivotPos += 1 bigKeySlice[pivotPos], bigKeySlice[i] = bigKeySlice[i], bigKeySlice[pivotPos] } else { continue } } //不要忘记最后的互换,将分隔值与标记点元素互换 bigKeySlice[low], bigKeySlice[pivotPos] = bigKeySlice[pivotPos], bigKeySlice[low] return pivotPos }
普通快速排序默认左边的第一个元素作为基准数,对于渐进有序的数组来说,这就导致小于基准的数会相当少,而大于等于基准的数相当多,造成分区不平衡的问题,普通排序就会退化,严重的将退化成O(n^2)。所以对其改进:不再默认选择第一个数,而是随机选一个数作为基准,这样的快排称为随机普通快排。
//随机普通快速排序,不使用第一个元素作为基准,而是使用一个随机元素作为基准 func partition(bigKeySlice []*bigKeyInstance, low, high int) int { //取slice中的一个随机元素作为分割点,而不是第一个元素开始分割 rand_low := low + rand.Intn(high - low) bigKeySlice[low], bigKeySlice[rand_low] = bigKeySlice[rand_low], bigKeySlice[low] pivotPos := low for i := low + 1; i < high; i++ { if bigKeySlice[i].size < bigKeySlice[low].size { pivotPos += 1 bigKeySlice[pivotPos], bigKeySlice[i] = bigKeySlice[i], bigKeySlice[pivotPos] } else { continue } } bigKeySlice[low], bigKeySlice[pivotPos] = bigKeySlice[pivotPos], bigKeySlice[low] return pivotPos }
对于含有大量重复元素的数组,则对于与基准数相同的数,要么分到了左边,要么分到了右边,同样会造成分治不平衡的问题,造成性能退化。这时,采用双路排序或三路排序进行改进。
双路排序 & 三路排序待续....