Написание инструментов анализа памяти REDIS с помощью Golang RMA4GO

Redis

инструмент анализа памяти Redisrma4goВведение

Redis — это хорошо известная база данных в памяти, которая не будет здесь подробно описываться. иrma4go(анализатор памяти redis для golang) — это инструмент для анализа памяти Redis. Основная функция этого инструмента — анализировать память Redis во время выполнения, подсчитывать распределение ключей в Redis, использование различных типов данных и размер keys. , количество и распределение больших ключей, распределение статуса истечения срока действия ключа и другие инструменты, которые помогают обнаруживать проблемы с использованием Redis. Я надеюсь, что это может предоставить разработчикам приложений удобство для устранения практических проблем, возникающих в рабочей среде.

rma4goсценарии применения

Redis — очень популярная база данных в памяти, и многие предприятия используют ее. Однако, поскольку в отрасли не так много спецификаций для использования Redis или некоторые спецификации не соблюдаются должным образом, при использовании Redis возникает много проблем.Вот несколько примеров:

  1. Хранилище Redis заполнено, я не знаю распределение ключей, и я не знаю, откуда исходит приложение
  2. Redis заблокирован, я не знаю, что вызвало блокировку, какая ключевая операция, в каком приложении это вызвало
  3. Я хочу перенести данные Redis или настроить некоторые параметры, но я не знаю, сохранять ли данные в Redis, и я не знаю, какой бизнес используется и т. д.
  4. Срок действия ключа Redis неясен, и я не знаю, что можно удалить или изменить. На самом деле, некоторые из вышеперечисленных проблем просто перечислены мной, а не все существующие проблемы, я полагаю, что есть много других сценариев, которые также будут использовать такой инструмент анализа памяти Redis.rma4go

Особенности rma4go

измерение данных

Для ключевого анализа наш инструмент предоставит данные в следующих измерениях:

  • Количество распределение Размер клавиш
  • измерение распределения срока действия ключа
  • измерение распределения типа ключа
  • Измерение распределения размера данных, соответствующих ключу
  • Измерение распределения префикса ключа
  • Размер медленного ключа и большого ключа

Конечно, если в будущем будут найдены лучшие широты, они будут добавлены.В настоящее время эти широты являются основными.

дизайн типа данных

type RedisStat struct {
	All     KeyStat `json:"all"`
	String  KeyStat `json:"string"`
	Hash    KeyStat `json:"hash"`
	Set     KeyStat `json:"set"`
	List    KeyStat `json:"list"`
	ZSet    KeyStat `json:"zset"`
	Other   KeyStat `json:"other"`
	BigKeys KeyStat `json:"bigKeys"`
}

// distributions of keys of all prefixes
type Distribution struct {
	KeyPattern string `json:"pattern"`
	Metrics
}

// basic metrics of a group of key
type Metrics struct {
	KeyCount       int64 `json:"keyCount"`
	KeySize        int64 `json:"keySize"`
	DataSize       int64 `json:"dataSize"`
	KeyNeverExpire int64 `json:"neverExpire"`
	ExpireInHour   int64 `json:"expireInHour"`  // >= 0h < 1h
	ExpireInDay    int64 `json:"expireInDay"`   // >= 1h < 24h
	ExpireInWeek   int64 `json:"expireInWeek"`  // >= 1d < 7d
	ExpireOutWeek  int64 `json:"expireOutWeek"` // >= 7d
}

детали реализации

ключевая метаинформация

type KeyMeta struct {
	Key      string
	KeySize  int64
	DataSize int64
	Ttl      int64
	Type     string
}

Как мы все знаем, все данные в Redis в основном основаны на ключе, и они также работают в соответствии с ключом. Затем, чтобы проанализировать ключ в Redis, мы должны записать информацию об этом ключе. Информация, которую мы можем записать, Так же, как и в приведенной выше структуре, сам ключ, размер ключа, размер данных, срок действия и тип ключа. Эта информация является базовой информацией для нашего ключевого анализа, которую можно получить с помощью некоторых простых команд Redis.

Пройти все ключи Redis

Чтобы выполнить полный анализ ключей в Redis, нам нужен способ доступа к исходной информации обо всех ключах.scanТаким образом, все ключи могут быть легко пройдены, и можно получить доступ к метаинформации соответствующего ключа. Таким образом, для Redis нагрузка, вызванная онлайн-анализом ключей, не будет очень большой.Конечно, анализ ключей не может выполняться в пиковый период QPS, и его необходимо анализировать, когда позволяют ресурсы Redis.

