目录
一、简单介绍
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)