• GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
  • 1、引子

  • 2、AFTER 的写一致性

  • 3、AFTER 的读一致性

  • 4、AFTER 执行流程

  • 5、BEFORE 执行流程

  • 6、一些思考

  • 7、参考文档

1、引子

某次测试过程中,发现在 AFTER 级别下,节点故障会导致集群无法进行事务提交,同时,当事务进入提交阶段后,其它节点无法开启只读事务。整个集群无法正常提供服务,直到故障节点被踢出集群。

以下首先复现上述故障场景的步骤:

1、初始化一个3节点的集群。集群信息如下:

   mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | c223cde5-0719-11ec-8295-ec5c6826bca3 | 127.0.0.1 | 13000 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | c22847e0-0719-11ec-b30b-ec5c6826bca3 | 127.0.0.1 | 13004 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | c22c3752-0719-11ec-98ef-ec5c6826bca3 | 127.0.0.1 | 13002 | ONLINE | PRIMARY | 8.0.25 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.00 sec) mysql> select @@group_replication_member_expel_timeout;
+------------------------------------------+
| @@group_replication_member_expel_timeout |
+------------------------------------------+
| 1000 |
+------------------------------------------+
1 row in set (0.00 sec)

2、在 AFTER 级别下创建表并插入一条数据

13000-conn1
mysql> set session group_replication_consistency='AFTER';
Query OK, 0 rows affected (0.00 sec) mysql> create table t1 (c1 int primary key, c2 int);
Query OK, 0 rows affected (0.12 sec) mysql> insert into t1 values (1,1);
Query OK, 1 row affected (0.03 sec)

3、强杀一个节点,由于设置的 expel_timeout 设置为1000秒,故障节点变成 UNREACHABLE状态

    mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | c223cde5-0719-11ec-8295-ec5c6826bca3 | 127.0.0.1 | 13000 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | c22847e0-0719-11ec-b30b-ec5c6826bca3 | 127.0.0.1 | 13004 | UNREACHABLE | PRIMARY | 8.0.25 |
| group_replication_applier | c22c3752-0719-11ec-98ef-ec5c6826bca3 | 127.0.0.1 | 13002 | ONLINE | PRIMARY | 8.0.25 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.00 sec)

4、此时,再次插入一条数据,无法返回,语句处于等待提交阶段

    13000-conn1
mysql> insert into t1 values (2,2); 13000-conn2
mysql> select time, state, info from information_schema.processlist;
+------+--------------------------------------------------------+--------------------------------------------------------------+
| time | state | info |
+------+--------------------------------------------------------+--------------------------------------------------------------+
| 0 | executing | select time, state, info from information_schema.processlist |
| 193 | waiting for handler commit | Group replication applier module |
| 228 | Waiting on empty queue | NULL |
| 193 | Slave has read all relay log; waiting for more updates | NULL |
| 50 | waiting for handler commit | insert into t1 values (2,2) |
+------+--------------------------------------------------------+--------------------------------------------------------------+
5 rows in set (0.01 sec)

5、再次登录另一个活着的节点,无法执行查询操作

13002-conn1
mysql> set @@group_replication_consistency='eventual';
Query OK, 0 rows affected (0.00 sec) mysql> select * from t1; 13002-conn2
mysql> select time, state, info from information_schema.processlist;
+------+--------------------------------------+--------------------------------------------------------------+
| time | state | info |
+------+--------------------------------------+--------------------------------------------------------------+
| 0 | executing | select time, state, info from information_schema.processlist |
| 354 | waiting for handler commit | Group replication applier module |
| 403 | Waiting on empty queue | NULL |
| 225 | waiting for handler commit | NULL |
| 13 | Executing hook on transaction begin. | select * from t1 |
+------+--------------------------------------+--------------------------------------------------------------+
5 rows in set (0.01 sec)

2、AFTER 的写一致性

上述故障的第一个问题是,在 MGR 集群中,当事务读写一致性级别设置为 AFTER 后,任何单点的故障都会导致集群不可用。在默认设置情况下,节点从故障到被踢出集群,一般需要5至10秒的时间,在这段时间内,整个数据库是无法进行写事务的提交的,当然,如上述测试一般,如果将expel_timeout设置的更大,则会有更长的时间无法正常工作。这对线上业务来说,将是一次灾难。

查看MGR读写一致性的原始worklog,发现上述现象是符合设计预期的,并不是一个bug。该 worklog 的以下需求即解释了上述的问题。