Кроме того, благодаря механизму очистки памяти самого redis, при анализе ключа можно очистить 25% просроченной занятости, поэтому данный инструмент анализа также имеет функцию очистки части памяти. в redis и существует в памяти внутри ключевых слов.

Анализировать и обобщать записанную информацию

При использовании метода обхода всех ключей и метаданных все остальное заключается в агрегировании и обобщении этих данных, что в основном представляет собой алгоритмическую работу. Самое сложное это часть агрегации ключей.Много компромиссов.Поскольку автор не специализируется на алгоритмах,а подходящей библиотеки я не нашел,могу только сам придумать способ. Основная идея такова:

алгоритм сжатия
  1. Метаинформация для каждого нового ключа добавляется к объекту анализа старого ключа
  2. Укоротите ключ от задней части к передней, удалите хвост и посмотрите, включена ли статистическая информация о ключе.Если она включена, информация о ключе накапливается.Если она не включена, создается новая запись .
  3. Когда количество записей добавляется к определенному числу, количество объектов сжимается один раз
    • Алгоритм сжатия также сжимает от конца строки к началу строки.
    • Когда сжатие не может увеличить количество ключей этого паттерна, используйте исходный ключ (ключ до сжатия)
    • При сжатии можно увеличить количество ключей этого паттерна, объединить ключи и установить паттерн в сжатый паттерн.
    • Когда количество записей превышает указанное число, цикл будет повторяться до тех пор, пока сжатие не станет меньше указанного числа.
    • Если есть требование минимальной длины ключа (даже при повторном сжатии одна-две цифры должны быть зарезервированы), то есть некоторые параметры, сжатые до минимальной длины строки, которые можно настроить и задать, и определенные компромиссы сделаны.
  4. пока не завершится сканирование
код показывает, как показано ниже
const (
	defaultSize = 128
	compactNum  = 30
	maxLeftNum =  150
	minKeyLenLower = 2
	minKeyLen   = 5
)


func (stat *KeyStat) compact() {
	distMap := stat.Distribution
	tmpMap := make(map[string][]string, defaultSize)
	shrinkTo := compactNum
	for k := range distMap {
		compactedKey := k
		if orgks, ok := tmpMap[compactedKey]; ok {
			orgks = append(orgks, k)
			tmpMap[compactedKey] = orgks
		} else {
			ks := make([]string, 0, defaultSize)
			ks = append(ks, k)
			tmpMap[compactedKey] = ks
		}
	}
	shrinkTo--
	for (len(tmpMap) > compactNum && shrinkTo >= minKeyLen) || (len(tmpMap) > maxLeftNum && shrinkTo >= minKeyLenLower) {
		tnMap := make(map[string][]string, defaultSize)
		for k := range tmpMap {
			// shrink
			if len(k) > shrinkTo {
				compactedKey := k[0:shrinkTo]
				if oik, ok := tnMap[compactedKey]; ok {
					oik = append(oik, tmpMap[k]...)
					tnMap[compactedKey] = oik

				} else {
					ks := make([]string, 0, defaultSize)
					ks = append(ks, tmpMap[k]...)
					tnMap[compactedKey] = ks
				}
			} else {
				tnMap[k] = tmpMap[k]
			}
		}

		// 如果此次shrink 没有使得这个集合的元素数量增加, 就使用原来的key
		for k := range tmpMap {
			if len(k) > shrinkTo {
				ck := k[0:shrinkTo]
				if len(tnMap[ck]) == len(tmpMap[k]) && len(tnMap[ck]) > 1 {
					x := make([]string, 0, defaultSize)
					tnMap[k] = append(x, tnMap[ck]...)
					delete(tnMap, ck)
				}
			}
		}
		tmpMap = tnMap
		shrinkTo --
	}

	dists := make(map[string]Distribution, defaultSize)
	for k, v := range tmpMap {
		if len(v) > 1 {
			var nd Distribution
			for _, dk := range v {
				d := distMap[dk]
				nd.KeyPattern = k + "*"
				nd.KeyCount += d.KeyCount
				nd.KeySize += d.KeySize
				nd.DataSize += d.DataSize
				nd.ExpireInHour += d.ExpireInHour
				nd.ExpireInWeek += d.ExpireInWeek
				nd.ExpireInDay += d.ExpireInDay
				nd.ExpireOutWeek += d.ExpireOutWeek
				nd.KeyNeverExpire += d.KeyNeverExpire
			}
			dists[k] = nd
		} else {
			for _, dk := range v {
				nd := distMap[dk]
				nd.KeyPattern = dk + "*"
				dists[dk] = nd
			}
		}
	}
	stat.Distribution = dists
}


