Golang进阶,揉碎数据库中间件,干货满满!

必读

欢迎关注白日梦的公众号(风一样的程序员)回复:数据库中间件

即可获取写有注释的中间件源码包,开箱即用~

一、Centos7、Mac安装MySQL

笔记地址:https://www.cnblogs.com/ZhuChangwu/p/12984153.html

视频串讲地址:https://www.bilibili.com/video/BV19g411N7NR?p=2

二、主从复制原理

2.1、基于binlog_filename + position

原理图:

笔记地址:https://mp.weixin.qq.com/s/cSToNVQPK8QCpkjapxNoEw

视频串讲地址:https://www.bilibili.com/video/BV19g411N7NR?p=3

2.2、基于GTID

原理图:

笔记地址:https://mp.weixin.qq.com/s/V5hU2ATeey871loWQIqHKg

视频串讲地址:https://www.bilibili.com/video/BV19g411N7NR?p=4

三、my.cnf

[mysqld]

# 端口
port = 3306

# 数据目录
datadir=/var/lib/mysql

# 错误日志
log-error=/var/log/mysqld.log

# 为每张表单独创建一个表空间文件
# 大家常说的表空间到底是什么?究竟什么又是数据表呢? https://mp.weixin.qq.com/s/CwxRjGI843UerF89G_WJ-Q
innodb_file_per_table=on
innodb_file_format = Barracuda

# binlog 相关配置
# 1、MySQL的 bin log有啥用?在哪里?谁写的?怎么配置?
# https://mp.weixin.qq.com/s/DN1shuyxPJ6BkE_RLezAnA

# 2、了解bin log的写入机制吗?说说你们线上如何调整参数的!
# https://mp.weixin.qq.com/s/MtWzoiJtupso5M8z1KUaQQ

# 3、bin log有哪些格式?有啥区别?优缺点?线上用哪种格式?
# https://mp.weixin.qq.com/s/ar-wVbDi4CYjPI1t6fTjVw
log_bin=mysql-bin
log-bin-index = mysql-bin.index
max_binlog_size = 256M
sync-binlog = 1000
binlog-format = ROW

# relaylog相关配置
relay_log_recovery = 1
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay-log = relay-log
relay-log-index = relay-log.index
sync_relay_log = 1000
max_relay_log_size = 256M

# 设置server-id,集群唯一
server-id=1

# pid、socket
pid-file=/var/run/mysqld/mysqld.pid
socket=/var/lib/mysql/mysql.sock
symbolic-links=0

四、测试SQL

create database test;
 
use test;

CREATE TABLE `runoob_tbl` (
  `runoob_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `runoob_title` varchar(100) NOT NULL,
  `runoob_author` varchar(40) NOT NULL,
  `submission_date` date DEFAULT NULL,
  PRIMARY KEY (`runoob_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

 INSERT INTO runoob_tbl  (runoob_title, runoob_author, submission_date)
 VALUES("欢迎微信搜索:", "风一样的程序员", NOW());
CHANGE MASTER TO
    MASTER_HOST='10.4.7.103',
    MASTER_USER='MySQLsync',
    MASTER_PASSWORD='MySQLsync123',
    MASTER_PORT=3306,
    MASTER_AUTO_POSITION = 1;
      
CHANGE MASTER TO
    MASTER_HOST='10.4.7.103',
    MASTER_USER='mysqlsync',
    MASTER_PASSWORD='mysqlsync123',
    MASTER_PORT=3306,
    MASTER_LOG_FILE='mysql-bin.000001',
    MASTER_LOG_POS=434;
    
    CHANGE MASTER TO MASTER_AUTO_POSITION=0; 
 grant replication slave on *.* to MySQLsync@"%" identified by "MySQLsync123";
 
 grant replication slave on *.* to mysqlsync@"127.0.0.1" identified by "mysqlsync123";
 
 grant replication slave on *.* to mysqlsync@"%" identified by "mysqlsync123";

五、中间件使用、概念串讲

脑图:

视频地址:https://www.bilibili.com/video/BV19g411N7NR?p=5

搞明白这几点:

  1. 跟大家讲明白这样一件事,你以为你是直连的MySQL?其实不是的,你直连的MySQL_Proxy
  2. Node、分片的概念
  3. Proxy配置文件的解读
  4. 演示Proxy的使用

六、总揽启动流程

  • 基于mysql协议获取主从库的连接
  • 将主从库的连接维护进连接池
  • 探活机制

视频地址:https://www.bilibili.com/video/BV19g411N7NR?p=6

Notice:跟大家讲明白这样一件事,你以为你是直连的MySQL?其实不是的,你直连的MySQL_Proxy!

七、权限管理实现原理

重点关注的地方:

# server.go:381

// todo 用户白名单校验,只有指定的user、ip才能使用Proxy
if allowConnect := conn.IsAllowConnect(); allowConnect == false {
		err := mysql.NewError(mysql.ER_ACCESS_DENIED_ERROR, "ip address access denied by kingshard.")
		conn.writeError(err)
		conn.Close()
		return
	}

视频地址:https://www.bilibili.com/video/BV19g411N7NR?p=7

八、MySQL协议-Handshake!和中间件握手机制原理

原理图:

重点关注的地方:

server.go:388

  // todo 基于MySQL协议和客户端建立握手机制
  if err := conn.Handshake(); err != nil {
  	golog.Error("server", "onConn", err.Error(), 0)
 	 	conn.writeError(err)
 	 	conn.Close()
 	  return
  }

backend_conn.go:101



	// todo 这里其实Proxy和MySQL Server之间建立连接的逻辑
	// todo 大家看到这里不明白也没关系,因为想看懂这里需要了解MySQL协议,后面的视频中我会跟大家讲明白这件事
	// todo 大家只需要知道,执行过这里的代码之后呢,proxy和MySQL-Service之间就会建立一个Conn,
	// todo 并且Proxy会维护这个Conn,后续用户的SQL打过来之后,Proxy会将用户的SQL转发给这里获取到的Conn,进尔让MySQL的引擎去真正的执行这里的SQL

	// todo 读MySQL发过来的握手报文
	if err := c.readInitialHandshake(); err != nil {
		c.conn.Close()
		return err
	}
	// todo 写自己的信息(port、username、password、host)
	if err := c.writeAuthHandshake(); err != nil {
		c.conn.Close()

		return err
	}
	// todo 读取MySQL-Server发过来的ok报文
	if _, err := c.readOK(); err != nil {
		c.conn.Close()

		return err
	}

视频地址:https://www.bilibili.com/video/BV19g411N7NR?p=8

九、中间件不断接受处理客户端SQL原理

重点关注的地方:

conn.go:279

// todo 下面的代码在一个无限循环中, 不断的接受客户端发送过来的sql语句
	for {
		// todo 根据MySQL协议解析数据包,获取出数据包中sql语句
		data, err := c.readPacket()

		if err != nil {
			return
		}

		if c.configVer != c.proxy.configVer {
			err := c.reloadConfig()
			if nil != err {
				golog.Error("ClientConn", "Run",
					err.Error(), c.connectionId,
				)
				c.writeError(err)
				return
			}
			c.configVer = c.proxy.configVer
			golog.Debug("ClientConn", "Run",
				fmt.Sprintf("config reload ok, ver:%d", c.configVer), c.connectionId,
			)
		}
		// 使用dispatch方法,继续处理数据包
			if err := c.dispatch(data); err != nil {
			c.proxy.counter.IncrErrLogTotal()
			golog.Error("ClientConn", "Run",
				err.Error(), c.connectionId,
			)
			c.writeError(err)
			if err == mysql.ErrBadConn {
				c.Close()
			}
		}

		if c.closed {
			return
		}

		c.pkg.Sequence = 0
	}
}