FR-08: When group_replication_consistency=AFTER or BEFORE_AND_AFTER,
if there are unreachable members but the group still has a
reachable majority, the transaction will wait until that
members are reachable or leave the group.

如此处理的一个好处是,可以满足部分业务对严格数据一致性的需求,但对一般的业务却是极度不友好的。因此,对于一般的业务,并不推荐使用 AFTER 机制。然而,由于非 AFTER 机制下,事务消息只是通过 paxos 协议在内存层面达成多数派,而并不要求数据落盘,因此,如果多数节点同时故障,是存在丢失数据风险的。

3、AFTER 的读一致性

上述故障的第二个问题是,当节点1 处于事务commit阶段过程中,在节点2上,甚至都无法在 eventual 级别下开启一个只读事务。此时,在故障节点被踢出集群之前,节点2无法提供任何的读写服务。

还是从 worklog 中,解释了这个问题。

FR-06: When group_replication_consistency=AFTER or BEFORE_AND_AFTER,
since the remote ONLINE members do acknowledge the
transaction on prepare, the new transactions on those members
shall be held until the preceding prepared are committed.

也就是说,如果事务在 remote 节点进入了 prepared 阶段,则必须等待该事务完成提交才能开启新的事务,不论 consistency 是何种级别。但是如此处理的一个弊端是,AFTER 级别下,不光会导致执行节点性能吞吐降低,其它节点作为只读节点性能也会降低。

在多主写部署中,性能的影响可能更大。另一个问题是,如果用户恶意开启 AFTER 级别执行一个大事务操作,会导致其它节点长时间无法开启新的事务。官方 worklog 中也提到这个问题。

SECURITY CONTEXT
================
From a point of view of malicious attack to the group, since when
group_replication_consistency=AFTER or BEFORE_AND_AFTER a
transaction will wait for a acknowledge from all ONLINE members, a
UNREACHABLE member will block a transaction execution until that
member is reachable or leaves the group. A malicious user can set group_replication_consistency=AFTER or
BEFORE_AND_AFTER on long lived transactions, which may block new
transactions while those long lived transactions are being applied.

4、AFTER 执行流程

首先,在事务执行节点上的流程如下:

1.首先,事务进入提交阶段后,会执行一个before_commit的HOOK,在 mgr 中,对应的实现是 group_replication_trans_before_commit。AFTER 的一致性保证通过该接口实现。

2.假设事务 T1 在节点 M1 上执行,如果是 AFTER 级别,会通过 paxos 发送一个携带事务全部数据的Transaction_with_guarantee_message消息,消息类型为 CT_TRANSACTION_WITH_GUARANTEE_MESSAGE。

3.节点接收到该消息并处理时,首先会获取当前集群中的 online_members。这里需要注意的是,即使节点状态变为 UNREACHABLE,只要没有踢出集群,也会认为是 online_members。

4.节点 M1 需要等待其它节点的消息反馈

5.节点M1只有收到上述 online_members 中所有节点的 prepared 消息时,才能继续完成提交

接下来,看一下其它节点(以M2节点为例)处理 AFTER 事务的流程:

1.首先,paxos 接收到事务,并进入事务执行阶段

2.事务T1在M2进入提交阶段时,调用 before_hook进行处理,不同于 M1的用户线程,M2上的复制线程是在 GR_APPLIER_CHANNEL 上执行

3.将事务加入到 prepared 事务列表

4.发送 transaction_prepared 消息给所有的节点,并等待处理

5.接收到其它节点对 transaction_prepared 消息确认后,从 prepared 事务列表中移除该事务,并继续提交

对于 AFTER 模式,所有的节点在处理事务时,均需要发送一个 transaction_prepared 消息并等待所有节点的确认,之后,用户线程执行的事务才能成功提交。排除用户线程等待所有节点事务提交的时间开销,这些消息处理的网络开销也会对性能造成一定的影响。

另一个需要注意的是,如果在M2节点,事务T1还未进入prepared阶段,此时开启新的事务并不会阻塞。在DEBUG版本下,可以通过如下步骤进行验证。

