暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

[译] 迁移MySQL的表到Redis

原创 严少安 2022-03-16
2101

原文链接:https://www.mortensi.com/2021/12/migrate-a-mysql-table-to-redis/
原文作者:Admin

1.png

在这篇文章中,我将列出一些从MySQL逻辑复制一张表到Redis的一些选项。我将使用经典的world数据库运行示例。 world数据库可以在这里下载,所以如果您想尝试这些示例,只需将它导入到您的MySQL实例中。

mysql> use world; Database changed mysql> show tables; +-----------------+ | Tables_in_world | +-----------------+ | city | | country | | countrylanguage | +-----------------+ 3 rows in set (0.01 sec)

有多种方法可以将表格导入Redis,让我们来看看其中的几种。在下面的例子中,我将选择一个Hash数据类型,最类似于Redis的传统表行。

Redis哈希是字符串字段和字符串值之间的映射,所以它们是表示对象的完美数据类型(例如,一个用户有许多字段,如名字、姓氏、年龄等)

使用 SELECT INTO OUTFILE 导出CSV格式,使用Python导入

为了从MySQL导出数据并将其导入Redis,我们可以使用MySQL SELECT into OUTFILE 功能,它可以生成一个格式化的输出文件。特别是,我将确保字段用逗号分隔。让我们试一试:

mysql> SELECT * INTO OUTFILE '/tmp/city.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY 'n' FROM world.city; ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

噢,MySQL默认配置禁止导出数据到文件系统。让我们在配置文件my.cnf中修复它:

[mysqld] secure_file_priv=/tmp

现在我可以导出:

mysql> SELECT * INTO OUTFILE '/tmp/city.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY 'n' FROM world.city; Query OK, 4079 rows affected (0.01 sec)

这是示例:

"1","Kabul","AFG","Kabol","1780000" "2","Qandahar","AFG","Qandahar","237500" "3","Herat","AFG","Herat","186800" "4","Mazar-e-Sharif","AFG","Balkh","127800" "5","Amsterdam","NLD","Noord-Holland","731200" "6","Rotterdam","NLD","Zuid-Holland","593321" "7","Haag","NLD","Zuid-Holland","440900" "8","Utrecht","NLD","Utrecht","234323"

现在数据是CSV格式,我能使用一个简单的Python脚本导入它,下面是个可用的示例(参考说明设置Python环境以连接到Redis)。

#!/usr/bin/python3 import redis from csv import reader r = redis.Redis(host='127.0.0.1', port=4321, password='') with open('/tmp/city.csv', 'r') as cities: csv_reader = reader(cities) for row in csv_reader: r.hset("city:" + row[0], mapping={ "Name" : row[1], "CountryCode" : row[2], "District" : row[3], "Population" : row[4] })

让我们检查下是否正确导入:

127.0.0.1:4321> SCAN 0 MATCH city:* COUNT 5
1) "3072"
2) 1) "city:2316"
   2) "city:749"
   3) "city:570"
   4) "city:3625"
   5) "city:3328"
   6) "city:1771"

我也可以验证Hash项的内容:

127.0.0.1:4321> HGETALL city:4059
1) "Name"
2) "Cary"
3) "CountryCode"
4) "USA"
5) "District"
6) "North Carolina"
7) "Population"
8) "91213"

使用mysqldump导出CSV格式

备份工具mysqldump可作为客户端,可以从远端导出数据,而不需要修改让任何服务器配置。可以此来代替SELECT INTO OUTFILE,对有些人来说,它可能代表一个安全问题,因为它是在服务器本身导出行数据到文件系统,并需要启用secure_file_priv

mysqldump --host=127.0.0.1 --user=root --password --tab=/tmp --fields-enclosed-by='"' --fields-terminated-by=',' --lines-terminated-by='n' world city

一旦文件导出(mysqldump导出两个文件,表定义保存在city.sql文件,数据以CSV格式保存在city.txt文件),数据可以按照说明导入。

