Bug #101107 Using VIEW with a prepared statement makes MySQL very slow
Submitted: 9 Oct 2020 15:24 Modified: 5 Nov 2020 17:28
Reporter: Masahiro Tomita Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server: Prepared statements Severity:S3 (Non-critical)
Version:8.0.21 OS:Ubuntu
Assigned to: CPU Architecture:Any

[9 Oct 2020 15:24] Masahiro Tomita
Description:
Using VIEW with a prepared statement and using mysql_stmt_reset() makes MySQL very slow.

If you use TABLE instead of VIEW or don't use mysql_stmt_reset(), it won't be slow.

How to repeat:
% cat test.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <mysql/mysql.h>

int main(int argc, char *argv[])
{
  MYSQL my;
  MYSQL_STMT *st;
  struct timespec ts;
  double t1, t2;

  mysql_init(&my);
  if (mysql_real_connect(&my, "localhost", "root", "", "test", 0, NULL, 0) == NULL)
    goto mysql_error;
  if ((st = mysql_stmt_init(&my)) == NULL)
    goto mysql_error;
#ifdef WITHOUT_VIEW
  char *q = "select * from t where name='hogehoge'";
#else
  char *q = "select * from v where name='hogehoge'";
#endif
  if (mysql_stmt_prepare(st, q, strlen(q)))
    goto stmt_error;

  puts("count\t  sec\t  qps");

  clock_gettime(CLOCK_REALTIME_COARSE, &ts);
  t1 = ts.tv_sec + (double)ts.tv_nsec / 1000000000;

  for (int i = 1; i <= 50000; i++) {
    if (i % 1000 == 0) {
      clock_gettime(CLOCK_REALTIME_COARSE, &ts);
      t2 = ts.tv_sec + (double)ts.tv_nsec / 1000000000;
      printf("%5d\t%6.3f\t%9.3f\n", i, t2-t1, 1000/(t2 - t1));
      clock_gettime(CLOCK_REALTIME_COARSE, &ts);
      t1 = ts.tv_sec + (double)ts.tv_nsec / 1000000000;
    }
    if (mysql_stmt_execute(st))
      goto stmt_error;
    if (mysql_stmt_store_result(st))
      goto stmt_error;
#ifndef WITHOUT_STMT_RESET
    if (mysql_stmt_reset(st))
      goto stmt_error;
#endif
  }
  return 0;

mysql_error:
  puts(mysql_error(&my));
  exit(1);

stmt_error:
  puts(mysql_stmt_error(st));
  exit(1);
}

% docker run --name mysql80 -d -e MYSQL_ALLOW_EMPTY_PASSWORD=1 mysql:8.0
% docker exec -it mysql80 mysql

mysql> create database test;
mysql> use test;
mysql> create table t (id int auto_increment primary key, name varchar(255));
mysql> create view v (id, name) as select id, name from t;
mysql> quit

% docker exec mysql80 bash -c 'apt update && apt install -y gcc libmysqlclient-dev'
% docker cp test.c mysql80:/

% docker exec mysql80 gcc test.c -lmysqlclient
% docker exec -it mysql80 ./a.out
count	  sec	  qps
 1000	 0.116	 8620.854
 2000	 0.140	 7142.998
 3000	 0.152	 6579.073
 4000	 0.180	 5555.664
 5000	 0.192	 5208.439
 6000	 0.220	 4545.543
 7000	 0.252	 3968.331
 8000	 0.320	 3125.061
 9000	 0.440	 2272.771
