Python3 操作 Redis(Cluster)及實踐(key 前綴)詞頻統計

目錄

一、簡單介紹

二、安裝 Redis 模塊

三、Redis 模塊基本操作

1、Redis 模塊使用分類

2、Redis 使用參考文檔

3、Python 操作 Redis 模式

4、數據操作

4.1 redisconn.py

4.2 redis_key_analysis.py

4.3 以下是一些測試結果


一、簡單介紹

        Redis 是一個 key-value 存儲系統。它支持存儲的 value 類型相對更多,包括 string(字符串)、list(鏈表、set(集合)、zset(sorted set --有序集合)和 hash(哈希類型)。

        這些數據類型都支持 push/pop,add/remove 及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在次基礎上,Redis 支持各種不同方式的排序,與 memcached 一樣,爲了保證效率,數據都是緩衝在內存中。區別是 Redis 會週期性的把更新的數據寫入磁盤或把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave (主從)同步。

        注意:默認 Redis 有 16 個數據庫,即 db0~db15, 一般存取數據如果不指定庫的話,默認都是存在 db0 中。

二、安裝 Redis 模塊

Python 想操作 Redis,需要安裝第三方模塊。

  • Redis:conda install -n higo_3.7 redis-py
  • Redis Cluster:pip install redis-py-cluster

三、Redis 模塊基本操作

1、Redis 模塊使用分類

  • 連接方式
  • 連接池
  • 操作
  • string 操作
  • hash 操作
  • list 操作
  • Set 操作
  • Sort Set 操作
  • 管道
  • 發佈訂閱

2、Redis 使用參考文檔

3、Python 操作 Redis 模式

  • str 還是 bytes?通過簡單測試就可以看到 Redis 取出的結果默認是字節,我們可以設定 decode_responses=True 改成字符串。
  • 默認 Redis 入庫編碼是 utf-8,如果要修改的話,需要指明 charset 和 decode_responsers=True
  • Redis 使用 ConnectionPool 來管理對一個 Redis Server 的所有連接,避免每次建立,釋放連接的開銷,默認,每個Redis實例都會維護一個自己的連接池。

4、數據操作

  • redisconn.py:Python3 連接 Redis/Redis Cluster,自定義可執行命令(即 Redis 數據操作命令的白名單),具體例子參考 redisconn.py
  • redis_key_analysis.py:Python3 實現指定 Redis(key 前綴)詞頻統計

4.1 redisconn.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

'''================================================================
@Project -> File   :Redis -> redisconn.py
@IDE    :PyCharm
@Author :Mr. Wufei
@Date   :2019/11/6 15:16
@Desc   :Python3 連接 Redis/Redis Cluster,自定義可執行命令(Example)
=================================================================='''

import sys
import redis
from rediscluster import RedisCluster

# Redis 普通連接
class RedisConn(object):

    def __init__(self, host='127.0.0.1', port=6379, password='123456',  decode_responses=True):
        try:
            # 創建 Redis 連接對象
            self.__conn = redis.Redis(host=host, port=port, password=password, decode_responses=decode_responses)
        except Exception as e:
            print('redis connect error!', e)
            sys.exit(1)

    def set_key_value(self, name, value, ex = None, px = None):
        """
        Redis 字符串(String)命令,Redis SET 命令用於設置給定 key 的值。如果 key 已經存儲其他值, SET 就覆寫舊值,且無視類型
        :param name: KEY_NAME
        :param value: VALUE
        :param ex: 代表秒 seconds
        :param px: 代表毫秒 ms
        :return: True/False
        """
        return self.__conn.set(name, value, ex = ex, px = px)

    def get_key(self, name):
        """
        Redis 字符串(String)命令,Redis Get 命令用於獲取指定 key 的值
        :param name: KEY_NAME
        :return: 返回 key 的值,如果 key 不存在,返回 nil,如果 key 儲存的值不是字符串類型,返回一個錯誤
        """
        return self.__conn.get(name)

    def get_all_key(self):
        """
        Redis Keys * 命令,用於查找所有的 key
        :return: 返回當前 Redis 所有 key 的列表 (Array)
        """
        return self.__conn.keys('*')

# Redis 連接池
class RedisConnPool(object):

    def __init__(self, host='127.0.0.1', port=6379, password='123456', max_connections=1024, decode_responses=True):
        try:
            # 創建 Redis 連接池
            self.__pool = redis.ConnectionPool(host=host, port=port, password=password, max_connections=max_connections, decode_responses=decode_responses)
            # 創建 Redis 連接對象
            self.__conn = redis.Redis(connection_pool=self.__pool)
        except Exception as e:
            print('redis pool connect error!', e)
            sys.exit(1)

    def set_key_value(self, name, value, ex = None, px = None):
        return self.__conn.set(name, value, ex = ex, px = px)

    def get_key(self, name):
        return self.__conn.get(name)

    def get_all_key(self):
        return self.__conn.keys('*')

# Redis Cluster 普通連接
class RedisClusterConn(object):

    def __init__(self, startup_nodes=[{'host': '127.0.0.1', 'port': 7001}], decode_responses=True, max_connections=1024):
        try:
            # 創建 Redis Cluster 連接對象
            self.__rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=decode_responses, max_connections=max_connections)
        except Exception as e:
            print('redis cluster connect error!', e)
            sys.exit(1)

    def set_key_value(self, name, value, ex = None, px = None):
        return self.__rc.set(name, value, ex = ex, px = px)

    def get_key(self, name):
        return self.__rc.get(name)

    def get_all_key(self):
        """
        Redis Keys * 命令,用於查找所有的 key
        :return: 返回當前 Redis Cluster 所有 key 的列表 (Array)
        """
        return self.__rc.keys('*')

