最近了解mysql MDL的设计。发现mysql "set global read_only=on/off"操作也依赖metadata lock(5.6.16, 推测是从5.5引入metadata lock后就这样了,没有查看更早版本的代码确认)。下面是set global read_only=on/off的实现。
调用路径如下:
#0 fix_read_only (self=0x134f5a0, thd=0x2470e0a0, type=OPT_GLOBAL) at /home/mysql-5.6.16/sql/sys_vars.cc:2219 #1 0x000000000064ab91 in sys_var::update (this=0x134f5a0, thd=0x2470e0a0, var=0x24691df0) at /home/mysql-5.6.16/sql/set_var.cc:194 #2 0x000000000064afad in set_var::update (this=0x24691df0, thd=0x2470e0a0) at /home/mysql-5.6.16/sql/set_var.cc:670 #3 0x000000000064a6c9 in sql_set_variables (thd=0x2470e0a0, var_list=<value optimized out>) at /home/mysql-5.6.16/sql/set_var.cc:573 #4 0x00000000006d779b in mysql_execute_command (thd=0x2470e0a0) at /home/mysql-5.6.16/sql/sql_parse.cc:3704 #5 0x00000000006dcb9b in mysql_parse (thd=0x2470e0a0, rawbuf=0x24691c90 "set global read_only=on", length=0, parser_state=<value optimized out>) at /home/mysql-5.6.16/sql/sql_parse.cc:6235 #6 0x00000000006de6b0 in dispatch_command (command=COM_QUERY, thd=0x2470e0a0, packet=0x246c7f61 "set global read_only=on", packet_length=23) at /home/mysql-5.6.16/sql/sql_parse.cc:1334 #7 0x00000000006df977 in do_command (thd=0x2470e0a0) at /home/mysql-5.6.16/sql/sql_parse.cc:1036 #8 0x00000000006a1f95 in do_handle_one_connection (thd_arg=0x2470e0a0) at /home/mysql-5.6.16/sql/sql_connect.cc:982
主要的实现都在fix_read_only函数内。主要分为2步:第一步初始化GRL 锁需要的第一个metadata lock -- 一个GLOBAL级别的显式的MDL_SHARED类型的metadata lock,第一步完成之后GRL的状态是GRL_ACQUIRED,并且这一步之后其他连接是无法拿到写锁的(新进来的写请求会被拒绝);第二步获取一个COMMIT级别的显式的MDL_SHARED类型的metadata lock,并将GRL的状态修改为GRL_ACQUIRED_AND_BLOCKS_COMMIT,这一步之后所有的事务都不再允许在提交了(所以执行set global read_only操作时如果有大事务正在执行,set global read_only操作是会被卡住的)。
fix_read_only(),sys_vars.cc:2219 --my_bool new_read_only= read_only; --read_only= opt_readonly; ... --lock_global_read_lock(),lock.cc:966 (enum_grl_state m_state = Global_read_lock::GRL_ACQUIRED_AND_BLOCKS_COMMIT -> m_state = GRL_ACQUIRED) --mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT);# 初始化一个namespace=GLOBAL的显式的MDL_SHARED的metadata lock, dbname 和 name为“” --thd->mdl_context.acquire_lock(&mdl_request,thd->variables.lock_wait_timeout) # 获取刚刚初始化过的metadata lock --make_global_read_lock_block_commit(),lock.cc:1041 (m_state = GRL_ACQUIRED -> m_state = GRL_ACQUIRED_AND_BLOCKS_COMMIT) --mdl_request.init(MDL_key::COMMIT, "", "", MDL_SHARED, MDL_EXPLICIT); --thd->mdl_context.acquire_lock(&mdl_request,thd->variables.lock_wait_timeout)) ... --opt_readonly= new_read_only; --read_only= opt_readonly;
细看这段逻辑,其实 set global read_only=on/off最主要的操作还是要把全局变量opt_readonly设置为on/off。之所以有上面这段fix_read_only的逻辑,我觉得是要考虑如何处理处于提交阶段的事务:第一步可以阻止新的写请求,第二步阻止事务提交。
Global_read_lock也很容易理解:
Global_read_lock主要有下面几个函数: lock_global_read_lock() unlock_global_read_lock() make_global_read_lock_block_commit() Global_read_lock主要有下面几个变量: /** In order to acquire the global read lock, the connection must acquire shared metadata lock in GLOBAL namespace, to prohibit all DDL. */ MDL_ticket *m_mdl_global_shared_lock; /** Also in order to acquire the global read lock, the connection must acquire a shared metadata lock in COMMIT namespace, to prohibit commits. */ MDL_ticket *m_mdl_blocks_commits_lock; enum_grl_state m_state;
前面说了set global read_only=on/off最重要的还是设置全局变量opt_readonly。那么只读是如何生效的呢?mysql在执行写操作前/commit的时候会去判断read_only状态(变量opt_readponly),如果read_only=on && 当前用户没有超级权限,就会报1290错误(ER_OPTION_PREVENTS_STATEMENT)--“ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement”
bool trans_begin(THD *thd, uint flags) //transaction.cc,事务开始start transaction/begin { ... if (opt_readonly && !user_is_super) { my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); DBUG_RETURN(true); } ... int ha_commit_trans(THD *thd, bool all, bool ignore_global_read_lock) //事务commit的时候 ... if (rw_trans && opt_readonly && !(thd->security_ctx->master_access & SUPER_ACL) && !thd->slave_thread) { my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); ha_rollback_trans(thd, all); ...
PS:
set global read_only=on/off是DBA经常用的一个操作:进行主备切换的时候,一般都会先对主库进行只读操作(on),然后主备同步完成后,再把备库置为可读写(off)。这样可以避免切换的过程中双写引起脏数据。
mysqld.cc中同时定义了2个变量:my_bool read_only= 0, opt_readonly= 0; opt_readonly是当前系统的read_only状态,read_only是要把read_only设置成的值。
相关推荐
- 修改`read_only`状态:若需要恢复写入功能,可以使用`SET GLOBAL read_only=0;`命令临时关闭只读模式。 - 查看错误日志:通过阅读MySQL的错误日志(通常在`/var/log/mysql/error.log`)来确定服务器重启的原因。...
如果其值为1,可以通过`SET GLOBAL read_only=0;`命令临时关闭只读模式,以解决紧急问题。 然而,服务器为何会自动将`read_only`设置为1,这通常与服务器的异常行为有关。在本例中,MySQL因内存溢出被系统强制重启...
mysql> SET GLOBAL read_only=0; ``` 然后刷新权限以应用更改: ``` mysql> FLUSH PRIVILEGES; ``` 接着,如果你遇到了因为binlog_format限制不能执行某些语句的问题,如存储引擎只支持行记录格式,你需要将...
- 首先,你可能需要取消新主库的只读属性,通过执行`mysql> SET GLOBAL read_only=0;`。 - 然后,刷新权限以应用更改:`mysql> FLUSH PRIVILEGES;`。 - 如果你遇到错误,提示“不可能写入二进制日志,因为BINLOG_...
mysql> SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; mysql> START SLAVE; ``` ### 永久跳过错误 如果经常遇到特定类型的错误,可以在`my.cnf`配置文件中添加`slave-skip-errors`选项,指定要忽略的错误编号。例如,要...
特别注意的是,必须将 `innodb_fast_shutdown` 参数设置为 0,以确保在关闭过程中不会跳过任何重要的清理工作,这可以通过执行 `mysql> set global innodb_fast_shutdown=0;` 来实现。 - **停止当前服务**:通过...
1. 动态调整:在MySQL命令行中输入`set global thread_cache_size=16`,即可临时修改此参数。 2. 配置文件修改:在`/etc/my.cnf`(或其他MySQL配置文件)中添加或修改`thread_cache_size`的值,如`thread_cache_size...
set global read_only=1; 这样,从数据库就只能读取数据,不能写入数据。 注意 在从数据库中,root 用户需要同步主数据库中的数据,所以不能将 root 用户设置为只读模式,否则同步不会成功。可以新建一个普通用户...
错误提示: user: ‘root’ host: `localhost’ (Got timeout reading communication packets) MYSQL server has gone away 引起这个原因是不可怕的.原因是更改了系统的断开时间. ...mysql> set global interactiv
- `set global read_only=1;`:设置从服务器为只读模式,防止在故障切换期间写入数据。 综上,MHA是一个强大的工具,它通过自动化故障检测和处理,降低了MySQL集群的停机时间,提升了系统的整体可用性。结合半同步...
SET GLOBAL server_id = 2; ``` 2. **开始复制**:使用在主服务器上获取的信息设置复制: ``` CHANGE MASTER TO MASTER_HOST='主服务器IP', MASTER_USER='replication', MASTER_PASSWORD='your_password', ...
- `SET GLOBAL sort_buffer_size = <value>` - 修改全局排序缓冲区大小,影响所有新创建的会话。 - `SET @@sort_buffer_size := <value>` - 更改当前会话的变量值。 - `SET @@session.sort_buffer_size := <value>` ...
首先,当我们尝试通过`SET GLOBAL`命令来更改一个参数,如`log_slave_updates`,可能会遇到“read only variable”的错误。例如: ```sql mysql> show variables like 'log_slave_updates'; +--------------------+...
$MYSQL_CMD -e "SET GLOBAL read_only = 0;" echo "Slave promoted to master." fi ``` #### 负载均衡 ##### 代理层 代理层可以实现负载均衡功能,将请求分发给多个数据库节点,以达到均衡负载的目的。 - **...
SET GLOBAL character_set_server = 'utf8'; ``` 综上所述,解决C#操作MySQL时的中文乱码问题需要从数据库连接、数据表创建、代码处理、文件编码等多个方面综合考虑。仔细检查并调整上述环节,通常能有效避免乱码...
- `Isolation.READ_COMMITTED`:防止脏读,但允许不可重复读和幻读。 - `Isolation.REPEATABLE_READ`:防止脏读和不可重复读,但允许幻读。 - `Isolation.SERIALIZABLE`:最严格的隔离级别,防止脏读、不可重复读和...
- 使用SQL命令设置新的最大连接数:`set GLOBAL max_connections=200` - 检查当前状态:`show processlist` 和 `show status` - 退出客户端:`exit` 3. **方法三**:修改源代码重新编译 - 解压MySQL源代码,...
`,其中`handler_read_key`值高表示使用索引查询较多,而`handler_read_rnd_next`值高则表明查询效率低。 #### 九、其他SQL优化技巧 - **ORDER BY NULL**:使用`ORDER BY NULL`禁用不必要的排序。 - **使用JOIN...
mysqlbinlog --start-position=<前一个有效位置> --stop-position=<Read_Master_Log_Pos> ./mysql-bin.000342 ``` 3. **确定新的同步位置**:根据分析结果,确定一个新的位置,确保数据的一致性。 4. **配置并启动...
- **Read_Master_Log_Pos**:已经读取的日志文件位置。 - **Relay_Master_Log_File**:从服务器上的中继日志文件名称。 - **Slave_IO_Running**:I/O线程是否正在运行。 - **Slave_SQL_Running**:SQL线程是否...