проект github для онлайн-анализа ключей

rma4go

GitHub.com/winkeyeg/nausey4…

Это проект, который я написал, он очень прост в использовании, бинарный пакет готов, вы можете скачать его по следующей ссылкеLinux MAC

метод сборки

  1. Перед сборкой убедитесь, что golang sdk установлен и его версия >=1.11.0.
  2. Пожалуйста, убедитесь, что у вас есть среда для преодоления стены, потому что она требует загрузки некоторых зависимостей, которые могут исходить из-за стены Способ опрокидывания стены следующий
// linux/osx
export http_proxy=somehost:port
export https_proxy=somehost:port
// windows
set http_proxy=somehost:port
set https_proxy=somehost:port

  1. Построить
git clone git@github.com:winjeg/rma4go.git
cd rma4go
go build .

инструкции

Использование заключается в следующем:rma4go -h

rma4go usage:
rma4go -r some_host -p 6379 -a password -d 0
======================================================
  -H string
        address of a redis (default "localhost")
  -a string
        password/auth of the redis
  -d int
        db of the redis to analyze
  -h    help content
  -p int
        port of the redis (default 6379)
  -r string
        address of a redis (default "localhost")

Пример вывода

all keys statistics

| PATTERN | KEY NUM | KEY SIZE | DATA SIZE | EXPIRE IN HOUR | EXPIRE IN DAY | EXPIRE IN WEEK | EXPIRE OUT WEEK | NEVER EXPIRE |
|---------|---------|----------|-----------|----------------|---------------|----------------|-----------------|--------------|
| total   |       0 |        0 |         0 |              0 |             0 |              0 |               0 |            0 |
string keys statistics

| PATTERN | KEY NUM | KEY SIZE | DATA SIZE | EXPIRE IN HOUR | EXPIRE IN DAY | EXPIRE IN WEEK | EXPIRE OUT WEEK | NEVER EXPIRE |
|---------|---------|----------|-----------|----------------|---------------|----------------|-----------------|--------------|
| total   |       0 |        0 |         0 |              0 |             0 |              0 |               0 |            0 |
list keys statistics

| PATTERN | KEY NUM | KEY SIZE | DATA SIZE | EXPIRE IN HOUR | EXPIRE IN DAY | EXPIRE IN WEEK | EXPIRE OUT WEEK | NEVER EXPIRE |
|---------|---------|----------|-----------|----------------|---------------|----------------|-----------------|--------------|
| total   |       0 |        0 |         0 |              0 |             0 |              0 |               0 |            0 |
hash keys statistics

| PATTERN | KEY NUM | KEY SIZE | DATA SIZE | EXPIRE IN HOUR | EXPIRE IN DAY | EXPIRE IN WEEK | EXPIRE OUT WEEK | NEVER EXPIRE |
|---------|---------|----------|-----------|----------------|---------------|----------------|-----------------|--------------|
| total   |       0 |        0 |         0 |              0 |             0 |              0 |               0 |            0 |
set keys statistics

| PATTERN | KEY NUM | KEY SIZE | DATA SIZE | EXPIRE IN HOUR | EXPIRE IN DAY | EXPIRE IN WEEK | EXPIRE OUT WEEK | NEVER EXPIRE |
|---------|---------|----------|-----------|----------------|---------------|----------------|-----------------|--------------|
| total   |       0 |        0 |         0 |              0 |             0 |              0 |               0 |            0 |
zset keys statistics

| PATTERN | KEY NUM | KEY SIZE | DATA SIZE | EXPIRE IN HOUR | EXPIRE IN DAY | EXPIRE IN WEEK | EXPIRE OUT WEEK | NEVER EXPIRE |
|---------|---------|----------|-----------|----------------|---------------|----------------|-----------------|--------------|
| total   |       0 |        0 |         0 |              0 |             0 |              0 |               0 |            0 |
other keys statistics

