最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

MySQL MEMORY(HEAP)存储引擎导致Slave节点有本地事务

XAMPP案例 中文小张 839浏览 0评论

1. MEMORY引擎简介

可能有的朋友对MEMORY存储引擎不太了解,首先介绍一下(以下描述来自官方):

  1. MEMROY存储引擎(以前称为HEAP)的表把表结构存放到磁盘上,而把数据放在内存中。
  2. 每个Memory表只实际对应一个磁盘文件,在磁盘中表现为.frm文件。因为它的数据是放在内存中的,并且默认使用hash索引(也支持B-Tree索引),因此Memory类型的表访问速度非常快(比使用B-Tree索引的MyISAM表快),但是一旦服务关闭,表中的数据就会丢失
  3. 由于MEMRORY表在mysqld重启后数据会丢失,为了获得稳定的数据源,可以在启动mysqld时添加--init-file选项,把类似insert into ... selectload data的语句放进去。
  4. MEMROY存储引擎的典型适用场景包含如下特征:
    a)涉及瞬态非关键数据的操作,如会话管理或缓存。
    b)数据可以完全放入内存而不会导致操作系统交换虚拟内存页,并且要求快速访问。
    c)只读或以读为主的数据访问模式(有限的更新)。
  5. 关于性能:
    a)在处理更新时,单线程执行和表锁开销导致的争用会限制MEMORY性能。
    b)尽管MEMORY表在内存中进行处理,但是对于繁忙的服务器、通用查询或读/写工作负载,它们并不一定比InnoDB表快。特别是,执行更新所涉及的表锁定会降低多个会话中内存表的并发使用速度。
  6. MEMORY表具有以下特征:
    a)MEMORY表的空间以小块形式分配。表对插入使用100%动态哈希,不需要占用额外的内存。
    b)被删除的行并未释放,而是放在链表中,并在插入新数据时重用。
    c)MEMORY表使用固定长度的行存储数据。(即使是VARCHAR也不例外)
    d)MEMORY表不支持 BLOB、TEXT 列。
    e)MEMORY表支持 AUTO_INCREMENT 列。
  7. MEMORY表是有大小限制的,主要受限于两个参数:max_heap_table_size 和 MAX_ROWS(默认情况下MAX_ROWS依赖于max_heap_table_size,可执行ALTER TABLE tbl_name MAX_ROWS= MAX_ROWS修改MAX_ROWS)。

问:MEMORY表和临时表有什么区别?

  1. 临时表默认使用的存储引擎是服务器指定的存储引擎(对于5.7是InnoDB),由于临时表定义和数据都放在内存中,未放到磁盘,因此用show tables招不到临时表。
  2. 如果临时表占用空间太大,MySQL会将其转为磁盘存储。而对于用户创建的MEMORY表,则不会转为磁盘存储。

mysql> create temporary table temp_t1(a int primary key, b int);

Query OK, 0 rows affected (0.00 sec)

 

mysql> show tables;

+—————+

| Tables_in_db4 |

+—————+

| t1 |

+—————+

1 row in set (0.00 sec)

2. 故障分析

现象:
最近碰到有用户使用MEMORY存储引擎,引发主从GTID不一致、从节点GTID比主节点多一条的情况。

分析:

  1. 检查日志,确认没有发生过主从切换,也就排除了主节点有prepare的事务然后故障(从节点变为主)、重启导致local commit的情况。
  2. 在从节点binlog中找到那条本地事务,发现是MEMORY表的DELETE FROM
  3. 该从节点发生过重启,根据MEMORY引擎的特性,确认是MEMORY表生成的。

 

向用户反馈问题原因后,用户将MEMORY表改为了InnoDB表。

3. 疑问

3.1. 何时生成DELETE FROM

A server’s MEMORY tables become empty when it is shut down and restarted. If the server is a replication master, its slaves are not aware that these tables have become empty, so you see out-of-date content if you select data from the tables on the slaves. To synchronize master and slave MEMORY tables, when a MEMORY table is used on a master for the first time since it was started, a DELETE statement is written to the master’s binary log, to empty the table on the slaves also. The slave still has outdated data in the table during the interval between the master’s restart and its first use of the table. To avoid this interval when a direct query to the slave could return stale data, use the --init-file option to populate the MEMORY table on the master at startup.

这段描述的含义是:

  1. 服务器的MEMORY表在关闭和重新启动时会变为空。
  2. 为了防止主服务器重启、从服务器未重启导致从服务器上有过期的MEMORY表数据,会在重启服务器时向binlog写入一条DELETE FROM语句,这条语句会复制到从节点,以达到主从数据一致的目的。

3.2. 对于主从复制的MySQL集群,主或从故障重启有什么问题?

PS:不想看过程的朋友,请跳到最后看总结。

举例来说,集群有三个节点A、B、C,节点A为主节点。

情形一:MEMORY表有数据的情况下,重启主节点、触发主从切换:

  1. 创建MEMORY表 mdb.t1 ,执行insert into mdb.t1 values(1,1),(2,2),(3,3),(4,4)插入一些数据。
  2. 关闭节点A的MySQL,节点B变为主,之后节点A以从节点启动,此时:

    a)节点A无数据:

    mysql> select * from mdb.t1;

    Empty set (0.00 sec)

    b)节点B、C有数据:

    mysql> select * from mdb.t1;

    +——+——+

    | a | b |

    +——+——+

    | 1 | 1 |

    | 2 | 2 |

    | 3 | 3 |

    | 4 | 4 |

    +——+——+

    4 rows in set (0.00 sec)

    并且,节点A的GTID为uuid_a:1-11,节点B、C的GTID为uuid_a:1-10,节点A的binlog比另外两个节点多一条DELTE FROM mdb.t1

情形二:MEMORY表无数据的情况下,重启主节点、触发主从切换:

  1. 将节点A切换为主节点,节点B、C同步了uuid_a:1-11这条事务,三个节点的mdb.t1数据为空。
  2. 关闭节点A的MySQL,节点B变为主,之后节点A以从节点启动,此时,节点A生成了一条本地DELETE FROM事务uuid_b:1-12

情形三:MEMORY表无数据的情况下,重启从节点:

  1. 将节点A切换为主节点,节点B、C同步了uuid_a:1-12这条事务
  2. 重启节点A的MySQL,节点A生成一条本地DELETE FROM事务uuid_a:1-13

情形四:MEMORY表有数据的情况下,重启从节点:

  1. 将节点A切换为主节点,另外两个节点同步节点A的本地事务,三个节点GTID为uuid_a:1-13
  2. 执行INSERT语句向mdb.t1插入一些数据,三个节点GTID为uuid_a:1-14
  3. 重启节点B,其生成了一条本地DELETE FROM事务uuid_b:1

3.3. 总结

  1. 测试发现,无论什么情况下,MEMORY存储引擎都会生成一条本地DELETE FROM事务。
  2. 在某些情况下,必须主动访问(比如SELECT)MEMORY表,才会触发生成DELETE FROM
  3. 最重要的一点,在生产环境中千万不要使用MEMORY存储引擎

 

转载请注明:XAMPP中文组官网 » MySQL MEMORY(HEAP)存储引擎导致Slave节点有本地事务

您必须 登录 才能发表评论!