10000	 0.440	 2272.771
11000	 0.372	 2688.224
12000	 0.412	 2427.233
13000	 0.460	 2173.956
14000	 0.520	 1923.114
15000	 0.544	 1838.271
16000	 0.568	 1760.598
17000	 0.600	 1666.699
18000	 0.664	 1506.053
19000	 0.664	 1506.053
20000	 0.688	 1453.517
21000	 0.708	 1412.457
22000	 0.748	 1336.925
23000	 0.788	 1269.060
24000	 0.820	 1219.536
25000	 0.964	 1037.364
26000	 0.896	 1116.093
27000	 0.960	 1041.687
28000	 0.980	 1020.428
29000	 1.000	 1000.020
30000	 1.168	  856.181
31000	 1.080	  925.944
32000	 1.140	  877.210
33000	 1.196	  836.137
34000	 1.188	  841.767
35000	 1.288	  776.413
36000	 1.308	  764.541
37000	 1.416	  706.228
38000	 1.304	  766.886
39000	 1.384	  722.558
40000	 1.432	  698.338
41000	 1.552	  644.342
42000	 1.428	  700.294
43000	 1.464	  683.073
44000	 1.496	  668.462
45000	 1.540	  649.363
46000	 1.548	  646.007
47000	 1.612	  620.359
48000	 1.616	  618.824
49000	 1.660	  602.421
50000	 1.692	  591.028

% docker exec mysql80 gcc test.c -lmysqlclient -DWITHOUT_VIEW
% docker exec -it mysql80 ./a.out                            
count	  sec	  qps
 1000	 0.084	11905.017
 2000	 0.088	11363.875
 3000	 0.088	11363.844
 4000	 0.084	11905.017
 5000	 0.084	11905.017
 6000	 0.092	10869.790
 7000	 0.084	11904.983
 8000	 0.088	11363.875
 9000	 0.088	11363.875
10000	 0.092	10869.790
11000	 0.084	11904.983
12000	 0.096	10416.878
13000	 0.116	 8620.872
14000	 0.088	11363.875
15000	 0.104	 9615.575
16000	 0.128	 7812.659
17000	 0.088	11363.875
18000	 0.088	11363.844
19000	 0.096	10416.878
20000	 0.092	10869.790
21000	 0.092	10869.790
22000	 0.100	10000.200
23000	 0.092	10869.790
24000	 0.100	10000.200
25000	 0.100	10000.200
26000	 0.084	11905.017
27000	 0.088	11363.875
28000	 0.088	11363.875
29000	 0.092	10869.790
30000	 0.088	11363.844
31000	 0.092	10869.790
32000	 0.096	10416.878
33000	 0.108	 9259.460
34000	 0.092	10869.761
35000	 0.100	10000.224
36000	 0.092	10869.790
37000	 0.100	10000.200
38000	 0.096	10416.878
39000	 0.100	10000.200
40000	 0.096	10416.878
41000	 0.112	 8928.743
42000	 0.108	 9259.460
43000	 0.092	10869.790
44000	 0.088	11363.844
45000	 0.088	11363.875
46000	 0.096	10416.878
47000	 0.092	10869.790
48000	 0.092	10869.790
49000	 0.100	10000.200
50000	 0.100	10000.200

% docker exec mysql80 gcc test.c -lmysqlclient -DWITHOUT_STMT_RESET
% docker exec -it mysql80 ./a.out                                  
count	  sec	  qps
 1000	 0.088	11363.875
 2000	 0.092	10869.790
 3000	 0.088	11363.875
 4000	 0.088	11363.875
 5000	 0.088	11363.844
 6000	 0.088	11363.875
 7000	 0.092	10869.790
 8000	 0.092	10869.790
 9000	 0.084	11904.983
