注:2013年的老文章

Prepare的作用

Prepare SQL产生的原因。首先从mysql服务器执行sql的过程开始讲起,SQL执行过程包括以下阶段 词法分析->语法分析->语义分析->执行计划优化->执行。词法分析->语法分析这两个阶段我们称之为硬解析。词法分析识别sql中每个词,语法分析解析SQL语句是否符合sql语法,并得到一棵语法树(Lex)。对于只是参数不同,其他均相同的sql,它们执行时间不同但硬解析的时间是相同的。而同一SQL随着查询数据的变化,多次查询执行时间可能不同,但硬解析的时间是不变的。对于sql执行时间较短,sql硬解析的时间占总执行时间的比率越高。而对于淘宝应用的绝大多数事务型SQL,查询都会走索引,执行时间都比较短。因此淘宝应用db sql硬解析占的比重较大。

Prepare的出现就是为了优化硬解析的问题。Prepare在服务器端的执行过程如下

1)  Prepare 接收客户端带”?”的sql, 硬解析得到语法树(stmt->Lex), 缓存在线程所在的preparestatement cache中。此cache是一个HASH MAP. Key为stmt->id. 然后返回客户端stmt->id等信息。

2)  Execute 接收客户端stmt->id和参数等信息。注意这里客户端不需要再发sql过来。服务器根据stmt->id在preparestatement cache中查找得到硬解析后的stmt, 并设置参数,就可以继续后面的优化和执行了。

Prepare在execute阶段可以节省硬解析的时间。如果sql只执行一次,且以prepare的方式执行,那么sql执行需两次与服务器交互(Prepare和execute), 而以普通(非prepare)方式,只需要一次交互。这样使用prepare带来额外的网络开销,可能得不偿失。我们再来看同一sql执行多次的情况,比如以prepare方式执行10次,那么只需要一次硬解析。这时候  额外的网络开销就显得微乎其微了。因此prepare适用于频繁执行的SQL。

Prepare的另一个作用是防止sql注入,不过这个是在客户端jdbc通过转义实现的,跟服务器没有关系。

硬解析的比重

交易买家库 tcbyer压测时,通过perf 得到的结果。

硬解析相关的函数比重都比较靠前(MYSQLparse 4.93%, lex_one_token 1.79%, lex_start 1.12%)总共接近8%。因此,服务器使用prepare是可以带来较多的性能提升的。

jdbc与prepare

jdbc服务器端的参数:

useServerPrepStmts:默认为false. 是否使用服务器prepare开关

jdbc客户端参数:

cachePrepStmts:默认false.是否缓存prepareStatement对象。每个连接都有一个缓存,是以sql为唯一标识的LRU cache. 同一连接下,不同stmt可以不用重新创建prepareStatement对象。

prepStmtCacheSize:LRU cache中prepareStatement对象的个数。一般设置为最常用sql的个数。

prepStmtCacheSqlLimit:prepareStatement对象的大小。超出大小不缓存。

Jdbc对prepare的处理过程:

useServerPrepStmts=true时Jdbc对prepare的处理

1)  创建PreparedStatement对象,向服务器发送COM_PREPARE命令,并传送带问号的sql. 服务器返回jdbc stmt->id等信息

2)  向服务器发送COM_EXECUTE命令,并传送参数信息。

useServerPrepStmts=false时Jdbc对prepare的处理

1)  创建PreparedStatement对象,此时不会和服务器交互。

2) 根据参数和PreparedStatement对象拼接完整的SQL,向服务器发送QUERY命令

我们再看参数cachePrepStmts打开时在useServerPrepStmts为true或false时,均缓存PreparedStatement对象。只不过useServerPrepStmts为的true缓存PreparedStatement对象包含服务器的stmt->id等信息,也就是说如果重用了PreparedStatement对象,那么就省去了和服务器通讯(COM_PREPARE命令)的开销。而useServerPrepStmts=false是,开启cachePrepStmts缓存PreparedStatement对象只是简单的sql解析信息,因此此时开启cachePrepStmts意义不是太大。

我们来开看一段java代码

1
2
3
4
5
6
7
8
9
10
11
            Connection con = null;
            PreparedStatement ps = null;
            String sql = "select * from user where id=?";
            ps = con.prepareStatement(sql);            
            ps.setInt(11);‍‍            
            ps.executeQuery();            
            ps.close();            
            ps = con.prepareStatement(sql);            
            ps.setInt(13);            
            ps.executeQuery();            
            ps.close();

这段代码在同一会话中两次prepare执行同一语句,并且之间有ps.close();