"""
if __name__ == '__main__':
    redis_dict = {'host': 'xx.xx.1.15', 'port': 6379, 'password': 'wf37@show'}
    startup_nodes = [
        {'host': 'xx.xx.2.35', 'port': 7001},
        {'host': 'xx.xx.2.35', 'port': 7002},
        {'host': 'xx.xx.2.35', 'port': 7003},
        {'host': 'xx.xx.2.35', 'port': 7004},
        {'host': 'xx.xx.2.35', 'port': 7004},
        {'host': 'xx.xx.2.35', 'port': 7006}
    ]
    # redis_conn = RedisConn(host=redis_dict['host'], port=redis_dict['port'], password=redis_dict['password'])
    # redis_conn = RedisConnPool(host=redis_dict['host'], port=redis_dict['port'], password=redis_dict['password'])
    redis_conn = RedisClusterConn(startup_nodes)
    redis_conn.set_key_value('wf', 'show', ex=5)
    val = redis_conn.get_key('wf')
    print(val)
    all_key_list = redis_conn.get_all_key()
    print(all_key_list)
"""

4.2 redis_key_analysis.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

'''=================================================
@Project -> File   :Redis -> redis_key_analysis.py
@IDE    :PyCharm
@Author :Mr. Wufei
@Date   :2019/11/6 11:40
@Desc   :Python3 實現指定 Redis(key 前綴)詞頻統計
=================================================='''

import re
import redisconn
from string import digits