视频地址:https://www.bilibili.com/video/BV19g411N7NR?p=9

十、中间件是如何执行你的select语句的?

重点关注的地方:

conn.go:279

	// 使用dispatch方法,继续处理数据包
			if err := c.dispatch(data); err != nil {
			c.proxy.counter.IncrErrLogTotal()
			golog.Error("ClientConn", "Run",
				err.Error(), c.connectionId,
			)
			c.writeError(err)
			if err == mysql.ErrBadConn {
				c.Close()
			}
		}

conn.go:340

func (c *ClientConn) dispatch(data []byte) error {
	c.proxy.counter.IncrClientQPS()
	// todo MYSQL协议规定了,客户端发送过来的数据格式是:cmd+data
	// todo 其中的cmd就是sql的类型,类型都枚举在下面了,打眼一看都能懂
	cmd := data[0]
	// todo data部分就是sql详细内容
	data = data[1:]

	switch cmd {
	case mysql.COM_QUIT:
		c.handleRollback()
		c.Close()
		return nil
	case mysql.COM_QUERY: // todo select 语句
		return c.handleQuery(hack.String(data))
	case mysql.COM_PING: // todo ping 语句
		return c.writeOK(nil)
	case mysql.COM_INIT_DB:
		return c.handleUseDB(hack.String(data))
	case mysql.COM_FIELD_LIST:
		return c.handleFieldList(data)
	case mysql.COM_STMT_PREPARE:
		return c.handleStmtPrepare(hack.String(data))
	case mysql.COM_STMT_EXECUTE:// todo insert、update 语句
		return c.handleStmtExecute(data)
	case mysql.COM_STMT_CLOSE:
		return c.handleStmtClose(data)
	case mysql.COM_STMT_SEND_LONG_DATA:
		return c.handleStmtSendLongData(data)
	case mysql.COM_STMT_RESET:
		return c.handleStmtReset(data)
	case mysql.COM_SET_OPTION:
		return c.writeEOF(0)
	default:
		msg := fmt.Sprintf("command %d not supported now", cmd)
		golog.Error("ClientConn", "dispatch", msg, 0)
		return mysql.NewError(mysql.ER_UNKNOWN_ERROR, msg)
	}

	return nil
}

视频地址:https://www.bilibili.com/video/BV19g411N7NR?p=10

十一、读写分离实现原理

重点关注的地方:

conn_pershard.go:97

	// todo 从选出DB中获取一条可用的连接,如果是没有开事物且是读请求的话,executeDB.IsSlave一般为true
	conn, err := c.getBackendConn(executeDB.ExecNode, executeDB.IsSlave)
	defer c.closeConn(conn, false)
	if err != nil {
		return false, err
	}

视频地址:https://www.bilibili.com/video/BV19g411N7NR?p=11

十二、赠送Proxy源码

写有注释的项目已上传置百度网盘,项目基于vendor管理依赖包,开箱即用~

关注白日梦的公众号(风一样的程序员),回复:数据库中间件 ,即可领取当前文档以及源码包。

posted @ 2021-11-28 14:54  赐我白日梦  阅读(1414)  评论(0编辑  收藏  举报