欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MySQL prepare原理詳解

 更新時(shí)間:2017年09月28日 10:34:17   作者:sstrive  
這篇文章主要介紹了MySQL prepare的相關(guān)內(nèi)容,包括prepare的產(chǎn)生,在服務(wù)器端的執(zhí)行過程,以及jdbc對prepare的處理以及相關(guān)測試,需要的朋友可以了解下。希望對大家有所幫助。

Prepare的好處 

    Prepare SQL產(chǎn)生的原因。首先從mysql服務(wù)器執(zhí)行sql的過程開始講起,SQL執(zhí)行過程包括以下階段 詞法分析->語法分析->語義分析->執(zhí)行計(jì)劃優(yōu)化->執(zhí)行。詞法分析->語法分析這兩個(gè)階段我們稱之為硬解析。詞法分析識別sql中每個(gè)詞,語法分析解析SQL語句是否符合sql語法,并得到一棵語法樹(Lex)。對于只是參數(shù)不同,其他均相同的sql,它們執(zhí)行時(shí)間不同但硬解析的時(shí)間是相同的。而同一SQL隨著查詢數(shù)據(jù)的變化,多次查詢執(zhí)行時(shí)間可能不同,但硬解析的時(shí)間是不變的。對于sql執(zhí)行時(shí)間較短,sql硬解析的時(shí)間占總執(zhí)行時(shí)間的比率越高。而對于淘寶應(yīng)用的絕大多數(shù)事務(wù)型SQL,查詢都會走索引,執(zhí)行時(shí)間都比較短。因此淘寶應(yīng)用db sql硬解析占的比重較大。 

    Prepare的出現(xiàn)就是為了優(yōu)化硬解析的問題。Prepare在服務(wù)器端的執(zhí)行過程如下

 1)  Prepare 接收客戶端帶”?”的sql, 硬解析得到語法樹(stmt->Lex), 緩存在線程所在的preparestatement cache中。此cache是一個(gè)HASH MAP. Key為stmt->id. 然后返回客戶端stmt->id等信息。

 2)  Execute 接收客戶端stmt->id和參數(shù)等信息。注意這里客戶端不需要再發(fā)sql過來。服務(wù)器根據(jù)stmt->id在preparestatement cache中查找得到硬解析后的stmt, 并設(shè)置參數(shù),就可以繼續(xù)后面的優(yōu)化和執(zhí)行了。

    Prepare在execute階段可以節(jié)省硬解析的時(shí)間。如果sql只執(zhí)行一次,且以prepare的方式執(zhí)行,那么sql執(zhí)行需兩次與服務(wù)器交互(Prepare和execute), 而以普通(非prepare)方式,只需要一次交互。這樣使用prepare帶來額外的網(wǎng)絡(luò)開銷,可能得不償失。我們再來看同一sql執(zhí)行多次的情況,比如以prepare方式執(zhí)行10次,那么只需要一次硬解析。這時(shí)候  額外的網(wǎng)絡(luò)開銷就顯得微乎其微了。因此prepare適用于頻繁執(zhí)行的SQL。

    Prepare的另一個(gè)作用是防止sql注入,不過這個(gè)是在客戶端jdbc通過轉(zhuǎn)義實(shí)現(xiàn)的,跟服務(wù)器沒有關(guān)系。
硬解析的比重

   壓測時(shí)通過perf 得到的結(jié)果,硬解析相關(guān)的函數(shù)比重都比較靠前(MYSQLparse 4.93%, lex_one_token 1.79%, lex_start 1.12%)總共接近8%。因此,服務(wù)器使用prepare是可以帶來較多的性能提升的。

jdbc與prepare 

  jdbc服務(wù)器端的參數(shù):

   useServerPrepStmts:默認(rèn)為false. 是否使用服務(wù)器prepare開關(guān)

 jdbc客戶端參數(shù):

   cachePrepStmts:默認(rèn)false.是否緩存prepareStatement對象。每個(gè)連接都有一個(gè)緩存,是以sql為唯一標(biāo)識的LRU cache. 同一連接下,不同stmt可以不用重新創(chuàng)建prepareStatement對象。

  prepStmtCacheSize:LRU cache中prepareStatement對象的個(gè)數(shù)。一般設(shè)置為最常用sql的個(gè)數(shù)。

  prepStmtCacheSqlLimit:prepareStatement對象的大小。超出大小不緩存。

 Jdbc對prepare的處理過程: 