useServerPrepStmts=false时,服务器会两次硬解析同一SQL。

useServerPrepStmts=true, cachePrepStmts=false时服务器仍然会两次硬解析同一SQL。

useServerPrepStmts=true, cachePrepStmts=true时服务器只会硬解析一次SQL。

如果两次prepare之间没有ps.close();那么cachePrepStmts=true,cachePrepStmts=false也只需一次硬解析.

因此,客户端对同一sql,频繁分配和释放PreparedStatement对象的情况下,开启cachePrepStmts参数是很有必要的。

测试

1)做了一个简单的测试,主要测试prepare的效果和useServerPrepStmts参数的影响.

1
<span style="font-family: 宋体, SimSun; font-size: 14px;">        cnt = 5000;<br>        // no prepare<br>        String sql = "select biz_order_id,out_order_id,seller_nick,buyer_nick,seller_id,buyer_id,auction_id,auction_title,auction_price,buy_amount,biz_type,sub_biz_type,fail_reason,pay_status,logistics_status,out_trade_status,snap_path,gmt_create,status,ifnull(buyer_rate_status, 4) buyer_rate_status from tc_biz_order_0030 where " +<br>        "parent_id = 594314511722841 or parent_id =547667559932641;";<br><br>        begin = new Date();<br>        System.out.println("begin:" + df.format(begin));<br>        <br>        stmt = con.createStatement();<br>        for (int i = 0; i < cnt; i++)<br>        {         <br>            stmt.executeQuery(sql);<br>        } <br>               <br>        end = new Date();<br>        System.out.println("end:" + df.format(end));<br>        <br>        long temp end.getTime() - begin.getTime();<br>        System.out.println("no perpare interval:" temp);<br>        <br>        <br>        // test prepare       <br>        sql = "select biz_order_id,out_order_id,seller_nick,buyer_nick,seller_id,buyer_id,auction_id,auction_title,auction_price,buy_amount,biz_type,sub_biz_type,fail_reason,pay_status,logistics_status,out_trade_status,snap_path,gmt_create,status,ifnull(buyer_rate_status, 4) buyer_rate_status from tc_biz_order_0030 where " +<br>                "parent_id = 594314511722841 or parent_id =?;";<br>        ps = con.prepareStatement(sql);<br>        BigInteger param = new BigInteger("547667559932641");<br>        <br>        begin = new Date();<br>        System.out.println("begin:" + df.format(begin));<br>        <br>        for (int i = 0; i < cnt; i++)<br>        {<br>         ps.setObject(1, param);<br>            ps.executeQuery(); <br>        } <br>   <br>        end = new Date();<br>        System.out.println("end:" + df.format(end));<br>        <br>        temp end.getTime() - begin.getTime();<br>        System.out.println("prepare interval:" temp);<br></span>

经多次采样测试结果如下:

非prepare和prepare时间比
useServerPrepStmts=true 0.93
useServerPrepStmts=false 1.01

结论:

useServerPrepStmts=true时,prepare提升7%;

useServerPrepStmts=false时,prepare与非prepare性能相当。

如果将语句简化为select * from tc_biz_order_0030 where parent_id =?。那么测试的结论useServerPrepStmts=true时,prepare仅提升2%;sql越简单硬解析的时间就越少,prepare的提升就越少。

注意:这个测试是在单个连接,单条sql的理想情况下进行的,线上会出现多连接多sql,还有sql执行频率,sql的复杂程度等不同,因此prepare的提升效果会随具体环境而变化。

2)prepare 前后的perf top 对比

以下为非prepare

6.46%   mysqld  mysqld              [.] _Z10MYSQLparsePv

3.74%   mysqld  libc-2.12.so        [.] __memcpy_ssse3

2.50%   mysqld  mysqld              [.] my_hash_sort_utf8

2.15%   mysqld  mysqld              [.] cmp_dtuple_rec_with_match

2.05%   mysqld  mysqld              [.] _ZL13lex_one_tokenPvS_

1.46%   mysqld  mysqld              [.] buf_page_get_gen

1.34%   mysqld  mysqld              [.] page_cur_search_with_match

1.31%   mysqld  mysqld              [.] _ZL14build_templateP19row_prebuilt_structP3THDP5TABLEj

1.24%   mysqld  mysqld              [.] rec_init_offsets

1.11%   mysqld  libjemalloc.so.1    [.] free

1.09%   mysqld  mysqld              [.] rec_get_offsets_func

1.01%   mysqld  libjemalloc.so.1    [.] malloc