| PATTERN | KEY NUM | KEY SIZE | DATA SIZE | EXPIRE IN HOUR | EXPIRE IN DAY | EXPIRE IN WEEK | EXPIRE OUT WEEK | NEVER EXPIRE |
|---------|---------|----------|-----------|----------------|---------------|----------------|-----------------|--------------|
| total   |       0 |        0 |         0 |              0 |             0 |              0 |               0 |            0 |
big keys statistics

| PATTERN | KEY NUM | KEY SIZE | DATA SIZE | EXPIRE IN HOUR | EXPIRE IN DAY | EXPIRE IN WEEK | EXPIRE OUT WEEK | NEVER EXPIRE |
|---------|---------|----------|-----------|----------------|---------------|----------------|-----------------|--------------|
| total   |       0 |        0 |         0 |              0 |             0 |              0 |               0 |            0 |

rendered by markdown total count 4004

all keys statistics

PATTERN KEY NUM KEY SIZE DATA SIZE EXPIRE IN HOUR EXPIRE IN DAY EXPIRE IN WEEK EXPIRE OUT WEEK NEVER EXPIRE
TOP_TEN_NEW_XXXXXXXX* 1 20 1529 0 0 0 0 1
XXXXXXXXXXXXXX_STATISTICS_MIGRATION_LIST* 1 40 7692832 0 0 0 0 1
time-root:* 23 272 299 0 0 0 0 23
DS_AXXXXXXXX_CORRECT* 2 45 46 0 0 0 0 2
time-2* 761 7528 9893 0 0 0 0 761
time-level:* 537 8461 6981 0 0 0 0 537
time-9* 102 901 1326 0 0 0 0 102
time-7* 153 1372 1989 0 0 0 0 153
DS_MAGIC_SUCC_2017-06-22* 1 24 415 0 0 0 0 1
tersssss* 5 124 0 0 0 0 0 5
appoint_abcdefg_msgid* 1 21 0 0 0 0 0 1
BUSSINESSXXXXXXX_STATISTICS_NEED_CALC_RECENT* 1 44 1 0 0 0 0 1
switch_abcd_abcde* 3 69 3 0 0 0 0 3
abcdeferCounter_201* 3 78 0 0 0 0 0 3
diy1234567flag* 1 14 1 0 0 0 0 1
DS_PRXXBCD_LIST* 1 15 17208 0 0 0 0 1
time-4* 133 1194 1729 0 0 0 0 133
datastatistics_switch_version0* 1 30 1 0 0 0 0 1
register_count_2_201* 592 15984 640 0 0 0 0 592
canVisitNewabcdef1234PageLevels* 1 31 0 0 0 0 0 1
YOUR_WEEK_VITALITY_INFO* 1 23 75782 0 0 0 0 1
time-8* 101 894 1313 0 0 0 0 101
EXPERTS_APPOINT_INFO_MAP* 1 24 0 0 0 0 0 1
time-3* 130 1215 1690 0 0 0 0 130
time-1* 943 9456 12259 0 0 0 0 943
time-64* 87 781 1131 0 0 0 0 87
time-5* 168 1516 2184 0 0 0 0 168
total 4004 53422 7832490 0 0 0 0 4004

string keys statistics

PATTERN KEY NUM KEY SIZE DATA SIZE EXPIRE IN HOUR EXPIRE IN DAY EXPIRE IN WEEK EXPIRE OUT WEEK NEVER EXPIRE
BUSSINESSXXXXXXX_STATISTICS_NEED_CALC_RECENT* 1 44 1 0 0 0 0 1
time-5* 130 1174 1690 0 0 0 0 130
datastatistics_switch_version0* 1 30 1 0 0 0 0 1
time-7* 39 348 507 0 0 0 0 39
time-level:* 567 8939 7371 0 0 0 0 567
diy1234567flag* 1 14 1 0 0 0 0 1
switch_abcd_abcde* 3 69 3 0 0 0 0 3
time-2* 598 5918 7774 0 0 0 0 598
time-6* 125 1118 1625 0 0 0 0 125
time-4* 136 1225 1768 0 0 0 0 136
time-8* 72 636 936 0 0 0 0 72
time-1* 1176 11814 15288 0 0 0 0 1176
time-9* 100 880 1300 0 0 0 0 100
time-root:* 23 272 299 0 0 0 0 23
register_count_2_201* 592 15984 640 0 0 0 0 592
DS_AXXXXXXXX_CORRECT* 1 20 20 0 0 0 0 1
TOP_TEN_NEW_tersssss* 1 20 1529 0 0 0 0 1
time-3* 202 1925 2626 0 0 0 0 202
total 3989 53042 46253 0 0 0 0 3989