useServerPrepStmts=true時(shí)Jdbc對prepare的處理

  1)  創(chuàng)建PreparedStatement對象,向服務(wù)器發(fā)送COM_PREPARE命令,并傳送帶問號的sql. 服務(wù)器返回jdbc stmt->id等信息

  2)  向服務(wù)器發(fā)送COM_EXECUTE命令,并傳送參數(shù)信息。

 useServerPrepStmts=false時(shí)Jdbc對prepare的處理

  1)  創(chuàng)建PreparedStatement對象,此時(shí)不會和服務(wù)器交互。

  2) 根據(jù)參數(shù)和PreparedStatement對象拼接完整的SQL,向服務(wù)器發(fā)送QUERY命令

  我們再看參數(shù)cachePrepStmts打開時(shí)在useServerPrepStmts為true或false時(shí),均緩存PreparedStatement對象。只不過useServerPrepStmts為的true緩存PreparedStatement對象包含服務(wù)器的stmt->id等信息,也就是說如果重用了PreparedStatement對象,那么就省去了和服務(wù)器通訊(COM_PREPARE命令)的開銷。而useServerPrepStmts=false是,開啟cachePrepStmts緩存PreparedStatement對象只是簡單的sql解析信息,因此此時(shí)開啟cachePrepStmts意義不是太大。

我們來開看一段java代碼 

Connection con = null;
      PreparedStatement ps = null;
      String sql = "select * from user where id=?";
      ps = con.prepareStatement(sql);      
      ps.setInt(1, 1);‍‍      
      ps.executeQuery();      
      ps.close();      
      ps = con.prepareStatement(sql);      
      ps.setInt(1, 3);      
      ps.executeQuery();      
      ps.close();

這段代碼在同一會話中兩次prepare執(zhí)行同一語句,并且之間有ps.close();

    useServerPrepStmts=false時(shí),服務(wù)器會兩次硬解析同一SQL。

   useServerPrepStmts=true, cachePrepStmts=false時(shí)服務(wù)器仍然會兩次硬解析同一SQL。

   useServerPrepStmts=true, cachePrepStmts=true時(shí)服務(wù)器只會硬解析一次SQL。

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

   因此,客戶端對同一sql,頻繁分配和釋放PreparedStatement對象的情況下,開啟cachePrepStmts參數(shù)是很有必要的。

測試

  1)做了一個(gè)簡單的測試,主要測試prepare的效果和useServerPrepStmts參數(shù)的影響.    

cnt = 5000;
    // no prepare
    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 " +
    "parent_id = 594314511722841 or parent_id =547667559932641;";
    begin = new Date();
    System.out.println("begin:" + df.format(begin));
    stmt = con.createStatement();
    for (int i = 0; i < cnt; i++)
    {      
      stmt.executeQuery(sql);
    } 
    end = new Date();
    System.out.println("end:" + df.format(end));
    long temp = end.getTime() - begin.getTime();
    System.out.println("no perpare interval:" + temp);
    
    // test prepare    
    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 " +
        "parent_id = 594314511722841 or parent_id =?;";
    ps = con.prepareStatement(sql);
    BigInteger param = new BigInteger("547667559932641");
    begin = new Date();
    System.out.println("begin:" + df.format(begin));
    for (int i = 0; i < cnt; i++)
    {
      ps.setObject(1, param);
      ps.executeQuery(); 
    } 
    end = new Date();
    System.out.println("end:" + df.format(end));
    temp = end.getTime() - begin.getTime();
    System.out.println("prepare interval:" + temp);

經(jīng)多次采樣測試結(jié)果如下

非prepare和prepare時(shí)間比
useServerPrepStmts=true 0.93
useServerPrepStmts=false 1.01

結(jié)論:

useServerPrepStmts=true時(shí),prepare提升7%;

useServerPrepStmts=false時(shí),prepare與非prepare性能相當(dāng)。

如果將語句簡化為select * from tc_biz_order_0030 where parent_id =?。那么測試的結(jié)論useServerPrepStmts=true時(shí),prepare僅提升2%;sql越簡單硬解析的時(shí)間就越少,prepare的提升就越少。

注意:這個(gè)測試是在單個(gè)連接,單條sql的理想情況下進(jìn)行的,線上會出現(xiàn)多連接多sql,還有sql執(zhí)行頻率,sql的復(fù)雜程度等不同,因此prepare的提升效果會隨具體環(huán)境而變化。

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

 對比可以發(fā)現(xiàn) MYSQLparse lex_one_token在prepare時(shí)已優(yōu)化掉了。

思考

  1 開啟cachePrepStmts的問題,前面談到每個(gè)連接都有一個(gè)緩存,是以sql為唯一標(biāo)識的LRU cache. 在分表較多,大連接的情況下,可能會個(gè)應(yīng)用服務(wù)器帶來內(nèi)存問題。這里有個(gè)前提是ibatis是默認(rèn)使用prepare的。 在mybatis中,標(biāo)簽statementType可以指定某個(gè)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的個(gè)數(shù),減少內(nèi)存消耗。遺憾的是目前集團(tuán)貌似大多使用的是ibatis 2.0版本,不支持statementType
