一. redis数据转存到mysql数据库的意义
redis的最佳使用场景是缓存,用来持久化数据存储也是可以的,但既然有更适合于用作持久化数据存储的数据库,我们为什么不用呢,做到物尽其用,发挥其最大优势,所以redis中有的数据,也有必要在mysql中进行存储。
二. redis数据如何转存到mysql数据库
思路:我们可以使用Python语言来编写一个程序,连接redis数据库,连接mysql数据库,再从redis里面读出数据,将数据插入到mysql数据库中,最后新建一个shell文件,在文件里面调用写好的Python程序即可。详细操作步骤如下:
1. 导包
import json # json是一种轻量级的数据交换格式,后续程序中会用到json.loads()将已编码的json字符串解码为Python对象,json.dumps()是将Python对象编码成json字符串
import redis
import pymysql
import json # json是一种轻量级的数据交换格式,后续程序中会用到json.loads()将已编码的json字符串解码为Python对象,json.dumps()是将Python对象编码成json字符串
import redis
import pymysql
2. 定义主函数
def main():
# 指定redis数据库
rediscli = redis.StrictRedis(host='IP‘, port=PORT, password='PASSWORD', db=0)
# 指定mysql数据库
mysqlcli = pymysql.connect(host="localhost", port=3306, user="USER", passwd="PASSWORD", db="task", charset="utf8")
# 指定unique_id为唯一索引
point_unique = 'alter table tb_stop_record add unique key(`unique_id`)'
mysqlcli.cursor().execute(point_unique)
# 设置lrange取值的起始值和终止值,步长,页数初始值设为0
page_start = 0
step = 2
page_end = page_start + step
page_num = 0
# 统计出页数
while True:
data = rediscli.lrange('acct_stop_record', start=page_start, end=page_end)
# 判断获取到的数据是否为空
if data:
page_start = page_end + 1
page_end = page_start + step
page_num += 1
# 如果获取到的数据为空,则跳出循环
else:
break
# 重新设置lrange取值的起始值和终止值,步长,数据总数初始值设为0,实际插入的数据条数初始值为0
page_start = 0
step = 2
page_end = page_start + step
total = 0
count = 0
for page in range(0, page_num):
data_list = rediscli.lrange('acct_stop_record', start=page_start, end=page_end)
page_start = page_end + 1
page_end = page_start + step
params = []
for data in data_list:
total += 1
item = json.loads(data)
if item.get('session_id'):
params.append(([item.get('session_id'), item.get('unique_id'), item.get('member_name')]))
else:
continue
try:
# 使用cursor()方法获取游标
cursor = mysqlcli.cursor()
sql = "insert ignore into tb_stop_record(session_id,unique_id,member_name) " \
"VALUES(%s,%s,%s)"
# 添加的数据量
add_num = cursor.executemany(sql, params)
count = count + add_num
mysqlcli.commit()
# 关闭游标
cursor.close()
except pymysql.Error as e:
mysqlcli.rollback()
print "insert error", e
try:
# 根据实际插入的条数和每页的条数,计算出页数,用math.ceil()向上取整
if count % (step + 1) == 0:
page_num = count / (step + 1)
else:
page_num = math.ceil(count / (step + 1))
# 计算最后一页实际插入的条数
if count % int(page_num) == 0:
last_page_data_num = step + 1
else:
last_page_data_num = count % int(step + 1)
print 'There are {} datas'.format(total)
print 'Actually add {} pages,insert {} datas,per page has {} datas,the last page insert {} datas.'.format(int(page_num), count,step + 1, last_page_data_num)
except ZeroDivisionError:
print 'There are {} datas'.format(total)
print 'The data already exists in the database'
def main():
# 指定redis数据库
rediscli = redis.StrictRedis(host='IP‘, port=PORT, password='PASSWORD', db=0)
# 指定mysql数据库
mysqlcli = pymysql.connect(host="localhost", port=3306, user="USER", passwd="PASSWORD", db="task", charset="utf8")
# 指定unique_id为唯一索引
point_unique = 'alter table tb_stop_record add unique key(`unique_id`)'
mysqlcli.cursor().execute(point_unique)
# 设置lrange取值的起始值和终止值,步长,页数初始值设为0
page_start = 0
step = 2
page_end = page_start + step
page_num = 0
# 统计出页数
while True:
data = rediscli.lrange('acct_stop_record', start=page_start, end=page_end)
# 判断获取到的数据是否为空
if data:
page_start = page_end + 1
page_end = page_start + step
page_num += 1
# 如果获取到的数据为空,则跳出循环
else:
break
# 重新设置lrange取值的起始值和终止值,步长,数据总数初始值设为0,实际插入的数据条数初始值为0
page_start = 0
step = 2
page_end = page_start + step
total = 0
count = 0
for page in range(0, page_num):
data_list = rediscli.lrange('acct_stop_record', start=page_start, end=page_end)
page_start = page_end + 1
page_end = page_start + step
params = []
for data in data_list:
total += 1
item = json.loads(data)
if item.get('session_id'):
params.append(([item.get('session_id'), item.get('unique_id'), item.get('member_name')]))
else:
continue
try:
# 使用cursor()方法获取游标
cursor = mysqlcli.cursor()
sql = "insert ignore into tb_stop_record(session_id,unique_id,member_name) " \
"VALUES(%s,%s,%s)"
# 添加的数据量
add_num = cursor.executemany(sql, params)
count = count + add_num
mysqlcli.commit()
# 关闭游标
cursor.close()
except pymysql.Error as e:
mysqlcli.rollback()
print "insert error", e
try:
# 根据实际插入的条数和每页的条数,计算出页数,用math.ceil()向上取整
if count % (step + 1) == 0:
page_num = count / (step + 1)
else:
page_num = math.ceil(count / (step + 1))
# 计算最后一页实际插入的条数
if count % int(page_num) == 0:
last_page_data_num = step + 1
else:
last_page_data_num = count % int(step + 1)
print 'There are {} datas'.format(total)
print 'Actually add {} pages,insert {} datas,per page has {} datas,the last page insert {} datas.'.format(int(page_num), count,step + 1, last_page_data_num)
except ZeroDivisionError:
print 'There are {} datas'.format(total)
print 'The data already exists in the database'
3. 执行
"""
一个python的文件有两种使用的方法,第一是直接作为脚本执行,第二是import到其他的python脚本中被调用(模块重用)执行。因此if __name__ == '__main__': 的作用就是控制这两种情况执行代码的过程,在if __name__ == '__main__': 下的代码只有在第一种情况下(即文件作为脚本直接执行)才会被执行,而import到其他脚本中是不会被执行的。
"""
if __name__ == "__main__":
main()
"""
一个python的文件有两种使用的方法,第一是直接作为脚本执行,第二是import到其他的python脚本中被调用(模块重用)执行。因此if __name__ == '__main__': 的作用就是控制这两种情况执行代码的过程,在if __name__ == '__main__': 下的代码只有在第一种情况下(即文件作为脚本直接执行)才会被执行,而import到其他脚本中是不会被执行的。
"""
if __name__ == "__main__":
main()
4. 创建shell文件并调用Python文件
在终端命令行中输入以下命令,创建一个shell脚本
vim redis_mysql_script.sh
进入后开启编辑模式,输入一下代码:
#! /bin/bash
python filename.py
退出编辑模式,保存并退出,在命令行中执行shell脚本:
sh redis_mysql_script.sh
数据转存成功。
三. 总结
这一过程看似挺简单,实际上要注意的点不少,例如从redis数据库中取出数据时如果用blpop或者lpop的话,会将原库中的数据删除,而一旦数据在mysql中没插入成功,那么我们的数据就丢失了。如果我们使用lrange方法来读取数据,能保证数据的安全性。sql语句中占位符也是一个值得注意的地方,不管是什么类型,统一用%s作为占位符。还有redis读取出的数据字段的个数和mysql表中的字段个数不相等的问题,也需要注意。还有效率问题,如果用for循环来遍历这个列表,那么在列表容量大的情况下,效率会很低,因此,需要用批量来读取,批量插入mysql数据库。还有要解决对重复数据插入的解决。