10000	 0.092	10869.790
11000	 0.088	11363.875
12000	 0.088	11363.875
13000	 0.092	10869.790
14000	 0.088	11363.844
15000	 0.092	10869.790
16000	 0.084	11905.017
17000	 0.092	10869.790
18000	 0.092	10869.761
19000	 0.084	11905.017
20000	 0.088	11363.875
21000	 0.084	11904.983
22000	 0.092	10869.790
23000	 0.088	11363.875
24000	 0.096	10416.878
25000	 0.084	11905.017
26000	 0.092	10869.761
27000	 0.092	10869.790
28000	 0.092	10869.790
29000	 0.088	11363.875
30000	 0.092	10869.790
31000	 0.092	10869.790
32000	 0.088	11363.844
33000	 0.088	11363.875
34000	 0.092	10869.790
35000	 0.092	10869.790
36000	 0.088	11363.875
37000	 0.084	11904.983
38000	 0.092	10869.790
39000	 0.096	10416.878
40000	 0.088	11363.875
41000	 0.092	10869.790
42000	 0.088	11363.844
43000	 0.088	11363.875
44000	 0.088	11363.875
45000	 0.092	10869.790
46000	 0.092	10869.790
47000	 0.088	11363.844
48000	 0.088	11363.875
49000	 0.092	10869.790
50000	 0.092	10869.790
[12 Oct 2020 12:14] MySQL Verification Team
Hello Mr. Tomita,

Thank you for your bug report.

However, what you describe is, actually, expected behaviour.

If you read our documentation on the statement reset, it actually clears lots of buffers and hence a new run must be slower.

Also, if you read our documentation on the VIEWs, you will see that those are actually NOT replacements for the TABLEs. Unlike a direct work with tables, using views requires several additional operations. Views are here to make programming easier and not to make it as fast.

Not a bug.
[13 Oct 2020 1:46] Masahiro Tomita
This does not indicate that it is slow, but that it gradually slows down as it repeats.

count	  sec	  qps
 1000	 0.116	 8620.854
...
10000	 0.440	 2272.771
...
20000	 0.688	 1453.517
...
30000	 1.168	  856.181
...
40000	 1.432	  698.338
...
50000	 1.692	  591.028
[13 Oct 2020 12:29] MySQL Verification Team
Hi Mr. Tomita,

This phenomenon has the same explanation as the one provided. More buffers get cleared and more temporary tables get created.

Not a bug.
[5 Nov 2020 17:28] Masahiro Tomita
This has been fixed in MySQL 8.0.22.

Results at 8.0.22:

% docker exec -it mysql80 ./a.out
count	  sec	  qps
 1000	 0.108	 9259.460
 2000	 0.104	 9615.575
 3000	 0.104	 9615.597
 4000	 0.104	 9615.597
 5000	 0.100	10000.200
 6000	 0.104	 9615.597
 7000	 0.108	 9259.460
 8000	 0.100	10000.224
 9000	 0.100	10000.200
10000	 0.108	 9259.460
11000	 0.100	10000.224
12000	 0.108	 9259.440
13000	 0.100	10000.224
14000	 0.112	 8928.762
15000	 0.104	 9615.597
16000	 0.104	 9615.597
17000	 0.104	 9615.575
18000	 0.100	10000.224
19000	 0.100	10000.200
20000	 0.104	 9615.597
21000	 0.100	10000.224
22000	 0.100	10000.200
23000	 0.104	 9615.597
24000	 0.100	10000.224
25000	 0.112	 8928.762
26000	 0.096	10416.878
27000	 0.132	 7575.924
28000	 0.100	10000.200
29000	 0.104	 9615.597
30000	 0.100	10000.224
31000	 0.108	 9259.460
32000	 0.100	10000.200
33000	 0.104	 9615.597
34000	 0.100	10000.200
35000	 0.104	 9615.597
36000	 0.112	 8928.762
37000	 0.096	10416.904
38000	 0.104	 9615.575
39000	 0.104	 9615.597
40000	 0.096	10416.904
41000	 0.104	 9615.575
42000	 0.100	10000.224
43000	 0.100	10000.200
44000	 0.104	 9615.597
45000	 0.100	10000.224
46000	 0.104	 9615.575
47000	 0.104	 9615.597
48000	 0.096	10416.904
49000	 0.108	 9259.440
50000	 0.104	 9615.597
[6 Nov 2020 12:33] MySQL Verification Team
Thank you for the feedback.