connect 13000:
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 1ca5023b-0a1d-11ec-82f9-c8f7507e5048 | 127.0.0.1 | 13000 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | 1cab1e2b-0a1d-11ec-9eb9-c8f7507e5048 | 127.0.0.1 | 13004 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | 1caf096c-0a1d-11ec-a241-c8f7507e5048 | 127.0.0.1 | 13002 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | 1cbc3cf7-0a1d-11ec-955d-c8f7507e5048 | 127.0.0.1 | 13006 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | 1cc6f5eb-0a1d-11ec-8e81-c8f7507e5048 | 127.0.0.1 | 13008 | ONLINE | PRIMARY | 8.0.25 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
5 rows in set (0.03 sec) mysql> set session group_replication_consistency='AFTER';
Query OK, 0 rows affected (0.00 sec) mysql> create table t1 (c1 int primary key, c2 int);
Query OK, 0 rows affected (0.17 sec) mysql> insert into t1 values (1,1);
Query OK, 1 row affected (0.07 sec) kill -9 13008
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 1ca5023b-0a1d-11ec-82f9-c8f7507e5048 | 127.0.0.1 | 13000 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | 1cab1e2b-0a1d-11ec-9eb9-c8f7507e5048 | 127.0.0.1 | 13004 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | 1caf096c-0a1d-11ec-a241-c8f7507e5048 | 127.0.0.1 | 13002 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | 1cbc3cf7-0a1d-11ec-955d-c8f7507e5048 | 127.0.0.1 | 13006 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | 1cc6f5eb-0a1d-11ec-8e81-c8f7507e5048 | 127.0.0.1 | 13008 | UNREACHABLE | PRIMARY | 8.0.25 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
5 rows in set (0.00 sec) connect 13002,使用DEBUG_SYNC,控制节点不进入prepared阶段:
mysql> set global debug='+d,group_replication_before_commit_hook_wait';
Query OK, 0 rows affected (0.00 sec) connect 13000,插入新的事务,没有返回
mysql> insert into t1 values (2,2); connect 13002,可以发现,事务被 DEBUG_SYNC 阻塞:
mysql> select command, time, state, info from information_schema.processlist;
+---------+------+----------------------------+-----------------------------------------------------------------------+
| command | time | state | info |
+---------+------+----------------------------+-----------------------------------------------------------------------+
| Connect | 189 | waiting for handler commit | Group replication applier module |
| Query | 0 | executing | select command, time, state, info from information_schema.processlist |
| Sleep | 7 | | NULL |
| Daemon | 240 | Waiting on empty queue | NULL |
| Query | 64 | debug sync point: now | NULL |
+---------+------+----------------------------+-----------------------------------------------------------------------+
5 rows in set (0.01 sec) 此时,可以查询到数据:
mysql> set session group_replication_consistency='eventual';
Query OK, 0 rows affected (0.00 sec) mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 1 |
+----+------+
1 row in set (0.00 sec)

5、BEFORE 执行流程

与 AFTER 相对应的是 BEFORE,如果一个 session 开启了 BEFORE 级别,则在事务开启时,需要等待所有已经提交的事务已经在本地完成提交,这是通过 WAIT_FOR_GTID_SET 实现。在事务的开启时刻,获取到节点已经同步接收到的所有的事务 gtid 集合,并等待集合内所有的 gtid 完成提交,即可保证事务执行时,读取到最新的数据。

但是在获取 gtid 集合之前,节点需要通过 paxos 发送一个 SYNC_BEFORE_EXECUTION 类型的消息。由于 paxos 会对消息进行排队,因此,当 SYNC_BEFORE_EXECUTION 处理完成时,可以保证该消息发送之前的所有的事务消息均完成在 paxos 中的处理。由于该消息是本次事务开启时产生的,因而此时节点收到的 gtid 集合符合 BEFORE 级别。

如果节点不发送 SYNC_BEFORE_EXECUTION 消息,则 BEFORE 级别未必能够读取到最新数据。假设当前存在网络分区,总共三个节点A,B,C,网络分区后,A,B节点组成多数派,C节点为少数派,此时,A,B节点上新的写入事务将不会继续同步到C节点。在 C 节点被踢出集群之前,如果 C 开启了 BEFORE 级别,却未发送 SYNC_BEFORE_EXECUTION 消息,那么 C 中不能读取到新的数据,违背了 BEFORE 的设计宗旨。但是发送该消息后,由于无法达成消息一致性,那么新的事务将失败、或者一直等待消息返回,而不会返回用户过时的数据。

如下示例则显示了多数节点故障下,BEFORE 级别的执行行为。