0.96%   mysqld  libc-2.12.so        [.] __strlen_sse42

0.93%   mysqld  mysqld              [.] _ZN4JOIN8optimizeEv

0.91%   mysqld  mysqld              [.] _ZL15get_hash_symbolPKcjb

0.88%   mysqld  mysqld              [.] row_search_for_mysql

0.86%   mysqld  [kernel.kallsyms]   [k] tcp_recvmsg

以下为perpare

3.46%   mysqld  libc-2.12.so        [.] __memcpy_ssse3

2.32%   mysqld  mysqld              [.] cmp_dtuple_rec_with_match

2.14%   mysqld  mysqld              [.] _ZL14build_templateP19row_prebuilt_structP3THDP5TABLEj

1.96%   mysqld  mysqld              [.] buf_page_get_gen

1.66%   mysqld  mysqld              [.] page_cur_search_with_match

1.54%   mysqld  mysqld              [.] row_search_for_mysql

1.44%   mysqld  mysqld              [.] btr_cur_search_to_nth_level

1.41%   mysqld  libjemalloc.so.1    [.] free

1.35%   mysqld  mysqld              [.] rec_init_offsets

1.32%   mysqld  [kernel.kallsyms]   [k] kfree

1.14%   mysqld  libjemalloc.so.1    [.] malloc

1.08%   mysqld  [kernel.kallsyms]   [k] fget_light

1.05%   mysqld  mysqld              [.] rec_get_offsets_func

0.99%   mysqld  mysqld              [.] _ZN8Protocol24send_result_set_metadataEP4ListI4ItemEj

0.90%   mysqld  mysqld              [.] sync_array_print_long_waits

0.87%   mysqld  mysqld              [.] page_rec_get_n_recs_before

0.81%   mysqld  mysqld              [.] _ZN4JOIN8optimizeEv

0.81%   mysqld  libc-2.12.so        [.] __strlen_sse42

0.78%   mysqld  mysqld              [.] _ZL20make_join_statisticsP4JOINP10TABLE_LISTP4ItemP16st_dynamic_array

0.72%   mysqld  [kernel.kallsyms]   [k] tcp_recvmsg

0.63%   mysqld  libpthread-2.12.so  [.] __pthread_getspecific_internal

0.63%   mysqld  [kernel.kallsyms]   [k] sk_run_filter

0.60%   mysqld  mysqld              [.] _Z19find_field_in_tableP3THDP5TABLEPKcjbPj

0.60%   mysqld  mysqld              [.] page_check_dir

0.57%   mysqld  mysqld              [.] _Z16dispatch_command19enum_server_commandP3THDP

对比可以发现 MYSQLparse lex_one_token在prepare时已优化掉了。

思考

1 开启cachePrepStmts的问题,前面谈到每个连接都有一个缓存,是以sql为唯一标识的LRU cache. 在分表较多,大连接的情况下,可能会个应用服务器带来内存问题。这里有个前提是ibatis是默认使用prepare的。 在mybatis中,标签statementType可以指定某个sql是否是使用prepare.

statementType Any one of STATEMENT, PREPARED or CALLABLE. This causes MyBatis to use Statement, PreparedStatement orCallableStatement respectively. Default: PREPARED.

这样可以精确控制只对频率较高的sql使用prepare,从而控制使用prepare sql的个数,减少内存消耗。遗憾的是目前集团貌似大多使用的是ibatis 2.0版本,不支持statementType

标签。

2 服务器端prepare cache是一个HASH MAP. Key为stmt->id,同时也是每个连接都维护一个。因此也有可能出现内存问题,待实际测试。如有必要需改造成Key为sql的全局cache,这样不同连接的相同prepare sql可以共享。

3 oracle prepare与mysql prepare的区别:

mysql与oracle有一个重大区别是mysql没有oracle那样的执行计划缓存。前面我们讲到SQL执行过程包括以下阶段 词法分析->语法分析->语义分析->执行计划优化->执行。oracle的prepare实际上包括以下阶段:词法分析->语法分析->语义分析->执行计划优化,也就是说oracle的prepare做了更多的事情,execute只需要执行即可。因此,oracle的prepare比mysql更高效。