list keys statistics

PATTERN KEY NUM KEY SIZE DATA SIZE EXPIRE IN HOUR EXPIRE IN DAY EXPIRE IN WEEK EXPIRE OUT WEEK NEVER EXPIRE
XXXXXXXXXXXXXX_STATISTICS_MIGRATION_LIST* 1 40 7692832 0 0 0 0 1
DS_MAGIC_SUCC_2017-06-22* 1 24 415 0 0 0 0 1
DS_PRXXBCD_LIST* 1 15 17208 0 0 0 0 1
total 3 79 7710455 0 0 0 0 3

hash keys statistics

PATTERN KEY NUM KEY SIZE DATA SIZE EXPIRE IN HOUR EXPIRE IN DAY EXPIRE IN WEEK EXPIRE OUT WEEK NEVER EXPIRE
tersssss_action_prepage_new* 1 27 0 0 0 0 0 1
YOUR_WEEK_VITALITY_INFO* 1 23 75782 0 0 0 0 1
EXPERTS_APPOINT_INFO_MAP* 1 24 0 0 0 0 0 1
abcdeferCounter_2017-06-11* 1 26 0 0 0 0 0 1
tersssssHardTaskCounter* 1 23 0 0 0 0 0 1
abcdeferCounter_2018-04-27* 1 26 0 0 0 0 0 1
abcdeferCounter_2017-09-01* 1 26 0 0 0 0 0 1
tersssssEasyTaskCounter* 1 23 0 0 0 0 0 1
total 8 198 75782 0 0 0 0 8

set keys statistics

PATTERN KEY NUM KEY SIZE DATA SIZE EXPIRE IN HOUR EXPIRE IN DAY EXPIRE IN WEEK EXPIRE OUT WEEK NEVER EXPIRE
tersssss_bind_phone_phone* 1 25 0 0 0 0 0 1
appoint_abcdefg_msgid* 1 21 0 0 0 0 0 1
canVisitNewabcdef1234PageLevels* 1 31 0 0 0 0 0 1
tersssss_bind_phone_userid* 1 26 0 0 0 0 0 1
total 4 103 0 0 0 0 0 4

zset keys statistics

PATTERN KEY NUM KEY SIZE DATA SIZE EXPIRE IN HOUR EXPIRE IN DAY EXPIRE IN WEEK EXPIRE OUT WEEK NEVER EXPIRE
total 0 0 0 0 0 0 0 0

other keys statistics

PATTERN KEY NUM KEY SIZE DATA SIZE EXPIRE IN HOUR EXPIRE IN DAY EXPIRE IN WEEK EXPIRE OUT WEEK NEVER EXPIRE
total 0 0 0 0 0 0 0 0

big keys statistics

PATTERN KEY NUM KEY SIZE DATA SIZE EXPIRE IN HOUR EXPIRE IN DAY EXPIRE IN WEEK EXPIRE OUT WEEK NEVER EXPIRE
XXXXXXXXXXXXXX_STATISTICS_MIGRATION_LIST* 1 40 7692832 0 0 0 0 1
total 1 40 7692832 0 0 0 0 1

использовать как зависимость

Способ приобретения следующий:

go get github.com/winjeg/rma4go

Способ применения следующий:

func testFunc() {
	h := "localhost"
	a := ""
	p := 6379
	cli := client.BuildRedisClient(client.ConnInfo{
		Host: h,
		Auth: a,
		Port: p,
	}, cmder.GetDb())

	stat := analyzer.ScanAllKeys(cli)
    // print in command line
	stat.Print()
	// the object is ready to use
}

обслуживание github (основная позиция)

  1. Другие разработчики могут присоединиться
  2. Добро пожаловать, чтобы оставить отзыв
  3. Любые содержательные предложения приветствуются
  4. Кроме того, звездочка приветствуется, форк не рекомендуется, рекомендуется подавать PR напрямую ;)