class RedisKeyAnalysis(object):

    def __init__(self, redis_conn, out_prefix_file='/tmp/redis_key_prefix.log'):
        """
        :param redis_conn: Redis 連接信息,根據連接方式判斷是 Redis 還是 Redis Cluster
        :param out_prefix_file: Redis key 前綴詞頻統計輸出文件路徑
        """
        self.__out_prefix_file = out_prefix_file
        self.__redis_dict = ''
        self.__startup_nodes = ''
        if type(redis_conn) == type(dict()):
            self.__redis_dict = redis_conn
        else:
            self.__startup_nodes = redis_conn

    def _redis_all_key(self):
        """
        調用 Redis Keys * 命令,用於查找所有的 key(bytes 類型的字符串)
        :return: 返回當前 Redis 所有 key (bytes 類型)的列表 (Array)
        """
        rc = redisconn.RedisConn(host=self.__redis_dict['host'], port=self.__redis_dict['port'], password=self.__redis_dict['password'])
        all_key_list = rc.get_all_key()
        return all_key_list

    def _cluster_all_key(self):
        """
        Redis Keys * 命令,用於查找所有的 key
        :return: 返回當前 Redis Cluster 所有 key 的列表 (Array)
        """
        rc = redisconn.RedisClusterConn(self.__startup_nodes)
        all_key_list = rc.get_all_key()
        return all_key_list

    def _write_line(self, key_num_list):
        """
        將處理後的 Redis key 前綴及詞頻統計的列表寫入新文件
        :param key_num_list: Redis key 前綴及詞頻統計的列表,格式:[(('xxxx_unread_notice_count', 699212), ('xxxx_promo_flag', 106415))]
        :return:
        """
        with open(self.__out_prefix_file, 'w') as logfile_writer:
            for line in key_num_list:
                line = str(line)
                logfile_writer.write(line + '\n')

    def redis_process(self):
        """
        解析 Redis key,按前綴統計其頻率
        :return: key 按前綴分類數列表長度
        """
        # 定義一個空字典,用於詞頻統計
        key_dict = dict()
        # 獲取當前 Redis 所有 key (bytes 類型)的列表 (Array)
        all_key_list = self._redis_all_key()
        for line in all_key_list:
            # 將 bytes 轉換爲 str
            line = line.decode('utf-8')
            # string.digits:數字0~9,清除數字,這裏主要清除 key 後綴中的各種 ID
            remove_digits = str.maketrans('', '', digits)
            line = line.translate(remove_digits)
            # 刪除末尾的':',當然具體怎麼篩選還得根據當前具體情況具體分析
            line = line.rstrip(':')
            # 刪除末尾的'_'
            line = line.rstrip('_')
            # 判斷是否爲特殊 key,特殊處理,前綴可根據具體情況修改
            line_auth = re.search(r'Auth', line)
            if line_auth:
                line = 'Auth:ACCESS_TOKEN'
            # 通過字典進行詞頻統計
            if line in key_dict:
                key_dict[line] += 1
            else:
                key_dict.setdefault(line, 1)
        # 通過 value 值給字典排序,返回一個列表
        key_num_list = sorted(key_dict.items(), key=lambda x: x[1], reverse=True)
        # 將處理後的 Redis key 前綴及詞頻統計的列表寫入新文件
        self._write_line(key_num_list)
        return len(key_num_list)

"""
if __name__ == '__main__':
    redis_dict = {'host': 'xx.xx.1.15', 'port': 6379, 'password': 'wf37@show'}
    out_prefix_file = '/home/work/wufei/redis_udp/redis_key_prefix.log'
    rka = RedisKeyAnalysis(redis_dict, out_prefix_file)
    key_num = rka.redis_process()
    print(key_num)
"""

4.3 以下是一些測試結果(redis_key_prefix.log)

('xxxx_unread_notice_count', 699212)
('xxxx_promo_flag', 106415)
('report_event_notify_account_goods', 33124)
('account_merge_status_account_merge_trigger', 12032)
('hgim_unread_msg_list', 1219)
('bim_m', 779)
('greate_user_sim_.', 724)
('bim_new', 559)
('account_vip_toast', 432)
('xxxx_promotion_flag', 352)
('xxxx_accmerchant', 311)
('wx_un_send_last_msg', 182)
('wx_contact_session_list', 129)
('main_new_in_shop_image', 84)
('wx_un_send_msg_num', 70)
('account_merge_status_account_merge_rollback', 62)
('weekly_top_sales_distinct_detail', 52)
('weekly_top_gmv_detail', 48)
('weekly_top_sales_detail', 36)
('media_detail', 16)
('MP_FORM_ID_LIST', 14)
('hiparty_unread_notify_count', 14)
('delay_queue_unique_key', 9)
('xxxx_event_thematic_goods_infos', 6)
('xxxx_user_mobile', 5)
('ImGroupLatestedId', 5)
('goods_detail', 4)
('xxxx_event_thematic_whole_category_data', 4)
('xxxx_verifycode', 4)
('xxxx_shop_life_favorite_list', 4)
('wx_prepayid_use_timeswxe', 2)
('wx_prepayid_use_timeswxaf', 2)
('wx_prepayid_use_timeswxa', 2)
('xxxx_homefeed_abtest_hit', 2)
('xxxx_xlive_info', 2)
('wx_prepayid_use_timeswxdbd', 1)
('wx_prepayid_use_timeswxcdb', 1)
('wx_prepayid_use_timeswxecc', 1)
('wx_prepayid_use_timeswxc', 1)
('wx_prepayid_use_timeswxeeea', 1)

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章