mysql prepare原理相关推荐

  1. mysql prepare原理,mysql Prepared Statements 原理介绍与优点 | 学步园

    一:介绍从 5.1开始,mysql支持服务器端的Prepared Statements,他使用在client/server更有优势的binary protocol,(mysql的传统的协议中,再把数据 ...

  2. mysql xid原理_MySQL数据库分布式事务XA实现原理分析

    [IT168 技术]MySQL XA原理 MySQL XA分为两类,内部XA与外部XA;内部XA用于同一实例下跨多个引擎的事务,由大家熟悉的Binlog作为协调者;外部XA用于跨多MySQL实例的分布 ...

  3. mysql prepare有什么用_mysql中的prepare介绍和应用

    简单的用set或者declare语句定义变量,然后直接作为sql的表名是不行的,mysql会把变量名当作表名.在其他的sql数据库中也是如此,mssql的解决方法是将整条sql语句作为变量,其中穿插变 ...

  4. mysql prepare 存储过程_mysql prepare 存储过程使用

    语法 [sql] PREPARE statement_name FROM sql_text /*定义*/ EXECUTE statement_name [USING variable [,variab ...

  5. mysql json匹配key为数值_干货篇:一篇文章让你——《深入解析MySQL索引原理》

    概述 最近一段时间重新深入研究了一遍MySQL的内容,今天主要分享分析MySQL索引原理,后续会输出一些关于MySQL方面的干货,希望各位小伙伴喜欢. 一.什么是索引.为什么要建立索引? 关于索引的理 ...

  6. 转:MySQL索引原理及慢查询优化

    来自:http://tech.meituan.com/mysql-index.html MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色, ...

  7. 赠书:深入理解MySQL主从原理

    根据经验,想要快速学习一门技术有3种方式. 第一种方式是通过代码来理解它的实现,反推它的逻辑. 这种方式的难度很大,而且起点相对高,能够沉浸其中的人非常少,过程相对来说是苦闷的,但如果能够沉下心来看代 ...

  8. MySQL主从原理,基于快速学习一门技术的3种方式!

    根据经验,想要快速学习一门技术有3种方式. 第一种方式是通过代码来理解它的实现,反推它的逻辑. 这种方式的难度很大,而且起点相对高,能够沉浸其中的人非常少,过程相对来说是苦闷的,但如果能够沉下心来看代 ...

  9. mysql主从复制原理 简书_mysql主从复制,从原理讲到安装配置,全干货

    0.为什么需要主从复制? 1.在业务复杂的系统中,有这么一个情景,有一句sql语句需要锁表,导致暂时不能使用读的服务,那么就很影响运行中的业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出 ...

最新文章

  1. 舵机任意角度程序_真香!!!飞特发布性价比超高的19kg磁编码360°双轴串口总线舵机STS3215...
  2. PHP ----MySQL 数据库
  3. Maven私服安装及配置——(十二)
  4. ArrayAdapter的使用
  5. MTK+Android编译
  6. 7、Java并发性和多线程-如何创建并运行线程
  7. C语言高级编程:二维数组、指针的指针、数组指针、指针数组
  8. UWidgetBlueprintLibrary
  9. Java读书笔记(8)-单例模式
  10. DataGrid添加CheckBox(下拉列表)
  11. Python使用Mysql官方驱动(取出dict类型的数据)
  12. Java编程实现三种等级的扫雷游戏(完整版)
  13. AtCoder Beginner Contest 262 部分题解
  14. 计算机网络军训口号,关于物联网的军训口号
  15. 【报告分享】2021快手内容生态半年报-快手(附下载)
  16. c语言 运行经过八个翻译阶段,GitHub - BUAA-SE-Compiling/miniplc0-handbook: miniplc0 指导书...
  17. gromcas 分析NAMD轨迹文件dcd
  18. 研究生招生信息网服务器异常,2012研究生网上报名常见问题汇总
  19. TS之namespace与module
  20. Python细枝末节个人知识记录

热门文章

  1. 快速人体姿态估计--Pose Proposal Networks
  2. Windows系统下MySQL安装详细教程(解决MySQL服务无法启动)
  3. LeetCode 92. Reverse Linked List II--Python 解法--反转部分链表--笔试算法题
  4. WSL的openssh-server使用报错:Could not load host key: /etc/ssh/ssh_host_rsa_key
  5. 优先深度搜索判断曲线相交_程序员必知的十大基础实用算法之-DFS(深度优先搜索)...
  6. linux下php连接mysql数据库_Linux下PHP连接Oracle数据库
  7. matlab fft simulink,在Simulink中复制MATLAB的FFT?
  8. java获取真实ip工具类
  9. python自动整理文件夹_计算机文件和文件夹的Python自动管理,自动化,电脑,及
  10. 奔腾微型计算机采用的微处理器的型号,“奔腾”微型计算机采用的微处理器的型号是()。...