使用RIOT导入CSV备份

假设你有一个MySQL表的CSV备份,事实上,CSV可以来自于任何数据源,比如Excel表格,Postgres等等。如果你不想编码来导入到Redis,你可以使用RIOT(Redis输入输出工具),用于将异构数据源迁移到Redis。

2.png
Redis输入输出工具: RIOT

Redis输入/输出工具(RIOT)是一个工具集,其设计目的在于帮助您从Redis导入导出数据。

在本节中,我将演示RIOT的CSV导入工具,称之为RIOT文件。首先,安装它(我使用Mac,但是您可以参考文档了解其他安装方法)

brew install redis-developer/tap/riot-file

安装后,测试帮助方法:

(redisvenv) bash-3.2$ riot-file --help The operation couldn’t be completed. Unable to locate a Java Runtime. Please visit http://www.java.com for information on installing Java.

是的,您也需要从www.java.com下载JRE,完成后,继续:

(redisvenv) bash-3.2$ riot-file import hset --help Usage: riot-file import hset [OPTIONS] Set hashes from input -H, --help Show this help message and exit -s, --separator=<str> Key separator (default: :) -r, --remove Remove key or member fields the first time they are used --ignore-missing Ignore missing fields -p, --keyspace=<str> Keyspace prefix -k, --keys=<fields>... Key fields --include=<field>... Fields to include --exclude=<field>... Fields to exclude

现在,我们必须将CSV数据源映射到适当的Hash数据结构。我们原始的CSV文件第一行没有标题。如果我们想加标题,我们甚至可以按照顺序从MySQL information_schema中抽取。

注意:MySQL SELECT INTO OUTFILE不会在导出的数据集添加头,这必须使用正确的SQL语法手动添加,或编辑导出的CSV文件。

mysql> SELECT group_concat(COLUMN_NAME ORDER BY ORDINAL_POSITION separator ',') FROM information_schema.COLUMNS WHERE TABLE_NAME='city' AND TABLE_SCHEMA='world'; +-------------------------------------------------------------------+ | group_concat(COLUMN_NAME ORDER BY ORDINAL_POSITION separator ',') | +-------------------------------------------------------------------+ | ID,Name,CountryCode,District,Population | +-------------------------------------------------------------------+ 1 row in set (0.00 sec)

添加ID,Name,CountryCode,District,Population到CSV文件的第一行。但是我们仍然可以继续手动执行映射。在这里,我们指定字段在CSV文件中出现的顺序,指定表名(keyspace),及补充字段名(key)(例如city:1234)。

riot-file -h 127.0.0.1 -p 4321 import /tmp/city.csv --fields ID Name CountryCode District Population hmset --keyspace city --keys ID

或者,如果CSV文件有从MySQL获得的头,我们可以添加--header参数来使用手动添加的CSV头。

riot-file -h 127.0.0.1 -p 4321 import /tmp/city.csv --header hmset --keyspace city --keys ID

使用 RIOT DB 从MySQL导入

为了从没有中间转储备份导入MySQL,您可以编码一个方案,来连接源数据库和目标Redis库,或者简单地使用RIOT DB。下面来安装它:

brew install redis-developer/tap/riot-db

然后像往常一样导入,指定表名和列名,提供正确的连接信息,显然,需要定义导入数据集的查询语句。

riot-db -h 127.0.0.1 -p 4321 import "SELECT * FROM city" --url jdbc:mysql://root:Oracle1*@127.0.0.1:3306/world hmset --keyspace city --keys ID

导出并以RESP格式做大量插入

到目前为止,我已经探索了一些逻辑方法,但是对于大量插入,最高速度需要达到百万个键,有一种不同的方法,即使用redis-cli的管道模式,读取数据的同时,立即发送到服务器。特别地,使用这种方法,我们将使用Redis协议(RESP)。关于大量插入的更多信息请参阅这里