开始阶段:
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | dd6398ec-09fe-11ec-95de-c8f7507e5048 | 127.0.0.1 | 13002 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | dd64424b-09fe-11ec-aeeb-c8f7507e5048 | 127.0.0.1 | 13000 | ONLINE | PRIMARY | 8.0.25 |
| group_replication_applier | dd65b9de-09fe-11ec-9d06-c8f7507e5048 | 127.0.0.1 | 13004 | ONLINE | PRIMARY | 8.0.25 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.06 sec) mysql> select @@group_replication_member_expel_timeout;
+------------------------------------------+
| @@group_replication_member_expel_timeout |
+------------------------------------------+
| 60 |
+------------------------------------------+
1 row in set (0.00 sec) 查询数据:
mysql> set session group_replication_consistency='BEFORE';
Query OK, 0 rows affected (0.00 sec) mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 2 | 2 |
+----+------+
1 row in set (0.01 sec) 使用kill -9模拟多数节点故障:
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | dd6398ec-09fe-11ec-95de-c8f7507e5048 | 127.0.0.1 | 13002 | UNREACHABLE | PRIMARY | 8.0.25 |
| group_replication_applier | dd64424b-09fe-11ec-aeeb-c8f7507e5048 | 127.0.0.1 | 13000 | UNREACHABLE | PRIMARY | 8.0.25 |
| group_replication_applier | dd65b9de-09fe-11ec-9d06-c8f7507e5048 | 127.0.0.1 | 13004 | ONLINE | PRIMARY | 8.0.25 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.00 sec) 此时,再次查询数据,一直未返回
mysql> select * from t1; 使用另一个客户端查看状态,一直处于 before hook 阶段:
mysql> select command, time, state, info from information_schema.processlist;
+---------+------+--------------------------------------------------------+-----------------------------------------------------------------------+
| command | time | state | info |
+---------+------+--------------------------------------------------------+-----------------------------------------------------------------------+
| Connect | 253 | waiting for handler commit | Group replication applier module |
| Daemon | 318 | Waiting on empty queue | NULL |
| Query | 238 | Slave has read all relay log; waiting for more updates | NULL |
| Query | 170 | Executing hook on transaction begin. | select * from t1 |
| Query | 0 | executing | select command, time, state, info from information_schema.processlist |
+---------+------+--------------------------------------------------------+-----------------------------------------------------------------------+
5 rows in set (0.00 sec)

当然,由于 BEFORE 级别下,额外发送了一次 SYNC_BEFORE_EXECUTION 消息,增加了一次网络开销,对性能是有一定的影响的。

6、一些思考

AFTER 模式下,当其它节点事务进入到 prepared 阶段,但用户线程并未完成提交,此时要求新的事务开启时必须等待事务提交是否必要 ?当前设计下,这一要求会造成一定程度上的节点不可用。

AFTER 模式需要等待所有的节点提交成功,这是一个强一致的数据同步方案,但同时会导致集群不可用。同时由于参数 group_replication_consistency 是一个 session 级的控制变量,即使某一个用户连接开启 AFTER 模式,都可能导致整个集群不可用。一个备

选方案是,采用开源的GreatSQL分支的 majority-after 模式(group_replication_majority_after_mode = ON),可以规避上述问题。

7、参考文档

