🌈欢迎来到MySQL专栏
- 🌍博客主页 :张小姐的猫~江湖背景
- 🔥所属专栏 :MySQL实战
- 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏

MySQL
- 🌈欢迎来到MySQL专栏
- 🌏链接上数据库
- 🌏MySQL接口介绍
-
- [🔥C++ 查询处理细节](#🔥C++ 查询处理细节)
- 🌏连接池原理
- 🌏简易网站数据流动如何进行?
- 📢写在最后

🌏链接上数据库
cpp
#include <iostream>
#include <mysql/mysql.h>
int main()
{
std::cout << "mysql client version: " << mysql_get_client_info() << std::endl;
return 0;
}
cpp
g++ test.cc -o test -L/lib64/mysql -lmysqlclient;
其中 include 包含所有的方法声明 , lib 包含所有的方法实现(打包成库)
lmysqlclient:告诉要链接哪个库- -
L/lib64/mysql:告诉去哪里能找到这个库 - 头文件,在
include中能搜索的到:mysql/mysql.h;如果不加就要-I去手动链接
cpp
./test
mysql client version: 5.7.44
🌏MySQL接口介绍
要使用库,必须先进行初始化!
初始化:mysql_init(),最后不用记得关闭mysql_close()
cpp
MYSQL *my = mysql_init(nullptr);
初始化完毕之后,必须先链接数据库,在进行后续操作。(mysql网络部分是基于TCP/IP的)
cpp
MYSQL *mysql_real_connect(MYSQL *mysql,
const char *host,
const char *user,
const char *passwd,
const char *db,
unsigned int port,
const char *unix_socket,
unsigned long clientflag
);
cpp
#include <iostream>
#include <string>
#include <mysql/mysql.h>
const std::string host = "127.0.0.1";
const std::string user = "connector";
const std::string password = "Ljj@747501";
const std::string db = "conn";
const unsigned int port = 3306;
int main()
{
MYSQL *my = mysql_init(nullptr);
if(my == nullptr)
{
std::cerr << "init MySQL error" << std::endl;
return 1;
}
if(mysql_real_connect(my, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
std::cerr << "Connect MySQL error" << std::endl;
return 2;
}
std::cout << "Connect MySQL success" << std::endl;
mysql_close(my);
return 0;
}
链接成功~
在conn库中创建出一个表
sql
mysql> create table user(
-> id bigint primary key auto_increment,
-> name varchar(20) not null,
-> age int not null,
-> telphone varchar(32) unique
-> );
紧接着下发mysql命令mysql_query
sql
int mysql_query(MYSQL *mysql, const char *q);
- 第一个参数是初始化后的数据库名
- 第二个参数为要执行的
sql语句
cpp
std::string sql = "update user set name='sheriy' where id = 1";
int n = mysql_query(my, sql.c_str());
if(n == 0) std::cout << sql << "success" << n << std::endl;
else std::cout << sql << "failed" << n << std::endl;
sql
mysql> select * from user;
+----+------+-----+----------+
| id | name | age | telphone |
+----+------+-----+----------+
| 1 | tom | 18 | 123456 |
+----+------+-----+----------+
⬇️
mysql> select * from user;
+----+--------+-----+----------+
| id | name | age | telphone |
+----+--------+-----+----------+
| 1 | sheriy | 18 | 123456 |
+----+--------+-----+----------+
那么如果我们插入中文字段呢? 会出现乱码问题!
cpp
std::string sql = insert into user (name, age, telphone) values ('张三', 18, '123456');
sql
mysql> select * from user;
+----+---------------+-----+----------+
| id | name | age | telphone |
+----+---------------+-----+----------+
| 1 | sheriy | 18 | 123456 |
| 2 | å¼ ä¸‰ | 18 | 12342256 |
+----+---------------+-----+----------+
建立好链接之后,获取英文没有问题,如果获取中文是乱码
- 设置链接的默认字符集是
utf8,原始默认是latin1
cpp
mysql_set_character_set(myfd, "utf8");
设置完后就能正常显示了!
sql
mysql> select * from user;
+----+---------------+-----+--------------+
| id | name | age | telphone |
+----+---------------+-----+--------------+
| 1 | sheriy | 18 | 123456 |
| 2 | å¼ ä¸‰ | 18 | 12342256 |
| 4 | 张三 | 18 | 123427878256 |
+----+---------------+-----+--------------+
🔥C++ 查询处理细节
当我们进行查询后,我们来看看如何获取查询结果?实际上执行结果会保存在my这个句柄里的(包括了保留数据的缓冲区)
如果mysql_query返回成功,那么我们就通过mysql_store_result这个函数来读取结果
原型如下:
cpp
MYSQL_RES *mysql_store_result(MYSQL *mysql);
该函数会调用MYSQL变量中的st_mysql_methods中的 read_rows 函数指针来获取查询的结果。同时该函数会返回MYSQL_RES 这样一个变量,该变量主要用于保存查询的结果 。同时该函数malloc了一片内存空间来存储查询过来的数据,所以我们一定要记的 free(result),不然是肯定会造成内存泄漏的 。 执行完mysql_store_result以后,其实数据都已经在MYSQL_RES 变量中了
cpp
mysql_free_result(res);
把结果转储到 MYSQL_RES 结构体中
sql
MYSQL_RES * res = mysql_store_result(my);
if(nullptr == res)
{
std::cout << sql << "mysql_store_result failed!" << n << std::endl;
return 4;
}
首先理解一下表结构,其中竖列是表表的属性,其他就是表的内容

我们的数据不已经放在了my结构体里了吗?为什么还需要进行转储呢?
- 数据在
my结构体里:只说明它在当前进程/当前运行时的内存里有一份表示,不等于它已经被安全保存或可被外部使用 。是否需要转储(dump/序列化/持久化)取决于你的目的
首先MySQL将所有的数据,读取出来的时候,全部都当作字符串看待

下面的api基本就是读取MYSQL_RES 中的数据
获取结果行数:
c
my_ulonglong mysql_num_rows(MYSQL_RES *res);
获取结果列数:
sql
unsigned int mysql_num_fields(MYSQL_RES *res);
sql
select * from usersuccess0
行:2
列:4
那如果想要获取结果内容呢?mysql_fetch_row
- 它会返回一个变量,
MYSQL_ROW其实就是char **,通过确定访问哪一行,再进而确定哪一列!
cpp
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
cpp
for(int i = 0; i < rows; i++)
{
//类似于迭代器! 逐行取出
MYSQL_ROW row = mysql_fetch_row(res);
for(int j = 0; j < fields; j++)
{
std::cout << row[j] << "\t"; //遍历行的每一个
}
std::cout << "\n";
}

获取列名 :mysql_fetch_fields
cpp
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);
cpp
MYSQL_FIELD *fields_array = mysql_fetch_fields(res);
for(int i = 0; i < fields; i++)
{
std::cout << fields_array[i].name << "\t";
}
std::cout << "\n";
//✅️
id name age telphone
1 sheriy 18 123456
4 张三 18 123427878256
最终版代码
cpp
#include <iostream>
#include <string>
#include <mysql/mysql.h>
const std::string host = "127.0.0.1";
const std::string user = "connector";
const std::string password = "Ljj@747501";
const std::string db = "conn";
const unsigned int port = 3306;
int main()
{
MYSQL *my = mysql_init(nullptr);
if(my == nullptr)
{
std::cerr << "init MySQL error" << std::endl;
return 1;
}
if(mysql_real_connect(my, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
std::cerr << "Connect MySQL error" << std::endl;
return 2;
}
//设置链接的默认字符集是utf8
mysql_set_character_set(my, "utf8");
std::string sql = "select * from user";
int n = mysql_query(my, sql.c_str());
if(n == 0) std::cout << sql << "success" << n << std::endl;
else
{
std::cout << sql << "failed" << n << std::endl;
return 3;
}
MYSQL_RES * res = mysql_store_result(my);
if(nullptr == res)
{
std::cout << sql << "mysql_store_result failed!" << n << std::endl;
return 4;
}
//全部都是和结果集res有关!
int rows = mysql_num_rows(res); //获取行数
int fields = mysql_num_fields(res); //获取列数
std::cout << "行:" << rows << std::endl;
std::cout << "列:" << fields << std::endl;
//属性
MYSQL_FIELD *fields_array = mysql_fetch_fields(res);
for(int i = 0; i < fields; i++)
{
std::cout << fields_array[i].name << "\t";
}
std::cout << "\n";
//内容
for(int i = 0; i < rows; i++)
{
//类似于迭代器! 逐行取出
MYSQL_ROW row = mysql_fetch_row(res);
for(int j = 0; j < fields; j++)
{
std::cout << row[j] << "\t"; //遍历行的每一个
}
std::cout << "\n";
}
mysql_free_result(res);
mysql_close(my);
return 0;
}
🌏连接池原理
在创建一批线程的同时 + 创建出MySQL对象:也就是每个线程分配一个MySQL对象
- 让多个线程预先和
MySQL先建立好链接 - 后续有任务到来,就push到连接池里
- 在进入while死循环之前,先让MySQL先链接上 ------ 再进入到对应的事件循环中
- 那么每个线程都有自己的MySQL的句柄,并且是链接好的

cpp
class task
{
private:
std::string sql; // 要执行的SQL语句
std::function<void(Result)> cb; // 回调函数
};
-
sql:告诉线程池需要执行什么SQL
-
cb:告诉线程池SQL执行完毕后,应该如何处理结果
线程池创建工作线程时,就为每个工作线程创建一个独立的 MySQL 对象,并提前调用 mysql_real_connect() 建立数据库连接,连接成功后再进入 while(true) 事件循环。此后,工作线程不断从任务队列中取出任务,使用自己持有的 MySQL 连接执行 SQL,执行完成后通过任务中封装的回调函数 cb(res) 将结果返回给业务逻辑,然后继续处理下一个任务。这样每个线程都能复用自己的数据库连接,避免频繁创建和销毁连接,提高数据库访问效率。

🌏简易网站数据流动如何进行?
比如说一个用户逻辑是怎么走的? 注册or登录
- 用户 ------ 服务器&&网站 ------ 中间件 ------ 数据库

Web业务处理 + C++数据库负载均衡 + MySQL集群定时同步
-
用户访问网站:浏览器向服务器发送 HTTP GET 请求,获取注册页面或登录页面。
-
用户提交信息:用户填写用户名、密码等信息,通过 HTTP POST 请求将数据发送给服务器。
-
Web应用层处理 :Nginx / Tomcat 接收请求,由 PHP、Python 或 Java 等应用程序提取 GET / POST 参数 ,完成业务逻辑处理,并生成对应的
SQL请求。 -
C++负载均衡:应用层将数据库请求交给 C++ 模块,由 C++ 根据负载均衡策略,从三个 MySQL 节点中选择一个节点,将 SQL 请求转发过去。
-
MySQL执行操作:被选中的 MySQL 节点执行 INSERT、SELECT、UPDATE 等操作,并将执行结果返回给 C++ 模块。
-
结果返回用户:C++ 将数据库执行结果返回给 Web 应用层,应用层进一步处理后,通过 HTTP 响应返回浏览器。
-
MySQL数据同步:三个 MySQL 节点之间按照设定的周期进行数据同步,使各节点的数据保持一致
用户通过浏览器发起 HTTP 请求,Web应用层负责接收请求和处理业务逻辑。对于数据库查询,优先访问缓存,命中则直接返回,未命中则通过 C++ 负载均衡模块选择一个 MySQL 节点执行查询,并将结果写入缓存。对于数据写入或修改,则选择一个 MySQL 节点执行操作,并处理相关缓存更新。三个 MySQL 节点之间通过定时同步机制共享数据
📢写在最后
接下来登场的是 ??