它可能导出RESP格式的表,并转瞬流到Redis服务器。让我们看看它是如何工作的。我们能使用SQL来生成数据,并以这种格式发送到Redis服务器:

*<args><cr><lf>
$<len0><cr><lf>
<arg0><cr><lf>
$<len1><cr><lf>
<arg1><cr><lf>
...
$<lenN><cr><lf>
<argN><cr><lf>

这里:

  • args 是参数的数量
  • lenN 是参数跟随的长度
  • argN 是参数

假设我们要为MySQL表中的每一行添加一个Hash,每个Hash将插入4个字段,在RESP协议中生成转储的SQL语句将是这样的:

SELECT CONCAT( "*10\r\n", '$', LENGTH(redis_cmd), '\r\n',redis_cmd, '\r\n','$', LENGTH(redis_key), '\r\n',redis_key, '\r\n', '$', LENGTH(hkey1), '\r\n',hkey1, '\r\n','$', LENGTH(hval1), '\r\n', hval1, '\r\n' '$', LENGTH(hkey2), '\r\n',hkey2, '\r\n','$', LENGTH(hval2), '\r\n', hval2, '\r\n' '$', LENGTH(hkey3), '\r\n',hkey3, '\r\n','$', LENGTH(hval3), '\r\n', hval3, '\r\n' '$', LENGTH(hkey4), '\r\n',hkey4, '\r\n','$', LENGTH(hval4), '\r\n', hval4, '\r\n' ) FROM ( SELECT 'HSET' AS redis_cmd, CONCAT('city:',ID) AS redis_key, 'Name' AS hkey1, Name AS hval1, 'CountryCode' AS hkey2, CountryCode AS hval2, 'District' AS hkey3, District AS hval3, 'Population' AS hkey4, Population AS hval4 FROM world.city ) AS t;

保存SQL到resp.sql文件,并用Redis的管道模式从MySQL流到Redis:

bash-3.2$ mysql -h 127.0.0.1 -uroot -p -Dworld --skip-column-names --raw </tmp/resp.sql |redis-cli -p 4321 --pipe Enter password: All data transferred. Waiting for the last reply... Last reply received from server. errors: 0, replies: 4079

数据已经导入到Redis服务器!

使用Redis的MySQL连接器:redis-connect-mysql

最后一节,我将介绍如何使用Redis连接套件,特别是 redis-connect-mysql。您可以阅读这份文档:

redis-connect-mysql第一次连接MySQL时,它会读取所有模式的一致性快照。当快照创建完成,连接器将连续发送MySQL的变更并生成相应的insert,update或delete事件。

因此,它不仅仅是一个迁移工具,还是一个连接异构数据库的复制和传输工具。Redis CDC (变更数据捕捉)曾在RedisConf 2021上演示,所以请查看演示的详细内容。

(译者注:这段视频在油管上,此处跳过。RedisCDC: Seamless database migrations and continuous changed-data replication, Redis Labs

我现在没有运行使用RedisCDC的示例,但是请查看代码库来了解如何操作它。

清理

为了移除那些导入的Hash值,您可以执行下面的命令:

redis-cli -p <PORT> --scan --pattern city:* -i 0.01 | xargs redis-cli -p <PORT> unlink

总结

现在,您能从MySQL导出一张表,或者从其他的关系型数据库,任何可以导出CSV的数据库,或者RIOT DB,或者RedisCDC能连接的多种数据库。

支持RedisCDC的连接器在这里,支持RIOT DB的连接器在这里

接下来,您可能想知道您能用这些创建在Redis实例中的哈希值做些什么。所以我在下篇文章中,将分享一些例子,关于在Redis中存储、检索、更改、删除、索引和检索数据的命令。

那么现在呢? 你可能想知道你可以用这些在Redis实例中创建的哈希值做什么。所以在下一篇文章中,我将分享几个在Redis中存储、检索、更改、删除、索引和搜索数据的命令示例。别走开,我很快就会写的!

最后修改时间:2022-03-23 10:40:48
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
1人已赞赏
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论