MySQL 设计文档(https://dev.mysql.com/worklog/task/?id=10379)

MySQL 用户文档(https://dev.mysql.com/doc/refman/8.0/en/group-replication-configuring-consistency-guarantees.html)

Enjoy GreatSQL

文章推荐:

GreatSQL MGR FAQ

https://mp.weixin.qq.com/s/J6wkUpGXw3YkyEUJXiZ9xA

万答#12,MGR整个集群挂掉后,如何才能自动选主,不用手动干预

https://mp.weixin.qq.com/s/07o1poO44zwQIvaJNKEoPA

『2021数据技术嘉年华·ON LINE』:《MySQL高可用架构演进及实践》

https://mp.weixin.qq.com/s/u7k99y6i7riq7ScYs7ySnA

一条sql语句慢在哪之抓包分析

https://mp.weixin.qq.com/s/AYibbzl860D90rOeyjB6IQ

万答#15,都有哪些情况可能导致MGR服务无法启动

https://mp.weixin.qq.com/s/inSGpd0Q_XIl2Mb-VsvNsA

技术分享 | 为什么MGR一致性模式不推荐AFTER

https://mp.weixin.qq.com/s/rNeq479RNsklY1BlfKOsYg

关于 GreatSQL

GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。

Gitee:

https://gitee.com/GreatSQL/GreatSQL

GitHub:

https://github.com/GreatSQL/GreatSQL

Bilibili:

https://space.bilibili.com/1363850082/video

微信&QQ群:

可搜索添加GreatSQL社区助手微信好友,发送验证信息“加群”加入GreatSQL/MGR交流微信群

QQ群:533341697

微信小助手:wanlidbc

本文由博客一文多发平台 OpenWrite 发布!

技术分享 | 为什么MGR一致性模式不推荐AFTER的更多相关文章

  1. 技术分享 | 在GreatDB分布式部署模式中使用Chaos Mesh做混沌测试

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 1. 需求背景与万里安全数据库软件GreatDB分布式部署模式介绍 1.1 需求背景 混沌测试是检测分布式系统不确定性.建 ...

  2. 技术分享 | MySQL中MGR中SECONDARY节点磁盘满,导致mysqld进程被OOM Killed

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 在MGR测试中,人为制造磁盘满问题后,节点被oom killed 问题描述 在对 ...

  3. 技术分享 | 简单测试MySQL 8.0.26 vs GreatSQL 8.0.25的MGR稳定性表现

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. M ...

  4. 技术分享|闪回在MySQL中的实现和改进

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 3306π 福州站,以下内容是由万里数据库,研发工程师唐洁分享的MySQL闪回方案完整PPT. Enjoy GreatSQ ...

  5. 技术分享 | Prometheus+Grafana监控MySQL浅析

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 简介 Prometheus 一套开源的监控&报警&时间序列数据库的组合,通常 Kubernetes 中都会 ...

  6. 技术分享 | MySQL Group Replication集群对IP地址的限制导致的一些问题与解决办法

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 1. 遇到问题 测试人员小玲准备在docker环境中部署MGR集群进行一些测试,她有三个容器,容器IP分别是: 172.3 ...

  7. 技术分享 | MySQL数据误删除的总结

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 内容提要 用delete语句 使用drop.truncate删除表以及drop删 ...

  8. 技术分享 | 自制GreatSQL Docker镜像

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 近期打算制作一个GreatSQL的docker镜像,方便社区用户使用GreatSQL. 制作docker镜像的环境基于Ce ...

  9. 技术分享 | 测试git上2500星的闪回小工具

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 1.实验环境 2.软件下载 3.开始测试 4.附参数说明 生产上发生误删数据或者误更新数据的事故时,传统恢复方法是利用备份 ...

随机推荐

  1. 微信小程序使用echarts遇到的问题

    问题1:ec-canvas出现上下滑动页面会漂移 解决方法:在标签内加 force-use-old-canvas="true" 问题2:echarts的tooltip会超出边界 解 ...

  2. mac安装git、node

    1.需要先安装homebrew(之前的文章里有) 2.安装git brew install git 3.安装node brew install node 3.1.安装成功后,查看版本号 node -v ...

  3. JS:条件语句2

    1.for循环:循环代码块一定的次数 例: for(var i = 0;i<5;i++){ console.log(i); } // 0 1 2 3 4 遍历对象: var arr=[" ...

  4. .Net Core 企业微信更新模版卡片消息

    1.搭建回调服务器 可参考:https://www.cnblogs.com/zspwf/p/16381643.html进行搭建 2.编写代码 2.1接口定义 应用可以发送模板卡片消息,发送之后可再通过 ...

  5. WPF开发随笔收录-获取软件当前目录的坑

    一.唠唠叨叨 软件开发过程中,经常需要使用到获取exe当前目录这个功能,前同事在实现这个需求时使用的是Directory.GetCurrentDirectory()这个方法,但再最近的测试中,突然发现 ...

  6. 全新升级的AOP框架Dora.Interception[6]: 实现任意的拦截器注册方式

    Dora.Interception提供了两种拦截器注册方式,一种是利用标注在目标类型.属性和方法上的InterceptorAttribute特性,另一种采用基于目标方法或者属性的调用表达式.通过提供的 ...

  7. centos 7编译安装mysql 5.7.20

    1. 下载mysql 5.7.20源码包 wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.20.tar.gz 下载boost ...

  8. OpenLayers入门(一)

    OpenLayers简介 OpenLayers(https://openlayers.org/)是一个用来帮助开发Web地图应用的高性能的.功能丰富的JavaScript类库,可以满足几乎所有的地图开 ...

  9. idea显示 RunDashboard ,多个启动项时列表显示

    在.idea(项目所在文件夹中)下的workspace.xml文件中找到 <component name="RunDashboard"> 标签,然后添加如下节点 < ...

  10. Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现

    我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...