-
MyCat - 使用篇(1)
数据库路由中间件MyCat - 使用篇(1)
基本概念
直接介绍概念太枯燥了,还是拿个和背景篇相似的例子介绍
业务场景:客户完成下单,快递员接受并更新运单状态,客户可以随时查看运单状态的任务。一票快递可能有多个子母件。同时,我们需要标记每个运单的状态,运单状态的解释和含义保存在运单状态字典表中。
因此,我们需要建立如下表:
我们现在按照业务将数据库垂直拆分成运单库(单表2000tps,6000W数据),快递员库(单表1500tps,100W数据),客户库(单表 1500tps,1000W数据记录);假设每个MySQL数据库单表不能超过2000W数据,单表不能超过1000tps。那么运单库则需要分成3片, 客户库需要分成2片,统一由MyCat管理。如下图所示:
1.逻辑库
MyCat作为一个中间件,对应用应为无感知的。
应用访问MyCat,根据之前所述,应用感知到后台只是一个(或者多个,和访问MySQL实例一样)数据库(假设只有一个数据库,这个库叫SF,里面有运单相关表,快递员相关表和客户相关表);这里MyCat的数据库就是逻辑库。访问MyCat,结果应该如下面所示
虽然其中的表可能存在于不同的库,但是表面上,他们属于同一个MyCat实例中的同一个逻辑库。所以,虽然上面的架构图显示他们不在同一个数据库,但是在MyCat中,他们在同一个逻辑库。2.逻辑表
在逻辑库下的表就是逻辑表。逻辑表可以分片,也可以不分片。
orders表明显是要分片的表,但是在MyCat看来,他们虽然分布在不同的分片节点上(分布在不同的MySQL数据库上),但仍视为是同一个逻辑表,在同一个逻辑库里。2.1分片表
分片表,是指那些原有的很大数据的表,需要切分到多个数据库的表,这样,每个分片都有一部分数据,所有分片构成了完整的数据。分片表都有自己的分片规则,根据分片规则确定分片。
配置里面,如下配置:<table name="orders" primaryKey="id" dataNode="test$1-2" rule="mod-long"> </table>
- 1
- 2
意思就是用mod-long规则根据主键id将运单表orders分割到test1,test2这两个数据库(分片节点)上。
请求情况1:select * from orders where id = 1;
- 1
对于分片表的查询,如果按照分片列查询,则请求只会被发送到一个分片上。
请求情况2:select * from orders where id < 100 and id > 0;
- 1
对于分片表的查询,如果按照分片列范围(在字段类型支持范围的情况下)查询,则请求会根据分片规则计算两个边界值,然后将请求发送到对应结果的分片上,并合并每个分片的结果。
请求情况3:select * from orders where initialpoint = 'Beijing';
- 1
像这种根据非分片列查询的情况,请求会被发送到所有分片上,并合并每个分片的结果。
请求情况4:
请求为更新类型的sql语句,与查询的三种情况相同处理。2.2 非分片表
一个数据库中并不是所有的表都很大,某些表是可以不用进行切分的,非分片是相对分片表来说的,就是那些不需要进行数据切分的表。
例如:<table name="courier" primaryKey="id" dataNode="test3"> </table>
- 1
- 2
意思就是快递员表不用分片,保存在test3这个分片节点上。
对于非分片表的操作和对普通数据库的一样,因为不涉及到分布式数据库。2.3 ER表
关系型数据库是基于实体关系模型(Entity-Relationship Model)之上,通过其描述了真实世界中事物与关系,Mycat中的ER表即是来源于此。根据这一思路,提出了基于E-R关系的数据分片策略,子表的记 录与所关联的父表记录存放在同一个数据分片上,即子表依赖于父表,通过表分组(Table Group)保证数据Join不会跨库操作。
表分组(Table Group)是解决跨分片数据join的一种很好的思路,也是数据切分规划的重要一条规则。
如下:<!-- 运单表,对主键id对2取模 --> <table name="orders" primaryKey="id" dataNode="test$1-2" rule="mod-long"> <!-- 运单子母件表,运单表的子表,order_id与orders的id列对应 --> <childTable name="orders_cargo" joinKey="order_id" parentKey="id"> </childTable> </table>
- 1
- 2
- 3
- 4
- 5
- 6
运单表为分片表,运单表和运单子母件表为一对多关系,可以做成父子表。
对于子表的sql请求,都是通过joinKey对应到父表对应字段后,按照之前分片表的规则进行处理。2.4 全局表
一个真实的业务系统中,往往存在大量的类似字典表的表,这些表基本上很少变动,字典表具有以下几个特性:
- 变动不频繁
- 数据量总体变化不大
- 数据规模不大,很少有超过数十万条记录。
对于这类的表,在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,所以Mycat中通 过数据冗余来解决这类表的join,即所有的分片都有一份数据的拷贝,所有将字典表或者符合字典表特性的一些表定义为全局表。
数据冗余是解决跨分片数据join的一种很好的思路,也是数据切分规划的另外一条重要规则
比如:<!-- 运单状态信息表,公共表,放在和运单表同样的分片上 --> <table name="order_status_interception" primaryKey="id" type="global" dataNode="test$1-2"> </table>
- 1
- 2
- 3
运单状态信息字典表,只是注释每种运单状态,就是典型的字典表,与分片表orders为多对一的关系。
对于全局表,所有的查询请求,只会发送到其中一个全局表分片上执行,所有的更新请求,会在每个全局表分片上执行。2.5 如何决定?
根据之前的描述,我们可以推断出,对于分片表的修改和查询,如果是按照分片字段进行查找的话,则请求会被转发到一个分片上。如果不是按照分片字段的 话,就会把请求发到每一个分片上进行查找。所以,分片字段的选择比较重要!对于全局表,相当于在每个分片上有一份相同的复制,修改请求会在每一个分片上执 行,但是查询只会落到一个分片上。所以,全局表尽量是不会改变的而且是需要和分片表做Join操作的,如果经常改变或者不需要做join,最好还是做成非 分片表。
先抛出了这几种逻辑表的概念,大家先有个印象。现在我们结合具体实际讨论如何决定表的类型。
首先,orders表可定是分片表。orders_cargo表是子母件表,一个order可能有多个子母件,所以,最好把orders_cargo作为orders的子表。
这种情况下,orders与orders_cargo按照对应键(就是子表按照哪个键与主表的哪个键对应进行分片。比如orders_cargo就是order_id与orders的id对应。这是以order_id与orders的id进行join结果就是对的)join结果也是正确的。
像这种简单的从属关系一对n的表,我们处理起来很简单,一般将它们按照需要做join的键设为父子表即可。但是下面的场景很麻烦,比如快递员与运单就是多对多的关系,客户对于运单也是多对多的关系(一个收方,一个寄方)。我们既有快递员需要查看自己的所有运单的场景和客户查看自己所有运单的场景。相对的,我们也有查看一个运单涉及到的快递员还有客户的场景。
customer表(客户表)以及courier表(快递员表)因为与分片表orders之间不做join操作,所以不用作为公共表。
首先,关系表可以作为公共表,这样的话,涉及到与分片表的join操作没有限制,因为在每个分片,公共表都是完整的。但是,关系表的更新很频繁,我们可能不能忍受每更新一次关系表就跑到每个分片上都更新一次(性能,可靠性考虑)。
那么作为运单的子表呢?那么查找一个运单涉及到的快递员还有客户就比较简单。因为根据运单号(也就是分片id)查询,MyCat就会根据分片规则给他定位到具体分片,而不是去按分片搜索。
但是相应的,快递员查看自己所有运单的场景就比较慢,因为请求是发送到每一个分片上查找。
作为快递员的子表也有同样的缺陷。
还有一种方法,就是这种关系表同时作为运单和快递员的子表。但是这样,目前需要应用自己去做双写。MyCat目前还没实现这种。当然,我觉得这是一个我们自己可以根据需要改进的地方。MyCat中间件根据关系冗余表关系进行双写另外,究竟取哪种方法,都是从业务出发去考虑的。在这里,如果从快递员出发去查找以及从运单出发去查找的业务压力差不多大的话,那么最好就采用关系 表同时作为运单和客户的子表这种方法。然后将快递员和运单的业务独立,每个业务应用都去维护自己的关系表,同时通过消息队列来保持关系表之间的一致性。这 样也不失为一种方法。
-
MyCat - 使用篇(2)
数据库路由中间件MyCat - 使用篇(2)
基本概念
3. 分片
3.1 分片节点(dataNode)
表被水平切分后,每个分片表所在的数据库就是一个分片节点。一个分片节点对应一个数据库(mysql数据库)。一个分片节点只能保存每个分片表的一个分片,因为db中不允许出现同名的表。
例如:<dataNode name="test1" dataHost="test" database="db1" />
- 1
这就表示,名字为test1这个分片节点,对应test节点主机(MySQL实例)主机上的db1数据库
3.2 节点主机(dataHost)
分片节点究竟被放在那个主机上。对应mysql里的mysql实例:一台主机可以部署多个mysql实例,一个mysql实例可以有多个数据库。为 了规避单节点主机并发数限制,尽量将读写压力高的分片节点(dataNode)均衡的放在不同的节点主机(dataHost).
例如:<dataHost name="test" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="-1" slaveThreshold="100"> <heartbeat>select 1 from dual</heartbeat> <writeHost host="master" url="10.202.4.181:3306" user="root" password="sf123456"> <readHost host="slave" url="10.202.4.181:3307" user="root" password="sf123456"/> </writeHost> </dataHost>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这个会在之后的配置文件说明中细讲。
分片规则
就决定分片表的记录如何分布在不同的分片节点上。分片规则有很多种,我们根据业务需要,并考虑到开发,维护以及扩容的难度,来决定用哪种分片方案。
分片规则一般还涉及到全局id生成,这个之后会讲。
MyCat支持我们自己开发自己的分片规则,如何开发,我们后面会讲到(以下规则最好不要照搬,参考之后并按照自己的需要开发自己的分片方案):1. 哈希取模:
这是最常见的一种分片方案,根据分片字段(一般是主键,因为按主键查找的场景偏多)的哈希值,对分片个数取模运算,根据结果决定记录到哪个分片上。
一般分片个数最好为2的n次方,这样计算起来可以用取与运算(x%(2^n)=x&(2^n - 1)).
好处:记录平均分布(除非id生成器故意生成取模正好只为同一个数的id),压力平均分布,数据没有倾斜
坏处:扩容(增加分片)是个大问题,分片个数改变,基本很难迁移数据
配置举例:
rule.xml:<tableRule name="mod-long-rule1"> <rule> <columns>user_id</columns> <algorithm>mod-long</algorithm> </rule> </tableRule> <function name="mod-long" class="org.opencloudb.route.function.PartitionByMod"> <!-- how many data nodes --> <property name="count">3</property> </function>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
可以看出,用java反射机制加载org.opencloudb.route.function.PartitionByMod这个类,在这个 org.opencloudb.route.function的所有类都为分片算法,如何实现,将会在之后的rule.xml配置说明中提到。这个算法接 收一个参数,其实就是分片个数。之后在tableRule标签中,规定是哪一列(字段)为分片字段,对应哪一算法。
在这里,就是用user_id对3取模之后的值作为该记录分布在哪一个分片节点上。2. 路由约定:
rule.xml:
<tableRule name="file-map-rule1"> <rule> <columns>address</columns> <algorithm>file-map</algorithm> </rule> </tableRule> <function name="file-map" class="org.opencloudb.route.function.PartitionByFileMap"> <property name="mapFile">partition-file-map.txt</property> <property name="type">1</property> <property name="defaultNode">0</property> </function>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
type为零则字段类型为整型,为1则为字符串类型。维护一个对应表配置文件partition-file-map.txt,如下所示:
partition-file-map.txt:北京=0 上海=1 深圳=2 广州=2 default=3
- 1
- 2
- 3
- 4
- 5
意思就是分片字段为北京的到分片0上,上海的到分片1上,深圳和广州的到分片2上,其他的到分片3上。
如果某天发现北京的分片需要扩容,可以将北京的数据整体迁移到一个更大的分片上,之后重载配置。MyCat支持在线重载配置
好处:扩容比较灵活
坏处:数据容易有倾斜,扩容不是很灵活,而且,分片字段很难是常用查询字段(如果查询字段不是分片字段,就是全分片检索)3.范围路由约定:
也是维护一个文件,如下所示:
<tableRule name="auto-sharding-long"> <rule> <columns>user_id</columns> <algorithm>rang-long</algorithm> </rule> </tableRule> <function name="rang-long" class="org.opencloudb.route.function.AutoPartitionByLong"> <property name="mapFile">autopartition-long.txt</property> <property name="defaultNode">0</property> </function>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
autopartition-long.txt
0~1000k=0 1000k~2000k=1 default=2
- 1
- 2
- 3
就是指分片字段在0~1000k范围内的到分片0上。。。。。。
好处:保证每个分片数据稳定,扩容也比较方便
坏处:需要配合id生成器,否则按顺序自增会有压力集中在一个分片的问题。同时,扩容时同时要改变MyCat配置以及id生成器配置。及时做数据清理,id最好能复用,这个规则才能很好的应用。4.哈希范围约定:
将哈希取模与范围路由结合。
<tableRule name="sharding-by-pattern"> <rule> <columns>user_id</columns> <algorithm>sharding-by-pattern</algorithm> </rule> </tableRule> <function name="sharding-by-pattern" class="org.opencloudb.route.function.PartitionByPattern"> <property name="patternValue">64</property> <property name="defaultNode">2</property> <property name="mapFile">partition-pattern.txt</property> </function>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
0~15=0 16~31=1 32~47=2 48~63=3
- 1
- 2
- 3
- 4
哈希取模后范围在0~15的流向分片1.。。。
这样可以某种程度上减轻扩容的压力。5.部分字段:
有时候,我们并不想一个字段的所有内容都作为分片我们可以取某个字段的一部分作为分片依据。配合id生成器使用。
6.综合约定(原创,非内置):
其实,我们可以结合id生成器,做一种既好扩容,又维护不复杂,又能平均分摊压力的方法。
参考百X的某些项目,他们是项目开始就建64个库,每个库64张表。假设每张表1000w数据,那么一共能承受409.6亿的数据。。。从现在来看估计这个项目做到死也许都用不完。
不过这给我们一个思路,我们根据项目需要估计未来n年的量,在项目一开始就分配这么多库。这些库可能在项目初期位于同一个实例上。在不够用时,我们把其中某几个库迁移到其他实例上。
我们可以让id生成器去做平均分布的事情。
比如下面这个id:
01-01-XXXASD1239091234
我们用第一个-之前的数字直接作为分片id,我们为了考虑到以后的业务增长,一开始就分配了64个库。id生成器先开始只会随机平均生成00~03开头的,之后业务增长到某个程度时,再按照需求多少在某个范围生成。7.多重规则-可扩容哈希路由(原创,非内置)
是从分片字段中抽取一段做分片路由,再取另一段做自动哈希分片。同时再规定某个范围内是某个分片规则,另一范围是另一个分片规则。
id示例:
北京-A0000001
配置文件:北京(A0000000~A9999999)=0,1,2,3,4 北京(B0000000)=5,6,7,8,9 上海(00000000~10000000)=10,11 上海=10,11,12,13,14,15
- 1
- 2
- 3
- 4
意思就是,开头为北京的范围在A0000000~A9999999的根据后面的哈希值对5取模平均分布在0,1,2,3,4分片节点上。开头为北京 的范围在B0000000以上的根据后面的哈希值对5取模平均分布在5,6,7,8,9分片节点上。开头为上海的范围在 00000000~10000000的根据后面的哈希值对2取模平均分布在10,11分片节点上,剩下的开头为上海的,对6取模平均分布在 10,11,12,13,14,15上。
这样,在发现某个开头的分片不够用时,可以随时改变分片规则,同时不影响以前数据的访问。2016 二月23-
MyCat - 使用篇(3)
数据库路由中间件MyCat - 使用篇(3)
全局序列号
数据切分后,原有的关系数据库中的主键约束在分布式条件下将无法使用,因此需要引入外部机制保证数据唯一性标识,这种保证全局性的数据唯一标识的机制就是全局序列号(sequence)。
1. 本地文件方式
classpath下有一个sequence_conf.properties文件:
GLOBAL_SEQ.HISIDS= GLOBAL_SEQ.MINID=1001 GLOBAL_SEQ.MAXID=1000000000 GLOBAL_SEQ.CURID=1000
- 1
- 2
- 3
- 4
HISIDS表示历史使用过的值,MINID为ID最小值,MAXID为ID最大值,CURID为当前值。
需要在server.xml加入如下配置:<system><property name="sequnceHandlerType">0</property></system>
- 1
sequnceHandlerType 需要配置为 0,表示使用本地文件配置。
使用示例:insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,‘test’);
- 1
但是这么做,MyCat就不是无状态中间件,很难去做MyCat集群。而且,这样的id只是纯数字。最后,每次MyCat重新发布,id恢复初始值。所以,不推荐这种用法。
2.数据库方式
在数据库中建立一张表,存放 sequence 名称(name),sequence 当前值(current_value),步长(increment)每次读取多少个 sequence,假设为 K)等信息;
server.xml:<system><property name="sequnceHandlerType">1</property></system>
- 1
建表语句:
DROP TABLE IF EXISTS MYCAT_SEQUENCE; CREATE TABLE MYCAT_SEQUENCE (name VARCHAR(50) NOT NULL,current_value INT NOT NULL,increment INT NOT NULL DEFAULT 100, PRIMARY KEY(name)) ENGINE=InnoDB; INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES (‘GLOBAL’, 100000, 100);
- 1
- 2
- 3
创建相关function:
DROP FUNCTION IF EXISTS mycat_seq_currval; DELIMITER CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf-8 DETERMINISTIC BEGIN DECLARE retval VARCHAR(64); SET retval=“-999999999,null”; SELECT concat(CAST(current_value AS CHAR),“,”,CAST(increment AS CHAR)) INTO retval FROM MYCAT_SEQUENCE WHERE name = seq_name; RETURN retval; END DELIMITER; – 讴置 sequence 值 DROP FUNCTION IF EXISTS mycat_seq_setval; DELIMITER CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),value INTEGER) RETURNS varchar(64) CHARSET utf-8 DETERMINISTIC BEGIN UPDATE MYCAT_SEQUENCE SET current_value = value WHERE name = seq_name; RETURN mycat_seq_currval(seq_name); END DELIMITER; – 获叏下一个 sequence 值 DROP FUNCTION IF EXISTS mycat_seq_nextval; DELIMITER CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf-8 DETERMINISTIC BEGIN UPDATE MYCAT_SEQUENCE SET current_value = current_value + increment WHERE name = seq_name; RETURN mycat_seq_currval(seq_name); END DELIMITER;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
sequence_db_conf.properties指定 sequence 相关配置在哪个节点上
USER_SEQ=test_dn1
- 1
使用示例:
insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,‘test’);
- 1
这样做虽然MyCat为无状态而且id有持久化,并且一次可以取出多个id,通过配置可以有主从切换。但是,id还是纯数字,没有有意义的信息,而且,MyCat主从切换并不可靠,id生成有故障,则整个服务都无法正常进行,这在架构上有单点问题,是不推荐的。
3.本地时间戳方式
ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加)
server.xml:<system><property name="sequnceHandlerType">2</property></system>
- 1
sequence_time_conf.properties:
WORKID=0-31 任意整数 DATAACENTERID=0-31 任意整数
- 1
- 2
4.通过继承或者重构相关类实现
修改源代码,主要是和MyCATSequnceProcessor相关的类,定制自己的id。之后源代码篇会讲
但是全局序列号还是推荐用独立的id生成器服务(独立于MyCat的服务)去实现最佳!
安装准备
环境
Red Hat Enterprise Linux Server release 6.6 (Santiago)
java version “1.7.0_79”软件
- zookeeper 3.4.6 (MyCat监控依赖于zookeeper,同时,MyCat1.5之后引入了zk的配置方式)
- MyCat 1.5GA(1.5版本已经比较稳定):https://github.com/MyCATApache/Mycat-Server/
- MyCat-eye 1.0: https://github.com/MyCATApache/Mycat-Web 用来监控MyCat
配置并部署zookeeper
只要部署好即可,默认配置,将conf/zoo_sample.cfg 重命名为conf/zoo.cfg。启动后验证下即可(因为目前只为了为监控服务)
配置MyCat
下载MyCat的源代码,并使用maven打包安装:mvn install -Dmaven.test.skip=true. 使用生成的linux下的tar.gz文件,解压。
1. 业务分析
接下来是我们的重点,MyCat的配置,还是拿之前的例子,我们只需要一个逻辑库(schema1),运单库则需要分成3片,客户库需要分成2片, 统一由MyCat管理。业务上,客户和快递员查询和自己相关的运单比客户和快递员还有仓管员通过运单号查询相关信息的业务量少的,所以以运单为中心进行分 片。
运单表根据运单号哈希取模分片;
运单子母件表作为运单表的子表;
快递员运单关系表作为运单表的子表;
客户运单关系表作为运单表的子表;
快递员信息变动不频繁,而且量不大,但是业务上基本没有需要和快递员join的场景,作为非分片表;
客户表根据客户id做哈希取模;
运单状态信息表,运单状态信息表记录状态的解释信息,做为公共表。运单根据量放在3个分片节点上,客户根据量也放在两个分片节点上。(这里假设都撑得住未来10年的量,主要要考虑存储量级和tps/qps两个维度,采用涉及到哈希取模的分片规则,最好一开始就估计足量,避免未来的扩容麻烦)
综上,如下分片:
2. 配置conf/server.xml
server.xml几乎保存了所有mycat需要的系统配置信息。其在代码内直接的映射类为SystemConfig类。
参考完整配置:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mycat:server SYSTEM "server.dtd"> <mycat:server xmlns:mycat="http://org.opencloudb/"> <system> <property name="defaultSqlParser">druidparser</property> <!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议--> <!-- <property name="processorBufferChunk">40960</property> --> <!-- <property name="processors">1</property> <property name="processorExecutor">32</property> --> <!--默认是65535 64K 用于sql解析时最大文本长度 --> <!--<property name="maxStringLiteralLength">65535</property>--> <!--<property name="sequnceHandlerType">0</property>--> <!-- 表示使用数据库方式生成sequence. --> <property name="sequnceHandlerType">1</property> <!--<property name="backSocketNoDelay">1</property>--> <!--<property name="frontSocketNoDelay">1</property>--> <!--<property name="processorExecutor">16</property>--> <!-- <property name="mutiNodeLimitType">1</property> 0:开启小数量级(默认) ;1:开启亿级数据排序 <property name="mutiNodePatchSize">100</property> 亿级数量排序批量 <property name="processors">32</property> <property name="processorExecutor">32</property> <property name="serverPort">8066</property> <property name="managerPort">9066</property> <property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property> <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> --> <property name="serverPort">8066</property> <property name="managerPort">9066</property> </system> <user name="root"> <!--从1.4.1开始,MyCat支持密文保存密码,这里明文密码为root --> <property name="usingDecrypt">1</property> <property name="password">CrjpLhvVJkHk0EPW35Y07dUeTimf52zMqClYQkIAN3/dqiG1DVUe9Zr4JLh8Kl+1KH1zd7YTKu5w04QgdyQeDw==</property> <property name="schemas">schema1</property> </user> <user name="test"> <!--这里明文密码为test--> <property name="usingDecrypt">1</property>