目录
1、看源码动力
平时工作中,经常要模拟高并发场景对MYSQL进行压测,比如轮询2W个不同用户模拟登录。因为mysqlslap不支持SQL传参,这个时候可以选择使用JEMETER工具,但这个工具太重了。所以这个时候最好选择是自己编写程序进行压测。怎么写个小巧性能又好的工具,mysqlslap是个很好的参考。
2、mysqlslap简介
mysqlslap is a diagnostic program designed to emulate client load for a MySQL server and to report the timing of each stage. It works as if multiple clients are accessing the server.
3、8.0版本官方链接
MySQL :: MySQL 8.0 Reference Manual :: 4.5.8 mysqlslap — A Load Emulation Client
4、mysqlslap执行的流程图
5、MAIN函数
循环的嵌套关系是:
1、按engine_options列表循环
2、接着按concurrency列表循环
3、调用函数concurrency_loop
int main(int argc, char **argv) {
MYSQL mysql{};
……
mysql_init(&mysql);
……
mysql_options(&mysql, MYSQL_OPT_ZSTD_COMPRESSION_LEVEL,
&opt_zstd_compress_level);
……
set_sql_mode(&mysql);
//对锁counter_mutex、sleeper_mutex,条件变量count_threshold、sleep_threshold进行初始化
native_mutex_init(&counter_mutex, nullptr);
native_cond_init(&count_threshold);
native_mutex_init(&sleeper_mutex, nullptr);
native_cond_init(&sleep_threshold);
/* Main iterations loop */
//循环的嵌套关系是:
//1、按engine_options列表循环
//2、接着按concurrency列表循环
eptr = engine_options;
do {
/* For the final stage we run whatever queries we were asked to run */
uint *current;
if (verbose >= 2) printf("Starting Concurrency Test\n");
if (*concurrency) {
for (current = concurrency; current && *current; current++)
concurrency_loop(&mysql, *current, eptr);
} else {
uint infinite = 1;
do {
concurrency_loop(&mysql, infinite, eptr);
} while (infinite++);
}
if (!opt_preserve) drop_schema(&mysql, create_schema_string);
} while (eptr ? (eptr = eptr->next) : nullptr);
native_mutex_destroy(&counter_mutex);
native_cond_destroy(&count_threshold);
native_mutex_destroy(&sleeper_mutex);
native_cond_destroy(&sleep_threshold);
mysql_close(&mysql); /* Close & free connection */
……
return EXIT_SUCCESS;
}
6、concurrency_loop函数
循环iterations次调用run_scheduler函数
void concurrency_loop(MYSQL *mysql, uint current, option_string *eptr) {
……
//循环嵌套关系
//1、循环iterations次
for (x = 0, sptr = head_sptr; x < iterations; x++, sptr++) {
……
run_scheduler(sptr, query_statements, current, client_limit);
if (post_statements) run_statements(mysql, post_statements);
……
}
……
//打印日志
generate_stats(&conclusion, eptr, head_sptr);
if (!opt_silent) print_conclusions(&conclusion);
if (opt_csv_str) print_conclusions_csv(&conclusion);
my_free(head_sptr);
}
7、run_statements函数
遍stmt中所有SQL,依次执行
static int run_statements(MYSQL *mysql, statement *stmt) {
statement *ptr;
MYSQL_RES *result;
DBUG_TRACE;
for (ptr = stmt; ptr && ptr->length; ptr = ptr->next) {
if (run_query(mysql, ptr->string, ptr->length)) {
fprintf(stderr, "%s: Cannot run query %.*s ERROR : %s\n", my_progname,
(uint)ptr->length, ptr->string, mysql_error(mysql));
exit(1);
}
if (mysql_field_count(mysql)) {
result = mysql_store_result(mysql);
mysql_free_result(result);
}
}
return 0;
}
8、run_scheduler函数
启动concur个线程,线程函数为run_task
static int run_scheduler(stats *sptr, statement *stmts, uint concur,
ulonglong limit) {
……
native_mutex_lock(&counter_mutex);
thread_counter = 0;
native_mutex_lock(&sleeper_mutex);
master_wakeup = 1;
native_mutex_unlock(&sleeper_mutex);
//启动concur个线程
for (x = 0; x < concur; x++) {
/* now you create the thread */
if (my_thread_create(&mainthread, &attr, run_task, (void *)&con) != 0) {
fprintf(stderr, "%s: Could not create thread\n", my_progname);
exit(0);
}
thread_counter++;
}
native_mutex_unlock(&counter_mutex);
my_thread_attr_destroy(&attr);
……
/*
We loop until we know that all children have cleaned up.
*/
……
return 0;
}
9、run_task函数
使用goto语句循环queries次执行runquery函数
extern "C" void *run_task(void *p) {
……
{
……
if (mysql_thread_init()) {
fprintf(stderr, "%s: mysql_thread_init() failed ERROR : %s\n",
my_progname, mysql_error(mysql));
mysql_close(mysql);
exit(0);
}
……
if (!opt_only_print) {
if (slap_connect(mysql)) goto end;
}
……
limit_not_met:
for (ptr = con->stmt, detach_counter = 0; ptr && ptr->length;
ptr = ptr->next, detach_counter++) {
……
if (run_query(mysql, buffer, length)) {
……
//遍历查询结果
……
queries++;
……
//居然使用goto语句来循环queries次数!!!
if (con->limit && queries == con->limit) goto end;
}
if (con->limit && queries < con->limit) goto limit_not_met;
end:
if (commit_rate) run_query(mysql, "COMMIT", strlen("COMMIT"));
……
}
10、run_query函数
static int run_query(MYSQL *mysql, const char *query, size_t len) {
if (opt_only_print) {
printf("%.*s;\n", (int)len, query);
return 0;
}
if (verbose >= 3) printf("%.*s;\n", (int)len, query);
return mysql_real_query(mysql, query, (ulong)len);
}