目录
-
一、导读
DBLE是一款应用较为广泛的开源分布式数据库中间件产品。本文主要结合实际生产中的案例,介绍DBLE缓存机制、缓存使用限制、以及缓存种类。下面将通过使用DBLE进行主键更新查询操作,返回结果异常的实际案例来进行演示分析。
-
二、现象描述
前期开发同学反馈在进行数据修正时,查询丢失部分数据。A表进行了数据的分片,分片方式为hash,分片节点数量为2。首先连接DBLE在表A中插入主键值1和主键值2两条数据,使其分布在不同分片上。更新主键值1为2(两条数据不在同一分片上不会产生主键冲突),根据主键值2进行查询,只返回一条数据,丢失更新后数据。现将问题复现如下。
2.1环境及版本
DBLE版本:2.19.11.99
MySQL版本:5.7.28
涉及分片表:test
主键为mobile_no
分片字段为user_code
分片拆分规则:hash
节点数量:2
2.2表结构示例
create table test(
mobile_no varchar(32) not null,
user_code varchar(32) not null,
PRIMARY KEY (mobile_no),
INDEX idx_usercode(user_code)
) ENGINE=InnoDB CHARACTER SET =utf8mb4;
2.3查询结果异常模拟
- 通过DBLE插入两条数据使其分别分布在node1、node2分片上。
mysql> insert into test values(13600000001,0);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test values(13800000002,1);
Query OK, 1 row affected (0.00 sec)
2)根据主键查询(缓存主键信息)
mysql> select * from test where mobile_no=13600000001;
+-------------+-----------+
| mobile_no | user_code |
+-------------+-----------+
| 13600000001 | 0 |
+-------------+-----------+
1 row in set (0.00 sec)
mysql> select * from test where mobile_no=13800000002;
+-------------+-----------+
| mobile_no | user_code |
+-------------+-----------+
| 13800000002 | 1 |
+-------------+-----------+
1 row in set (0.00 sec)
3)更新主键并查询(因所处不同分片,主键不产生冲突),执行计划查询分发到所有节点,查询结果符合预期。
mysql> update test set mobile_no=13800000002 where user_code=0;
Query OK, 1 row affected (0.00 sec)
mysql> explain select * from test where mobile_no in (13600000001,13800000002);
+-----------+----------+-----------------------------------------------------------------+
| DATA_NODE | TYPE | SQL/REF |
+-----------+----------+-----------------------------------------------------------------+
| dn1 | BASE SQL | select * from test where mobile_no in (13600000001,13800000002) |
| dn2 | BASE SQL | select * from test where mobile_no in (13600000001,13800000002) |
+-----------+----------+-----------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> select * from test where mobile_no in (13600000001,13800000002);
+-------------+-----------+
| mobile_no | user_code |
+-------------+-----------+
| 13800000002 | 0 |
| 13800000002 | 1 |
+-------------+-----------+
2 rows in set (0.00 sec)
修改条件再次查询,查询结果与实际不符,丢失更新后的数据。
mysql> select * from test where mobile_no=13800000002;
+-------------+-----------+
| mobile_no | user_code |
+-------------+-----------+
| 13800000002 | 1 |
+-------------+-----------+
1 row in set (0.00 sec)
查看执行计划如下
mysql> explain select * from test where mobile_no=13800000002;
+-----------+----------+------------------------------------------------+
| DATA_NODE | TYPE | SQL/REF |
+-----------+----------+------------------------------------------------+
| dn2 | BASE SQL | select * from test where mobile_no=13800000002 |
+-----------+----------+------------------------------------------------+
1 row in set (0.00 sec)
执行计划显示查询仅路由到dn2节点,并没有路由dn1节点。
-
三、原因排查
通过以上实验,我们怀疑DBLE对分片表test进行了主键缓存,当更新主键值时,缓存并没有更新,导致路由结果错误。下面我们查看下。
3.1连接DBLE管理端进行查看
通过连接DBLE的9066管理端口,执行show @@cache,可以显示当前缓存的使用情况。
mysql> show @@cache;
+-------------------------------------------+-------+------+--------+------+------+---------------------+---------------------+
| CACHE | MAX | CUR | ACCESS | HIT | PUT | LAST_ACCESS | LAST_PUT |
+-------------------------------------------+-------+------+--------+------+------+---------------------+---------------------+
| ER_SQL2PARENTID | 1000 | 0 | 0 | 0 | 0 | | |
| SQLRouteCache | 10000 | 0 | 5 | 0 | 0 | 2021/05/14 10:00:42 | |
| TableID2DataNodeCache.`sysbench_1`_`test` | 10000 | 2 | 6 | 4 | 2 | 2021/05/14 10:00:42 | 2021/05/14 09:56:22 |
+-------------------------------------------+-------+------+--------+------+------+---------------------+---------------------+
3 rows in set (0.00 sec)
列描述如下:
CACHE: cache名
MAX: 最大容量
CUR: 当前容量
ACCESS: 缓存查询次数
HIT: 命中次数
PUT: 加入缓存计数器
LAST_ACCESS:上一次查询时间
LAST_INPUT: 上一次加入缓存时间
再次执行mobile_no=13800000002查询语句,管理端查看:
mysql> show @@cache;
+-------------------------------------------+-------+------+--------+------+------+---------------------+---------------------+
| CACHE | MAX | CUR | ACCESS | HIT | PUT | LAST_ACCESS | LAST_PUT |
+-------------------------------------------+-------+------+--------+------+------+---------------------+---------------------+
| ER_SQL2PARENTID | 1000 | 0 | 0 | 0 | 0 | | |
| SQLRouteCache | 10000 | 0 | 6 | 0 | 0 | 2021/05/14 10:01:55 | |
| TableID2DataNodeCache.`sysbench_1`_`test` | 10000 | 2 | 7 | 5 | 2 | 2021/05/14 10:01:55 | 2021/05/14 09:56:22 |
+-------------------------------------------+-------+------+--------+------+------+---------------------+---------------------+
3 rows in set (0.00 sec)
两次show @@cache结果分析:
CACHE列:
TableID2DataNodeCache.`sysbench_1`_`test` : 缓存test表主键与分片对应关系。
CUR PUT 值说明表test主键已被缓存,再次查询HIT值发生变化表示查询缓存命中。
3.2关闭主键路由缓存
cache文件cacheservice.properties中将#layedpool.TableID2DataNodeCache=encache,10000,18000
表主键节点缓存注释,或者去掉schema.xml<table name=”test”中cacheKey=”mobile_no”>.
重新加载后再次查询:
mysql> explain select * from test where mobile_no=13800000002;
+-----------+----------+------------------------------------------------+
| DATA_NODE | TYPE | SQL/REF |
+-----------+----------+------------------------------------------------+
| dn1 | BASE SQL | select * from test where mobile_no=13800000002 |
| dn2 | BASE SQL | select * from test where mobile_no=13800000002 |
+-----------+----------+------------------------------------------------+
2 rows in set (0.00 sec)
mysql> select * from test where mobile_no=13800000002;
+-------------+-----------+
| mobile_no | user_code |
+-------------+-----------+
| 13800000002 | 0 |
| 13800000002 | 1 |
+-------------+-----------+
2 rows in set (0.00 sec)
查询结果正确,执行计划显示路由信息无误,验证了我们的猜想,DBLE果然缓存了我们的主键对应数据节点信息,并依照缓存进行查询路由。
-
四、DBLE缓存介绍
4.1 DBLE主键缓存机制
首先DBLE不在逻辑层做主键约束,DBLE通过schema.xml文件中的table标签cacheKey属性来指定逻辑表的主键。下发到底层各节点执行update语句可以成功(此项需要开发人员注意)。
当表主键节点缓存开启时,根据主键查询的SQL语句第一次执行时,DBLE会对其结果进行分析,确定该主键在哪个dataNode上,并进行该主键到分片ID的缓存。那么,下次再通过该主键进行查询时,DBLE就会通过缓存确定数据在哪个datanode上,然后在该datanode上执行查询。
4.2 DBLE主键缓存限制
1. DBLE启动时不会触发缓存;
2. insert语句不会触发缓存;
3. 首次通过主键查询时,才会触发缓存,且仅缓存where条件指定的(必须通过=或in指定,2.3.2模拟首次缓存);
4. 全表查询不会触发缓存,通过非主键查询不会触发缓存,通过主键+非主键查询也不会触发缓存。只有通过主键查询才触发缓存;
5. 按主键分片的表,DBLE不会作主键缓存;
4.3 DBLE缓存数据种类
DBLE缓存的数据除了上文讲述的TableID2DataNodeCache(表主键节点缓存),还有以下两种:
SQLRouteCache:SQL路由缓存,通过缓存SQL语句的路由信息,下次查询时,无需再次进行路由计算,直接从缓存中获取路由信息,然后发到各个节点执行。缓存的 key 是 schema+SQL 语句。
ER_SQL2PARENTID:ER关系的缓存目前只是在 Insert 语句中才会使用缓存,子表插入数据的时候,根据 joinKey 的值,判断父表所在分片,从而定位子表分片,分片信息put缓存,以便下次直接获取。
在cacheservice.properties文件中我们可以看到如下配置项:
pool.SQLRouteCache=encache,10000,1800
pool.ER_SQL2PARENTID=encache,1000,1800
layedpool.TableID2DataNodeCache=encache,10000,18000
其格式示例
pool.SQLRouteCache=type,max_size,expire_seconds
type指定缓存类型;max_size指定缓存的最大大小,单位是字节;expire_seconds指定缓存项的生命周期,单位是秒。type类型目前支持三种:分别为ehcache(DBLE默认使用本地ehcache缓存作为cache实现),mapdb(用MapDB数据库引擎作为cache实现),leveldb(用leveldb数据库作为cache实现),rocksdb(用RocksDB数据库引擎作为cache实现)。
-
五、总结
合理使用DBLE缓存机制,可以有效提高系统的处理速度和吞吐量。然而缓存的使用存在一些限制,不了解实现机制原理,可能会造成一些返回结果异常或者无法有效利用缓存机制。通过上文的解释说明,希望可以帮助大家更好的熟悉和利用DBLE缓存。