標(biāo)簽。

    2 服務(wù)器端prepare cache是一個(gè)HASH MAP. Key為stmt->id,同時(shí)也是每個(gè)連接都維護(hù)一個(gè)。因此也有可能出現(xiàn)內(nèi)存問題,待實(shí)際測試。如有必要需改造成Key為sql的全局cache,這樣不同連接的相同prepare sql可以共享。 

   3 oracle prepare與mysql prepare的區(qū)別:

     mysql與oracle有一個(gè)重大區(qū)別是mysql沒有oracle那樣的執(zhí)行計(jì)劃緩存。前面我們講到SQL執(zhí)行過程包括以下階段 詞法分析->語法分析->語義分析->執(zhí)行計(jì)劃優(yōu)化->執(zhí)行。oracle的prepare實(shí)際上包括以下階段:詞法分析->語法分析->語義分析->執(zhí)行計(jì)劃優(yōu)化,也就是說oracle的prepare做了更多的事情,execute只需要執(zhí)行即可。因此,oracle的prepare比mysql更高效。

總結(jié)

以上就是本文關(guān)于MySQL prepare原理詳解的全部內(nèi)容,感興趣的朋友可以參閱本站其他相關(guān)專題,有什么問題或者需要的文章或者書籍和源碼可以隨時(shí)留言,小編將竭誠為您解答。感謝大家對本站的支持。

相關(guān)文章

  • mysql如何獲取數(shù)據(jù)列值(int和string)最大值

    mysql如何獲取數(shù)據(jù)列值(int和string)最大值

    最近在開發(fā)項(xiàng)目的時(shí)候有個(gè)需求,我數(shù)據(jù)庫里面存了很多升級包,升級包有列數(shù)據(jù)表示的是升級包的版本號,類型屬于字符串,結(jié)構(gòu)類似于V1.0.2.22這種,然后后臺有個(gè)任務(wù)需要獲取最新版本號的那條數(shù)據(jù),本文給大家介紹mysql獲取數(shù)據(jù)列值(int和string)最大值,感興趣的朋友一起看看吧
    2024-01-01
  • MySQL DQL語句的具體使用

    MySQL DQL語句的具體使用

    本文主要介紹了MySQL DQL語句的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • MySql三種避免重復(fù)插入數(shù)據(jù)的方法

    MySql三種避免重復(fù)插入數(shù)據(jù)的方法

    這篇文章主要介紹了MySql三種避免重復(fù)插入數(shù)據(jù)的方法,幫助大家更好的理解和使用MySQL數(shù)據(jù)庫,感興趣的朋友可以了解下
    2020-09-09
  • 解析MySQL設(shè)置當(dāng)前時(shí)間為默認(rèn)值的方法

    解析MySQL設(shè)置當(dāng)前時(shí)間為默認(rèn)值的方法

    本篇文章是對MySQL設(shè)置當(dāng)前時(shí)間為默認(rèn)值的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • mac下重置mysl8.0.11密碼的方法

    mac下重置mysl8.0.11密碼的方法

    mac下安裝mysql8.0.11時(shí)要求輸入密碼之后想修改密碼。接下來通過本文給大家介紹mac下重置mysl8.0.11密碼的方法,需要的朋友可以參考下
    2018-06-06
  • 如何捕獲和記錄SQL Server中發(fā)生的死鎖

    如何捕獲和記錄SQL Server中發(fā)生的死鎖

    本篇文章是對如何捕獲和記錄SQL Server中發(fā)生的死鎖進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • 新手入門Mysql--sql執(zhí)行過程

    新手入門Mysql--sql執(zhí)行過程

    MySQL 是一種關(guān)系型數(shù)據(jù)庫,在Java企業(yè)級開發(fā)中非常常用,因?yàn)?MySQL 是開源免費(fèi)的,并且方便擴(kuò)展MySQL是開放源代碼的,因此任何人都可以在 GPL的許可下下載并根據(jù)個(gè)性化的需要對其進(jìn)行修改
    2021-06-06
  • CentOS 7 安裝Percona Server+Mysql

    CentOS 7 安裝Percona Server+Mysql

    這篇文章主要介紹了CentOS 7 安裝Percona Server+Mysql的相關(guān)資料,需要的朋友可以參考下
    2018-11-11
  • Fedora環(huán)境下裝MySQL命令方法介紹

    Fedora環(huán)境下裝MySQL命令方法介紹

    剛剛涉及mysql如果有不對的地方,請大家指正,希望自己的一點(diǎn)點(diǎn)經(jīng)驗(yàn),能夠幫到大家
    2012-11-11
  • SQL實(shí)戰(zhàn)演練之網(wǎng)上商城數(shù)據(jù)庫用戶信息數(shù)據(jù)操作

    SQL實(shí)戰(zhàn)演練之網(wǎng)上商城數(shù)據(jù)庫用戶信息數(shù)據(jù)操作

    一直認(rèn)為,扎實(shí)的SQL功底是一名數(shù)據(jù)分析師的安身立命之本,甚至可以稱得上是所有數(shù)據(jù)從業(yè)者的基本功。當(dāng)然,這里的SQL絕不單單是寫幾條查詢語句那么簡單,接下來請跟著小編通過案例項(xiàng)目進(jìn)一步提高SQL的能力吧
    2021-10-10

最新評論