{"id":10008363,"date":"2024-02-18T21:00:27","date_gmt":"2024-02-18T13:00:27","guid":{"rendered":"https:\/\/blog.itmax.cn\/?p=10008363"},"modified":"2024-02-18T21:00:27","modified_gmt":"2024-02-18T13:00:27","slug":"%e8%af%bbtidb%e6%ba%90%e7%a0%81%e8%81%8a%e8%ae%be%e8%ae%a1%ef%bc%9a%e6%b5%85%e6%9e%90htap%e7%9a%84sql%e4%bc%98%e5%8c%96%e5%99%a8","status":"publish","type":"post","link":"https:\/\/itmax.cn\/?p=10008363","title":{"rendered":"\u8bfbTiDB\u6e90\u7801\u804a\u8bbe\u8ba1\uff1a\u6d45\u6790HTAP\u7684SQL\u4f18\u5316\u5668"},"content":{"rendered":"<table>\n<thead>\n<tr>\n<th>\u7248\u672c<\/th>\n<th>\u65e5\u671f<\/th>\n<th>\u5907\u6ce8<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>1.0<\/td>\n<td>2024.2.18<\/td>\n<td>\u6587\u7ae0\u9996\u53d1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<blockquote><p>\u672c\u6587\u7684\u7684\u6e90\u7801\u5206\u6790\u5168\u90e8\u57fa\u4e8eTiDB6.5\u6765\u505a\u5206\u6790\u3002<\/p><\/blockquote>\n<h2>1.\u5f15\u5b50<\/h2>\n<p>\u5982\u679c\u8ba9\u4f60\u505a\u4e00\u4e2a\u5206\u5e03\u5f0f\u6570\u636e\u5e93\u7684\u4f18\u5316\u5668\uff0c\u9762\u5bf9\u4ee5\u4e0b\u7684SQL\uff0c\u4f60\u4f1a\u60f3\u5230\u4ec0\u4e48\u597d\u7684\u65b9\u6cd5\u53bb\u6267\u884c\u4ed6\u4eec\u5462\uff1f<\/p>\n<ul>\n<li><code>SELECT id, name FROM person WHERE age &gt;= 18 or height &gt; 180 limit 100;<\/code>\uff1a\u4ece\u6761\u4ef6\u4e0a\u770b\uff0c\u6211\u4eec\u770b\u5230\u6761\u4ef6\u5176\u5b9e\u662f\u4e8c\u9009\u4e00\u7684\uff1a <code>age &gt;= 18 or height &gt; 180<\/code>\u3002\u57fa\u4e8e\u8fd9\u79cd\u60c5\u51b5\uff0c\u6211\u4eec\u80af\u5b9a\u4f1a\u53bb\u9009\u62e9\u6709\u7d22\u5f15\u7684\u6570\u636e\uff0c\u5982\u679c\u90fd\u6709\u7d22\u5f15or\u90fd\u6ca1\u6709\uff0c\u90a3\u4e48\u80af\u5b9a\u9009\u62e9\u626b\u63cf\u884c\u6570\u6700\u5c11\u7684\u6570\u636e\u3002\u5982\u679c\u6709\u4e00\u4e9b\u7b97\u5b50\u5728\u91cc\u9762\u7684\u8bdd\uff0c\u5219\u989d\u5916\u9700\u8981\u8003\u8651\u6570\u636e\u7684\u5c31\u8fd1\u539f\u5219\u2014\u2014\u6709\u4e9b\u7b97\u5b50\u5728\u90e8\u5206\u67b6\u6784\u4e0b\u53ef\u4ee5\u5145\u5206\u5229\u7528MPP\u7684\u80fd\u529b\uff0c\u800c\u6709\u4e9b\u5219\u4e0d\u884c\u3002<\/li>\n<li><code>SELECT orders.order_id, customers.customer_name, orders.order_date FROM orders LEFT JOIN customers ON orders.customer_id=customers.customer_id;<\/code>\u5206\u5e03\u5f0f\u6570\u636e\u5e93\u4e2d\u7684join\uff0c\u6700\u4f18\u7684\u65b9\u5f0f\u5c31\u662f\u5c0f\u8868\u5e7f\u64ad\u5230\u5927\u8868\u6570\u636e\u6240\u5728\u7684\u5730\u65b9\u3002\u90a3\u4e48\u9996\u5148\u6211\u4eec\u5f97\u77e5\u9053\u8c01\u662f\u5c0f\u8868\uff0c\u8c01\u662f\u5927\u8868\u3002<\/li>\n<\/ul>\n<h2>2.\u7edf\u8ba1\u4fe1\u606f\u6536\u96c6<\/h2>\n<p>\u6839\u636e\u524d\u9762\u7684\u4e24\u4e2a\u4f8b\u5b50\uff0c\u6211\u4eec\u53ef\u4ee5\u53d1\u73b0\u2014\u2014\u5982\u679c\u6211\u4eec\u9700\u8981\u627e\u5230SQL\u5bf9\u5e94\u7684\u6700\u4f73\u8ba1\u5212\uff0c\u6211\u4eec\u4f1a\u9700\u8981\u4e00\u4e9b\u8868\u7684\u5143\u6570\u636e\uff0c\u6216\u8005\u8bf4\u662f\u7edf\u8ba1\u4fe1\u606f\u3002\u4ece\u5e38\u89c4\u7684\u89d2\u5ea6\u6765\u8bf4\uff0c\u4ee5\u4e0b\u7edf\u8ba1\u4fe1\u606f\u662f\u5fc5\u987b\u6536\u96c6\u7684\uff1a<\/p>\n<ul>\n<li>\u8868\u7684\u603b\u884c\u6570<\/li>\n<li>\u6bcf\u5217\u6570\u636e\u7684\u5e73\u5747\u5927\u5c0f<\/li>\n<li>\u6bcf\u5217\u6570\u636e\u7684\u57fa\u6570\uff1a\u5373NDV\uff08distinct value\uff09<\/li>\n<li>\u5217\u7684NULL\u503c\u4e2a\u6570<\/li>\n<\/ul>\n<p>\u5982\u679c\u662f\u4e8b\u52a1\u578b\u7684\uff08\u884c\u5f0f\u5b58\u50a8\uff09\uff0c\u90a3\u4e48\u8fd8\u8981\u8003\u8651\u7d22\u5f15\u5e73\u5747\u957f\u5ea6\u3001\u503c\u7684\u5206\u5e03\u7b49\u7b49\u3002<\/p>\n<p>\u5982\u679c\u662f\u5206\u6790\u578b\u7684\uff08\u5217\u5f0f\u5b58\u50a8\uff09\uff0c\u90a3\u4e48\u8fd8\u9700\u8981\u8003\u8651\u76f8\u5173\u5217\u7684\u6700\u5927\u503c\u3001\u6700\u5c0f\u503c\u7b49\u7b49\u3002<\/p>\n<p>\u800c\u7edf\u8ba1\u65b9\u5f0f\u7684\u6536\u96c6\uff0c\u4f1a\u6709\u591a\u79cd\u65b9\u5f0f\u3002\u4e3b\u8981\u662f\u8003\u8651\u8d44\u6e90\u548c\u51c6\u786e\u6027\u4e4b\u95f4\u7684Trade off\u3002\u5e38\u89c1\u7684\u6709\uff1a<\/p>\n<ul>\n<li>TopN\uff1a\u76f8\u5173\u6570\u636e\u51fa\u73b0\u6b21\u6570\u524d n \u7684\u503c\u3002<\/li>\n<li>\u76f4\u65b9\u56fe\uff1a\u7528\u4e8e\u63cf\u8ff0\u6570\u636e\u5206\u5e03\u56fe\u7684\u5de5\u5177\u3002\u6309\u7167\u6570\u636e\u7684\u503c\u5927\u5c0f\u8fdb\u884c\u5206\u6876\uff0c\u5e76\u7528\u4e00\u4e9b\u7b80\u5355\u7684\u6570\u636e\u6765\u63cf\u8ff0\u6bcf\u4e2a\u6876\uff0c\u6bd4\u5982\u843d\u5728\u6876\u91cc\u7684\u503c\u7684\u4e2a\u6570\u3002<\/li>\n<li>2D\u76f4\u65b9\u56fe\uff1a\u7531\u4e8e\u76f4\u65b9\u56fe\u65e0\u6cd5\u53cd\u5e94\u5217\u4e4b\u95f4\u7684\u5173\u8054\uff0c\u53ef\u4ee5\u75282D\u76f4\u65b9\u56fe\uff08\u8054\u5408\u5206\u5e03\uff09\u505a\u5230\uff0c\u4f46\u7a7a\u95f4\u5360\u7528\u4e5f\u6bd4\u8f83\u591a\u3002<\/li>\n<li>Count-Min Sketch\uff1a\u7c7b\u4f3c\u54c8\u5e0c\u8868\u52a0\u4e0a\u8ba1\u7b97\u5668\u7684\u5b9e\u73b0\u3002\u53ef\u4ee5\u7528\u5f88\u5c11\u7684\u6570\u636e\u6765\u63cf\u8ff0\u5168\u4f53\u6570\u636e\u7684\u7279\u6027\u3002<\/li>\n<li>HyperLogLog\uff1a\u4e00\u79cd\u4f30\u7b97\u6d77\u91cf\u6570\u636e\u57fa\u6570\u7684\u65b9\u6cd5\u3002\u5f88\u591a\u60c5\u51b5\u4e0b\uff0c\u7edf\u8ba1\u5e76\u4e0d\u9700\u8981\u90a3\u4e48\u7cbe\u786e\u3002\u5de5\u7a0b\u65b9\u9762\u8981\u5728\u4f7f\u7528\u8d44\u6e90\u548c\u51c6\u786e\u6027\u91cc\u627e\u5e73\u8861\u3002\u56e0\u6b64\u6709\u4eba\u63d0\u51fa\u7528HLL\uff0c\u8fd9\u662f\u4e00\u79cd\u5360\u7528\u8d44\u6e90\u5c11\uff0c\u4f46\u80fd\u7ed9\u51fa\u8f83\u4e3a\u51c6\u786e\u7684\u8fd1\u4f3c\u7ed3\u679c\u7684\u7b97\u6cd5\u3002<\/li>\n<\/ul>\n<p>TiDB\u6536\u96c6\u7684\u7edf\u8ba1\u4fe1\u606f\u89c1\uff1a<a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=%2Ff1DGAYpDIfxVx9GEahbRQ%3D%3D.Jsvd4caiUcrKGxdwZvpQeVKqoQSyN2Ld62Ys1QKlV1OGHMf9frUIOYVM%2F%2B5VSFw3fL2sc9XwzByVB4416fxgKQA8QM2V1t8Bje3mGksDWwE4LHlHU%2Bvg0u1fj1GtaCta\" rel=\"noopener noreferrer\">https:\/\/docs.pingcap.com\/zh\/tidb\/v6.5\/statistics#%E7%9B%B4%E6&#8230;<\/a><\/p>\n<h2>3.\u4ee3\u4ef7\u7684\u8bc4\u4f30<\/h2>\n<p>\u4e00\u4e2aSQL\u771f\u6b63\u7684\u7269\u7406\u6267\u884c\u8ba1\u5212\u53ef\u80fd\u662f\u6709\u591a\u4e2a\u7684\u3002\u5728\u4ee5\u7edf\u8ba1\u4fe1\u606f\u4e3a\u57fa\u7840\u4e4b\u4e0a\uff0c\u6211\u4eec\u8fd8\u9700\u8981\u52a0\u5165\u76f8\u5e94\u7684\u6743\u91cd\uff0c\u4e3e\u4e2a\u4f8b\u5b50\uff1a<\/p>\n<ol>\n<li>\u5982\u679c\u80fd\u591f\u5728\u5185\u5b58\u4e2d\u8ba1\u7b97\u5b8c\u6210\uff0c\u5c31\u4e0d\u7528\u53bb\u53cd\u590d\u53d1\u8d77\u672c\u5730IO<\/li>\n<li>\u5982\u679c\u80fd\u5728\u672c\u5730IO\u4e2d\u5b8c\u6210\uff0c\u5c31\u4e0d\u8981\u53bb\u53d1\u8d77\u7f51\u7edc\u8bf7\u6c42<\/li>\n<\/ol>\n<p>\u7b49\u7b49&#8230;<\/p>\n<p>\u8fd9\u5728TiDB\u7684\u4ee3\u7801\u4e2d\u4e5f\u6709\u5bf9\u5e94\u7684\u9ed8\u8ba4\u503c\u3002<\/p>\n<pre><code class=\"go\">DefOptCPUFactor                                = 3.0\nDefOptCopCPUFactor                             = 3.0\nDefOptTiFlashConcurrencyFactor                 = 24.0\nDefOptNetworkFactor                            = 1.0\nDefOptScanFactor                               = 1.5\nDefOptDescScanFactor                           = 3.0\nDefOptSeekFactor                               = 20.0\nDefOptMemoryFactor                             = 0.001\nDefOptDiskFactor                               = 1.5\nDefOptConcurrencyFactor                        = 3.0<\/code><\/pre>\n<pre><code class=\"go\">var defaultVer2Factors = costVer2Factors{\n    TiDBTemp:      costVer2Factor{&quot;tidb_temp_table_factor&quot;, 0.00},\n    TiKVScan:      costVer2Factor{&quot;tikv_scan_factor&quot;, 40.70},\n    TiKVDescScan:  costVer2Factor{&quot;tikv_desc_scan_factor&quot;, 61.05},\n    TiFlashScan:   costVer2Factor{&quot;tiflash_scan_factor&quot;, 11.60},\n    TiDBCPU:       costVer2Factor{&quot;tidb_cpu_factor&quot;, 49.90},\n    TiKVCPU:       costVer2Factor{&quot;tikv_cpu_factor&quot;, 49.90},\n    TiFlashCPU:    costVer2Factor{&quot;tiflash_cpu_factor&quot;, 2.40},\n    TiDB2KVNet:    costVer2Factor{&quot;tidb_kv_net_factor&quot;, 3.96},\n    TiDB2FlashNet: costVer2Factor{&quot;tidb_flash_net_factor&quot;, 2.20},\n    TiFlashMPPNet: costVer2Factor{&quot;tiflash_mpp_net_factor&quot;, 1.00},\n    TiDBMem:       costVer2Factor{&quot;tidb_mem_factor&quot;, 0.20},\n    TiKVMem:       costVer2Factor{&quot;tikv_mem_factor&quot;, 0.20},\n    TiFlashMem:    costVer2Factor{&quot;tiflash_mem_factor&quot;, 0.05},\n    TiDBDisk:      costVer2Factor{&quot;tidb_disk_factor&quot;, 200.00},\n    TiDBRequest:   costVer2Factor{&quot;tidb_request_factor&quot;, 6000000.00},\n}<\/code><\/pre>\n<h2>4.\u6267\u884c\u8ba1\u5212\u679a\u4e3e\u4e0e\u62e9\u4f18<\/h2>\n<p>\u5f53\u6211\u4eec\u53ef\u4ee5\u8bc4\u4f30\u51fa\u7269\u7406\u6267\u884c\u8ba1\u5212\u7684\u4ee3\u4ef7\u65f6\uff0c\u5c06\u4f1a\u679a\u4e3e\u6240\u6709\u53ef\u4ee5\u7528\u4e0a\u7269\u7406\u6267\u884c\u8ba1\u5212\uff0c\u5e76\u5728\u5176\u4e2d\u9009\u62e9\u4ee3\u4ef7\u6700\u5c0f\u7684\u7269\u7406\u6267\u884c\u8ba1\u5212\u3002\u4e00\u822c\u679a\u4e3e\u5206\u4e3a\u4e24\u4e2a\u6d41\u6d3e\uff1a<\/p>\n<ol>\n<li>\u81ea\u5e95\u5411\u4e0a\uff1a\u4ee3\u8868\u6709System R\u3002<\/li>\n<li>\u81ea\u9876\u5411\u4e0b\uff1a\u4ee3\u8868\u6709Cascade\u3002<\/li>\n<\/ol>\n<p>\u81ea\u5e95\u5411\u4e0a\u6ca1\u6cd5\u89e3\u51b3\u4e00\u4e2a\u95ee\u9898\u3002\u5f53\u4e0a\u5c42\u5bf9\u4e0b\u5c42\u7684\u8f93\u51fa\u7ed3\u679c\u987a\u5e8f\u611f\u5174\u8da3\u65f6\uff0c\u90a3\u5c31\u4e0d\u80fd\u53ea\u80fd\u4ece\u5e95\u5c42\u7684\u89c6\u89d2\u6765\u5bfb\u627e\u5c40\u90e8\u6700\u4f18\u3002<\/p>\n<p>\u4e3e\u4e2a\u4f8b\u5b50\uff0c\u591a\u8868Join\u3002\u4e00\u5f00\u59cb\u4e24\u4e2a\u8868Join\u53ef\u80fdHashJoin\u4ee3\u4ef7\u5f88\u4f4e\uff0c\u4f46\u662fJoin\u4e0b\u4e00\u4e2a\u8868\u65f6\uff0c\u7528MergeJoin\u4ece\u6574\u4f53\u6765\u770b\u4ee3\u4ef7\u66f4\u4f4e\u3002\u4ece\u8fd9\u4e2acase\u6765\u770b\uff0c\u81ea\u5e95\u5411\u4e0a\u6765\u505a\u8ba1\u5212\u53d6\u4f18\u5e76\u4e0d\u5408\u9002\u3002<\/p>\n<p>\u6240\u4ee5\u6709\u4e86Cascade\uff1a<\/p>\n<ol>\n<li>\u641c\u7d22\u65b9\u6848\u662f\u81ea\u9876\u5411\u4e0b\u7684\u3002\u8fd9\u610f\u5473\u7740\u5b83\u53ef\u4ee5\u907f\u514d\u5c40\u90e8\u6700\u4f18\u800c\u5bfc\u81f4\u5168\u5c40\u4e0d\u4f18\u3002\u4eceOperator Tree \u81ea\u9876\u5411\u4e0b\u904d\u5386\u65f6\uff0c\u53ef\u4ee5\u505a\u4e00\u7cfb\u5217\u53d8\u6362\uff1a<\/li>\n<\/ol>\n<ul>\n<li>\n<ul>\n<li>Implementation\uff1a\u903b\u8f91\u7b97\u5b50\u53ef\u4ee5\u8f6c\u6362\u6210\u7269\u7406\u7b97\u5b50\uff1b\u4f8b\u5982Join\u8f6c\u6362\u6210NestLoop\u6216\u8005HashJoin\u7b49<\/li>\n<li>Exploration\uff1a\u903b\u8f91\u7b97\u5b50\u53ef\u4ee5\u505a\u7b49\u4ef7\u53d8\u6362\uff1b\u4f8b\u5982\u4ea4\u6362Inner Join\u7684\u4e24\u4e2a\u5b50\u8282\u70b9\uff0c\u5373\u53ef\u679a\u4e3eJoin\u987a\u5e8f<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img decoding=\"async\"   referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000044635800\" alt=\"\" title=\"\"><\/p>\n<p>\u56fe\u7247\u6765\u81ea\u4e8e\uff1aCascades Optimizer\u2014\u2014<a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=nec6IT42fe5NDemGLm4i8A%3D%3D.Pgxdia0FwwJHQJXvyxqvkpC7D1fPn%2B2C6q4X%2BtvF%2FX61IESvJEWGD8jtnW4tLec8\" rel=\"noopener noreferrer\">https:\/\/zhuanlan.zhihu.com\/p\/73545345<\/a><\/p>\n<ol start=\"2\">\n<li>\u5b83\u662f\u57fa\u4e8eVolcano\u6a21\u578b\u6f14\u8fdb\u800c\u6765\u7684\u3002<\/li>\n<li>\u7528\u9762\u5411\u5bf9\u8c61\u7684\u65b9\u5f0f\u8fdb\u884c\u5efa\u6a21\uff0c\u7f16\u5199\u89c4\u5219\u65f6\u4e0d\u9700\u8981\u5173\u5fc3\u641c\u7d22\u8fc7\u7a0b\u3002\u76f8\u6bd4\u4f20\u7edf\u4f18\u5316\u5668\u4e2d\u9762\u5411\u8fc7\u7a0b\u53bb\u4e00\u6761\u6761\u7684\u7f16\u5199\uff0c\u7684\u786e\u662f\u5f88\u5927\u7684\u6539\u8fdb\u3002<\/li>\n<\/ol>\n<h2>5.TiDB\u7684\u5b9e\u73b0<\/h2>\n<p>\u5927\u81f4\u7684\u4ee3\u7801\u8c03\u7528\u94fe\u4e3a\uff1a<\/p>\n<pre><code class=\"log\">-- session\/session.go\n\n\/-- ExecuteStmt \/\/SQL\u6267\u884c\u7684\u5165\u53e3\n\n|-- executor\/compiler.go\n\n\/-- Compile \/\/\u5c06SQL\u53d8\u6210\u53ef\u6267\u884c\u7684\u8ba1\u5212\n\n|--planner\/planner\/optmize.go\n\n\/-- Optimize \/\/\u4f18\u5316\u7684\u5165\u53e3\n\n\/-- optimize \/\/\u8fd9\u91cc\u6709\u4e24\u4e2a\u5165\u53e3\u3002\u4e00\u79cd\u662f\u65b0\u7684\u4f18\u5316\u5668\u5165\u53e3\uff0c\u4e00\u79cd\u662f\u8001\u7684\u4f18\u5316\u5668\u5165\u53e3\u3002\u6839\u636e\u914d\u7f6e\u6765\u9009\u62e9\u3002\u6700\u7ec8\u90fd\u4f1a\u8fd4\u56de\u6700\u4f18\u7684\u7269\u7406\u6267\u884c\u8ba1\u5212\u3002\n\n    |-- planner\/cascades\/optmize.go\n\n        \/--FindBestPlan \u89c15.1\n    \n        \/-- onPhasePreprocessing \/\/\u89c15.3\n\n        \/-- implGroup\n\n        |--planner\/core\/optmizer.go \/\/\u89c15.4\n\n            \/-- DoOptimize\n\n            \/-- physicalOptimize\n\n            |--planner\/core\/find_best_task.go\n\n            \/-- findBestTask\n\n            \/-- enumeratePhysicalPlans4Task\n\n            \/-- compareTaskCost\n    \n            \/-- getTaskPlanCost\n    \n            |-- planner\/core\/plan_cost_ver2.go\n\n            \/-- getPlanCost<\/code><\/pre>\n<h3>5.1 \u903b\u8f91\u4f18\u5316<\/h3>\n<p>\u6838\u5fc3\u5165\u53e3\u4e3a\uff1a<\/p>\n<pre><code class=\"go\">\n\/\/ FindBestPlan is the optimization entrance of the cascades planner. The\n\/\/ optimization is composed of 3 phases: preprocessing, exploration and implementation.\n\/\/\n\/\/ ------------------------------------------------------------------------------\n\/\/ Phase 1: Preprocessing\n\/\/ ------------------------------------------------------------------------------\n\/\/\n\/\/ The target of this phase is to preprocess the plan tree by some heuristic\n\/\/ rules which should always be beneficial, for example Column Pruning.\n\/\/\n\/\/ ------------------------------------------------------------------------------\n\/\/ Phase 2: Exploration\n\/\/ ------------------------------------------------------------------------------\n\/\/\n\/\/ The target of this phase is to explore all the logically equivalent\n\/\/ expressions by exploring all the equivalent group expressions of each group.\n\/\/\n\/\/ At the very beginning, there is only one group expression in a Group. After\n\/\/ applying some transformation rules on certain expressions of the Group, all\n\/\/ the equivalent expressions are found and stored in the Group. This procedure\n\/\/ can be regarded as searching for a weak connected component in a directed\n\/\/ graph, where nodes are expressions and directed edges are the transformation\n\/\/ rules.\n\/\/\n\/\/ ------------------------------------------------------------------------------\n\/\/ Phase 3: Implementation\n\/\/ ------------------------------------------------------------------------------\n\/\/\n\/\/ The target of this phase is to search the best physical plan for a Group\n\/\/ which satisfies a certain required physical property.\n\/\/\n\/\/ In this phase, we need to enumerate all the applicable implementation rules\n\/\/ for each expression in each group under the required physical property. A\n\/\/ memo structure is used for a group to reduce the repeated search on the same\n\/\/ required physical property.\nfunc (opt *Optimizer) FindBestPlan(sctx sessionctx.Context, logical plannercore.LogicalPlan) (p plannercore.PhysicalPlan, cost float64, err error) {\n    logical, err = opt.onPhasePreprocessing(sctx, logical)\n    if err != nil {\n        return nil, 0, err\n    }\n    rootGroup := memo.Convert2Group(logical)\n    err = opt.onPhaseExploration(sctx, rootGroup)\n    if err != nil {\n        return nil, 0, err\n    }\n    p, cost, err = opt.onPhaseImplementation(sctx, rootGroup)\n    if err != nil {\n        return nil, 0, err\n    }\n    err = p.ResolveIndices()\n    return p, cost, err\n}<\/code><\/pre>\n<p>\u6ce8\u91ca+\u4ee3\u7801\u5f88\u5e72\u51c0\uff0c\u8fd9\u91cc\u4e00\u5171\u505a\u4e09\u4ef6\u4e8b<\/p>\n<ol>\n<li>onPhasePreprocessing\uff1a\u6ce8\u91ca\u5f88\u5b9e\u5728\uff0c\u8bf4<code>for example Column Pruning<\/code>\uff0c\u7ed3\u679c\u91cc\u9762\u771f\u7684\u53ea\u505a\u4e86\u5217\u88c1\u526a\u3002<\/li>\n<li>onPhaseExploration\uff1a\u63a2\u7d22\u6240\u6709\u903b\u8f91\u7b49\u4ef7\u5b58\u5728\u7684\u53ef\u80fd<\/li>\n<li>onPhaseImplementation\uff1a\u6839\u636e\u4ee3\u4ef7\u5bfb\u627e\u6700\u4f18\u7684\u7269\u7406\u6267\u884c\u8ba1\u5212<\/li>\n<\/ol>\n<p>\u8fd9\u5757\u5b98\u7f51\u7684\u535a\u5ba2\u5199\u7684\u975e\u5e38\u597d\uff0c\u6211\u5c31\u4e0d\u91cd\u590d\u4e86\uff1a<a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=4kFmhZXcDpBjJfYAB5CejQ%3D%3D.svw4tTcDgXc37bNHRx%2F8Vhb0B%2Bp0e36NLejyRwx5KeR7aq%2BTovS3B00Xl44sz575tLX9SjW5F%2FF3oLkdMUsdmg%3D%3D\" rel=\"noopener noreferrer\">https:\/\/cn.pingcap.com\/blog\/tidb-cascades-planner\/<\/a><\/p>\n<h3>5.2 \u7edf\u8ba1\u4fe1\u606f\u6536\u96c6<\/h3>\n<p>\u8fd9\u5757\u4ee3\u7801\u4e3b\u8981\u96c6\u4e2d\u5728stats.go\u91cc\uff0c\u91cc\u9762\u7684\u6838\u5fc3\u6570\u636e\u7ed3\u6784\u662fstats_info.go\u91cc\u7684StatsInfo\u3002\u8c03\u7528\u94fe\u5927\u81f4\u4e3a\uff1a<\/p>\n<pre><code class=\"log\">|-- planner\/cascades\/optimizer.go\n\n\/--fillGroupStats\n\n|-- planner\/core\/stats.go\n\n\/--DeriveStats<\/code><\/pre>\n<pre><code class=\"go\">type LogicalPlan interface {\n    Plan\n    \/\/......\u5ffd\u7565\u4e00\u4e9b\u4ee3\u7801\n    \/\/ DeriveStats derives statistic info for current plan node given child stats.\n    \/\/ We need selfSchema, childSchema here because it makes this method can be used in\n    \/\/ cascades planner, where LogicalPlan might not record its children or schema.\n    DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error)\n    \/\/......\u5ffd\u7565\u4e00\u4e9b\u4ee3\u7801\n}<\/code><\/pre>\n<p>\u6709\u5f88\u591a\u7ed3\u6784\u4f53\u5b9e\u73b0\u4e86\u8fd9\u4e2a\u65b9\u6cd5\uff1a<\/p>\n<p><img decoding=\"async\"   referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000044635801\" alt=\"\" title=\"\" loading=\"lazy\"><\/p>\n<ol start=\"3\">\n<li>\u6536\u96c6\u7edf\u8ba1\u4fe1\u606f\u4e3b\u8981\u662fanalyze.go#Next\u65b9\u6cd5\u4e2d\u8c03\u7528\u7684#analyzeWorker\u3002<\/li>\n<\/ol>\n<h3>5.3 \u65b0\u7248\u672c \u7269\u7406\u6267\u884c\u8ba1\u5212\u7684\u9009\u62e9<\/h3>\n<p>\u4ee3\u7801\u5165\u53e3\u4e3a\uff1a<\/p>\n<pre><code class=\"go\">\/\/ implGroup finds the best Implementation which satisfies the required\n\/\/ physical property for a Group. The best Implementation should have the\n\/\/ lowest cost among all the applicable Implementations.\n\/\/\n\/\/ g:            the Group to be implemented.\n\/\/ reqPhysProp: the required physical property.\n\/\/ costLimit:   the maximum cost of all the Implementations.\nfunc (opt *Optimizer) implGroup(g *memo.Group, reqPhysProp *property.PhysicalProperty, costLimit float64) (memo.Implementation, error) {\n    groupImpl := g.GetImpl(reqPhysProp)\n    if groupImpl != nil {\n        if groupImpl.GetCost() &lt;= costLimit {\n            return groupImpl, nil\n        }\n        return nil, nil\n    }\n    \/\/ Handle implementation rules for each equivalent GroupExpr.\n    var childImpls []memo.Implementation\n    err := opt.fillGroupStats(g)\n    if err != nil {\n        return nil, err\n    }\n    outCount := math.Min(g.Prop.Stats.RowCount, reqPhysProp.ExpectedCnt)\n    for elem := g.Equivalents.Front(); elem != nil; elem = elem.Next() {\n        curExpr := elem.Value.(*memo.GroupExpr)\n        impls, err := opt.implGroupExpr(curExpr, reqPhysProp)\n        if err != nil {\n            return nil, err\n        }\n        for _, impl := range impls {\n            childImpls = childImpls[:0]\n            for i, childGroup := range curExpr.Children {\n                childImpl, err := opt.implGroup(childGroup, impl.GetPlan().GetChildReqProps(i), impl.GetCostLimit(costLimit, childImpls...))\n                if err != nil {\n                    return nil, err\n                }\n                if childImpl == nil {\n                    impl.SetCost(math.MaxFloat64)\n                    break\n                }\n                childImpls = append(childImpls, childImpl)\n            }\n            if impl.GetCost() == math.MaxFloat64 {\n                continue\n            }\n            implCost := impl.CalcCost(outCount, childImpls...)\n            if implCost &gt; costLimit {\n                continue\n            }\n            if groupImpl == nil || groupImpl.GetCost() &gt; implCost {\n                groupImpl = impl.AttachChildren(childImpls...)\n                costLimit = implCost\n            }\n        }\n    }\n    \/\/ Handle enforcer rules for required physical property.\n    for _, rule := range GetEnforcerRules(g, reqPhysProp) {\n        newReqPhysProp := rule.NewProperty(reqPhysProp)\n        enforceCost := rule.GetEnforceCost(g)\n        childImpl, err := opt.implGroup(g, newReqPhysProp, costLimit-enforceCost)\n        if err != nil {\n            return nil, err\n        }\n        if childImpl == nil {\n            continue\n        }\n        impl := rule.OnEnforce(reqPhysProp, childImpl)\n        implCost := enforceCost + childImpl.GetCost()\n        impl.SetCost(implCost)\n        if groupImpl == nil || groupImpl.GetCost() &gt; implCost {\n            groupImpl = impl\n            costLimit = implCost\n        }\n    }\n    if groupImpl == nil || groupImpl.GetCost() == math.MaxFloat64 {\n        return nil, nil\n    }\n    g.InsertImpl(reqPhysProp, groupImpl)\n    return groupImpl, nil\n}<\/code><\/pre>\n<p>\u8fd9\u91cc\u4e2a\u51fd\u6570\u4f1a\u627e\u51fa\u6f5c\u5728\u7b26\u5408\u6761\u4ef6\u7684\u7269\u7406\u6267\u884c\u8ba1\u5212\uff0c\u5e76\u4e0d\u65ad\u7684\u641c\u7d22\u3002\u4f46\u5b83\u6709\u4e00\u4e2a\u526a\u679d\u903b\u8f91\u2014\u2014\u4f1a\u8bb0\u5f55\u5f53\u524d\u6700\u5c0f\u7684cost\uff0c\u5982\u679c\u4e00\u4e2a\u6267\u884c\u8ba1\u5212\u81ea\u4e0a\u5411\u4e0b\u641c\u7d22\u65f6\uff0c\u5982\u679c\u8d85\u8fc7\u4e86\u8fd9\u4e2acost\uff0c\u5219\u76f4\u63a5\u8fd4\u56de\u3002\u8fd9\u5c31\u662f\u5728\u7b2c3\u8282\u63d0\u5230\u7684\u81ea\u9876\u5411\u4e0b\u7684\u4f18\u5316\u3002<\/p>\n<p>\u63a5\u4e0b\u6765\u6211\u4eec\u770b\u4e00\u4e0b<code>memo.Implementation<\/code>\u7684\u5b9a\u4e49\uff1a<\/p>\n<pre><code class=\"go\">\npackage memo\n\nimport (\n    plannercore &quot;github.com\/pingcap\/tidb\/planner\/core&quot;\n)\n\n\/\/ Implementation defines the interface for cost of physical plan.\ntype Implementation interface {\n    CalcCost(outCount float64, children ...Implementation) float64\n    SetCost(cost float64)\n    GetCost() float64\n    GetPlan() plannercore.PhysicalPlan\n\n    \/\/ AttachChildren is used to attach children implementations and returns it self.\n    AttachChildren(children ...Implementation) Implementation\n\n    \/\/ GetCostLimit gets the costLimit for implementing the next childGroup.\n    GetCostLimit(costLimit float64, children ...Implementation) float64\n}<\/code><\/pre>\n<p>\u5176\u4e2d<code>CalcCost<\/code>\u65b9\u6cd5\u5c31\u662f\u7528\u6765\u8ba1\u7b97\u7269\u7406\u6267\u884c\u8ba1\u5212\u7684\u4ee3\u4ef7\u3002\u4e00\u5171\u6709\u8fd9\u4e48\u591a\u7ed3\u6784\u4f53\u5b9e\u73b0\u4e86\u5b83\uff1a<\/p>\n<p><img decoding=\"async\"   referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000044635802\" alt=\"\" title=\"\" loading=\"lazy\"><\/p>\n<h4>5.3.1 \u4ee3\u4ef7\u7684\u8bc4\u4f30<\/h4>\n<p>\u6211\u4eec\u4ee5\u5f00\u5934\u7684\u4f8b\u5b50\uff0c\u8bb2\u89e3\u4ee3\u4ef7\u7684\u8bc4\u4f30\u3002<\/p>\n<h5>select\u4ee3\u4ef7\u8ba1\u7b97\u65b9\u5f0f<\/h5>\n<pre><code class=\"go\">\/\/ CalcCost calculates the cost of the table scan Implementation.\nfunc (impl *TableScanImpl) CalcCost(outCount float64, _ ...memo.Implementation) float64 {\n    ts := impl.plan.(*plannercore.PhysicalTableScan)\n    width := impl.tblColHists.GetTableAvgRowSize(impl.plan.SCtx(), impl.tblCols, kv.TiKV, true)\n    sessVars := ts.SCtx().GetSessionVars()\n    impl.cost = outCount * sessVars.GetScanFactor(ts.Table) * width\n    if ts.Desc {\n        impl.cost = outCount * sessVars.GetDescScanFactor(ts.Table) * width\n    }\n    return impl.cost\n}\n\n\/\/ GetScanFactor returns the session variable scanFactor\n\/\/ returns 0 when tbl is a temporary table.\nfunc (s *SessionVars) GetScanFactor(tbl *model.TableInfo) float64 {\n    if tbl != nil {\n        if tbl.TempTableType != model.TempTableNone {\n            return 0\n        }\n    }\n    return s.scanFactor \n}\n\n\n\/\/ CalcCost implements Implementation interface.\nfunc (impl *IndexScanImpl) CalcCost(outCount float64, _ ...memo.Implementation) float64 {\n    is := impl.plan.(*plannercore.PhysicalIndexScan)\n    sessVars := is.SCtx().GetSessionVars()\n    rowSize := impl.tblColHists.GetIndexAvgRowSize(is.SCtx(), is.Schema().Columns, is.Index.Unique)\n    cost := outCount * rowSize * sessVars.GetScanFactor(is.Table)\n    if is.Desc {\n        cost = outCount * rowSize * sessVars.GetDescScanFactor(is.Table)\n    }\n    cost += float64(len(is.Ranges)) * sessVars.GetSeekFactor(is.Table)\n    impl.cost = cost\n    return impl.cost\n}<\/code><\/pre>\n<p>\u8fd9\u91cc\u6211\u4eec\u4ee5\u5168\u8868\u626b\u63cf\u8868\u548c\u547d\u4e2d\u7d22\u5f15\u7684\u4ee3\u7801\u4e3a\u4f8b\u5b50\u3002\u5176\u4e2d\u9ed8\u8ba4\u7684scanFactor\u662f1.5\u3002\u8fd9\u91cc\u53ef\u4ee5\u770b\u5230indexScan\u548ctableScan\u5c11\u4e86\u4e00\u4e2a\u56e0\u6570\uff1awidth\u3002\u8fd9\u4e2a\u53d8\u91cf\u4ee3\u8868\u4e86\u6240\u9700\u5217\u7684\u5e73\u5747\u5927\u5c0f\u3002\u8fd9\u4e48\u770b\u57fa\u672c\u4e0a\u662findexScan\u6700\u4f18\u4e86\u3002<\/p>\n<p>\u8fd9\u91cc\u7684\u4ee3\u7801\u7b14\u8005\u8ba4\u4e3a\u6709\u70b9\u4e0d\u4f18\u96c5\u2014\u2014\u5f53Desc\u65f6\uff0c\u5176\u5b9e\u4e4b\u524d\u7684Cost\u662f\u6ca1\u5fc5\u8981\u7b97\u4e00\u904d\u7684\uff0c\u6d6a\u8d39CPU\u8d44\u6e90\u3002<\/p>\n<h5>join\u4ee3\u4ef7\u8ba1\u7b97\u65b9\u5f0f<\/h5>\n<pre><code class=\"go\">\/\/ CalcCost implements Implementation CalcCost interface.\nfunc (impl *HashJoinImpl) CalcCost(_ float64, children ...memo.Implementation) float64 {\n    hashJoin := impl.plan.(*plannercore.PhysicalHashJoin)\n    \/\/ The children here are only used to calculate the cost.\n    hashJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan())\n    selfCost := hashJoin.GetCost(children[0].GetPlan().StatsCount(), children[1].GetPlan().StatsCount(), false, 0, nil)\n    impl.cost = selfCost + children[0].GetCost() + children[1].GetCost()\n    return impl.cost\n}\n\n\/\/ CalcCost implements Implementation CalcCost interface.\nfunc (impl *MergeJoinImpl) CalcCost(_ float64, children ...memo.Implementation) float64 {\n    mergeJoin := impl.plan.(*plannercore.PhysicalMergeJoin)\n    \/\/ The children here are only used to calculate the cost.\n    mergeJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan())\n    selfCost := mergeJoin.GetCost(children[0].GetPlan().StatsCount(), children[1].GetPlan().StatsCount(), 0)\n    impl.cost = selfCost + children[0].GetCost() + children[1].GetCost()\n    return impl.cost\n}<\/code><\/pre>\n<p>\u5177\u4f53\u7684\u8ba1\u7b97\u90fd\u5728plan_cost_v1.go\u91cc\uff1a<\/p>\n<pre><code class=\"go\">\n\/\/ GetCost computes cost of hash join operator itself.\nfunc (p *PhysicalHashJoin) GetCost(lCnt, rCnt float64, isMPP bool, costFlag uint64, op *physicalOptimizeOp) float64 {\n    buildCnt, probeCnt := lCnt, rCnt\n    build := p.children[0]\n    \/\/ Taking the right as the inner for right join or using the outer to build a hash table.\n    if (p.InnerChildIdx == 1 &amp;&amp; !p.UseOuterToBuild) || (p.InnerChildIdx == 0 &amp;&amp; p.UseOuterToBuild) {\n        buildCnt, probeCnt = rCnt, lCnt\n        build = p.children[1]\n    }\n    sessVars := p.ctx.GetSessionVars()\n    oomUseTmpStorage := variable.EnableTmpStorageOnOOM.Load()\n    memQuota := sessVars.MemTracker.GetBytesLimit() \/\/ sessVars.MemQuotaQuery &amp;&amp; hint\n    rowSize := getAvgRowSize(build.statsInfo(), build.Schema().Columns)\n    spill := oomUseTmpStorage &amp;&amp; memQuota &gt; 0 &amp;&amp; rowSize*buildCnt &gt; float64(memQuota) &amp;&amp; p.storeTp != kv.TiFlash\n    \/\/ Cost of building hash table.\n    cpuFactor := sessVars.GetCPUFactor()\n    diskFactor := sessVars.GetDiskFactor()\n    memoryFactor := sessVars.GetMemoryFactor()\n    concurrencyFactor := sessVars.GetConcurrencyFactor()\n\n    cpuCost := buildCnt * cpuFactor\n    memoryCost := buildCnt * memoryFactor\n    diskCost := buildCnt * diskFactor * rowSize\n    \/\/ Number of matched row pairs regarding the equal join conditions.\n    helper := &amp;fullJoinRowCountHelper{\n        sctx:            p.SCtx(),\n        cartesian:       false,\n        leftProfile:     p.children[0].statsInfo(),\n        rightProfile:    p.children[1].statsInfo(),\n        leftJoinKeys:    p.LeftJoinKeys,\n        rightJoinKeys:   p.RightJoinKeys,\n        leftSchema:      p.children[0].Schema(),\n        rightSchema:     p.children[1].Schema(),\n        leftNAJoinKeys:  p.LeftNAJoinKeys,\n        rightNAJoinKeys: p.RightNAJoinKeys,\n    }\n    numPairs := helper.estimate()\n    \/\/ For semi-join class, if `OtherConditions` is empty, we already know\n    \/\/ the join results after querying hash table, otherwise, we have to\n    \/\/ evaluate those resulted row pairs after querying hash table; if we\n    \/\/ find one pair satisfying the `OtherConditions`, we then know the\n    \/\/ join result for this given outer row, otherwise we have to iterate\n    \/\/ to the end of those pairs; since we have no idea about when we can\n    \/\/ terminate the iteration, we assume that we need to iterate half of\n    \/\/ those pairs in average.\n    if p.JoinType == SemiJoin || p.JoinType == AntiSemiJoin ||\n        p.JoinType == LeftOuterSemiJoin || p.JoinType == AntiLeftOuterSemiJoin {\n        if len(p.OtherConditions) &gt; 0 {\n            numPairs *= 0.5\n        } else {\n            numPairs = 0\n        }\n    }\n    if hasCostFlag(costFlag, CostFlagUseTrueCardinality) {\n        numPairs = getOperatorActRows(p)\n    }\n    \/\/ Cost of querying hash table is cheap actually, so we just compute the cost of\n    \/\/ evaluating `OtherConditions` and joining row pairs.\n    probeCost := numPairs * cpuFactor\n    probeDiskCost := numPairs * diskFactor * rowSize\n    \/\/ Cost of evaluating outer filter.\n    if len(p.LeftConditions)+len(p.RightConditions) &gt; 0 {\n        \/\/ Input outer count for the above compution should be adjusted by SelectionFactor.\n        probeCost *= SelectionFactor\n        probeDiskCost *= SelectionFactor\n        probeCost += probeCnt * cpuFactor\n    }\n    diskCost += probeDiskCost\n    probeCost \/= float64(p.Concurrency)\n    \/\/ Cost of additional concurrent goroutines.\n    cpuCost += probeCost + float64(p.Concurrency+1)*concurrencyFactor\n    \/\/ Cost of traveling the hash table to resolve missing matched cases when building the hash table from the outer table\n    if p.UseOuterToBuild {\n        if spill {\n            \/\/ It runs in sequence when build data is on disk. See handleUnmatchedRowsFromHashTableInDisk\n            cpuCost += buildCnt * cpuFactor\n        } else {\n            cpuCost += buildCnt * cpuFactor \/ float64(p.Concurrency)\n        }\n        diskCost += buildCnt * diskFactor * rowSize\n    }\n\n    if spill {\n        memoryCost *= float64(memQuota) \/ (rowSize * buildCnt)\n    } else {\n        diskCost = 0\n    }\n    if op != nil {\n        setPhysicalHashJoinCostDetail(p, op, spill, buildCnt, probeCnt, cpuFactor, rowSize, numPairs,\n            cpuCost, probeCost, memoryCost, diskCost, probeDiskCost,\n            diskFactor, memoryFactor, concurrencyFactor,\n            memQuota)\n    }\n    return cpuCost + memoryCost + diskCost\n}\n\n\n\/\/ GetCost computes cost of merge join operator itself.\nfunc (p *PhysicalMergeJoin) GetCost(lCnt, rCnt float64, costFlag uint64) float64 {\n    outerCnt := lCnt\n    innerCnt := rCnt\n    innerKeys := p.RightJoinKeys\n    innerSchema := p.children[1].Schema()\n    innerStats := p.children[1].statsInfo()\n    if p.JoinType == RightOuterJoin {\n        outerCnt = rCnt\n        innerCnt = lCnt\n        innerKeys = p.LeftJoinKeys\n        innerSchema = p.children[0].Schema()\n        innerStats = p.children[0].statsInfo()\n    }\n    helper := &amp;fullJoinRowCountHelper{\n        sctx:          p.SCtx(),\n        cartesian:     false,\n        leftProfile:   p.children[0].statsInfo(),\n        rightProfile:  p.children[1].statsInfo(),\n        leftJoinKeys:  p.LeftJoinKeys,\n        rightJoinKeys: p.RightJoinKeys,\n        leftSchema:    p.children[0].Schema(),\n        rightSchema:   p.children[1].Schema(),\n    }\n    numPairs := helper.estimate()\n    if p.JoinType == SemiJoin || p.JoinType == AntiSemiJoin ||\n        p.JoinType == LeftOuterSemiJoin || p.JoinType == AntiLeftOuterSemiJoin {\n        if len(p.OtherConditions) &gt; 0 {\n            numPairs *= 0.5\n        } else {\n            numPairs = 0\n        }\n    }\n    if hasCostFlag(costFlag, CostFlagUseTrueCardinality) {\n        numPairs = getOperatorActRows(p)\n    }\n    sessVars := p.ctx.GetSessionVars()\n    probeCost := numPairs * sessVars.GetCPUFactor()\n    \/\/ Cost of evaluating outer filters.\n    var cpuCost float64\n    if len(p.LeftConditions)+len(p.RightConditions) &gt; 0 {\n        probeCost *= SelectionFactor\n        cpuCost += outerCnt * sessVars.GetCPUFactor()\n    }\n    cpuCost += probeCost\n    \/\/ For merge join, only one group of rows with same join key(not null) are cached,\n    \/\/ we compute average memory cost using estimated group size.\n    NDV, _ := getColsNDVWithMatchedLen(innerKeys, innerSchema, innerStats)\n    memoryCost := (innerCnt \/ NDV) * sessVars.GetMemoryFactor()\n    return cpuCost + memoryCost\n}<\/code><\/pre>\n<p>HashJoin\u8981\u8003\u8651\u5230\u5185\u5b58\u4e0d\u591f\u7684\u60c5\u51b5\uff0c\u56e0\u6b64\u5728\u8ba1\u7b97\u5230\u6570\u636e\u4e0d\u591f\u65f6\uff0c\u4f1a\u5c06\u5bf9\u5e94\u7684\u6570\u636e\u538b\u5165\u786c\u76d8\u3002\u4f46\u5b83\u5bf9\u6570\u636e\u7684\u987a\u5e8f\u5e76\u65e0\u8981\u6c42\uff0c\u56e0\u6b64\u53ef\u4ee5\u5e76\u53d1\u7684\u53bb\u505a\u3002\u89c1\uff1a<\/p>\n<pre><code class=\"go\">    \/\/ Cost of traveling the hash table to resolve missing matched cases when building the hash table from the outer table\n    if p.UseOuterToBuild {\n        if spill {\n            \/\/ It runs in sequence when build data is on disk. See handleUnmatchedRowsFromHashTableInDisk\n            cpuCost += buildCnt * cpuFactor\n        } else {\n            cpuCost += buildCnt * cpuFactor \/ float64(p.Concurrency)\n        }\n        diskCost += buildCnt * diskFactor * rowSize\n    }<\/code><\/pre>\n<p>\u800cMergeJoin\u7684\u4ee3\u4ef7\u663e\u7136\u4f1a\u66f4\u5c0f\uff0c\u4f46\u80fd\u591f\u9009\u5219\u5230\u8fd9\u4e2a\u8ba1\u5212\u4e5f\u6709\u8f83\u9ad8\u7684\u8981\u6c42\uff1a\u5f53\u4e24\u4e2a\u5173\u8054\u8868\u8981 Join \u7684\u5b57\u6bb5\u9700\u8981\u6309\u6392\u597d\u7684\u987a\u5e8f\u8bfb\u53d6\u65f6\uff0c\u9002\u7528 Merge Join \u7b97\u6cd5\u3002<\/p>\n<h3>5.4 \u8001\u7248\u672c \u7269\u7406\u6267\u884c\u8ba1\u5212\u7684\u9009\u62e9<\/h3>\n<h4>5.4.1 \u4ee3\u4ef7\u7684\u8bc4\u4f30<\/h4>\n<p>\u8fd9\u5757\u4ee3\u7801\u4e3b\u8981\u662f\u5728<code>plan_cost_ver1.go<\/code>\u548c<code>plan_cost_ver2.go<\/code>\u3002v2\u5bf9\u4ee3\u4ef7\u516c\u5f0f\u8fdb\u884c\u4e86\u66f4\u7cbe\u786e\u7684\u56de\u5f52\u6821\u51c6\uff0c\u8c03\u6574\u4e86\u90e8\u5206\u4ee3\u4ef7\u516c\u5f0f\uff0c\u6bd4\u6b64\u524d\u7248\u672c\u7684\u4ee3\u4ef7\u516c\u5f0f\u66f4\u52a0\u51c6\u786e\u3002\u4ee3\u7801\u4e0a\u4e5f\u66f4\u4e3a\u7b80\u6d01\uff1av2\u53ea\u66b4\u9732\u51fa\u4e86\u4e00\u4e2a\u516c\u5171\u65b9\u6cd5\uff0c\u5185\u90e8\u901a\u8fc7\u4e0d\u540c\u7684\u7c7b\u578b\u505a\u8f6c\u53d1\u3002<\/p>\n<pre><code class=\"go\">\/\/ GetPlanCost returns the cost of this plan.\nfunc GetPlanCost(p PhysicalPlan, taskType property.TaskType, option *PlanCostOption) (float64, error) {\n    return getPlanCost(p, taskType, option)\n}\n\nfunc getPlanCost(p PhysicalPlan, taskType property.TaskType, option *PlanCostOption) (float64, error) {\n    if p.SCtx().GetSessionVars().CostModelVersion == modelVer2 {\n        planCost, err := p.getPlanCostVer2(taskType, option)\n        return planCost.cost, err\n    }\n    return p.getPlanCostVer1(taskType, option)\n}<\/code><\/pre>\n<p>\u6839\u636e\u4e0d\u540c\u7684<code>PhysicalPlan<\/code>\u7c7b\u578b\uff0c\u4f1a\u627e\u5230\u4e0d\u540c\u7ed1\u5b9a\u65b9\u6cd5\uff1a<\/p>\n<p><img decoding=\"async\"   referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000044635803\" alt=\"\" title=\"\" loading=\"lazy\"><\/p>\n<p>v1\u7684\u90e8\u5206\u65b9\u6cd5\u5c55\u793a\uff1a<\/p>\n<p><img decoding=\"async\"   referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000044635804\" alt=\"\" title=\"\" loading=\"lazy\"><\/p>\n<h5>select\u4ee3\u4ef7\u8ba1\u7b97\u65b9\u5f0f<\/h5>\n<pre><code class=\"go\">\/\/ getPlanCostVer2 returns the plan-cost of this sub-plan, which is:\n\/\/ plan-cost = child-cost + filter-cost\nfunc (p *PhysicalSelection) getPlanCostVer2(taskType property.TaskType, option *PlanCostOption) (costVer2, error) {\n    if p.planCostInit &amp;&amp; !hasCostFlag(option.CostFlag, CostFlagRecalculate) {\n        return p.planCostVer2, nil\n    }\n\n    inputRows := getCardinality(p.children[0], option.CostFlag)\n    cpuFactor := getTaskCPUFactorVer2(p, taskType)\n\n    filterCost := filterCostVer2(option, inputRows, p.Conditions, cpuFactor)\n\n    childCost, err := p.children[0].getPlanCostVer2(taskType, option)\n    if err != nil {\n        return zeroCostVer2, err\n    }\n\n    p.planCostVer2 = sumCostVer2(filterCost, childCost)\n    p.planCostInit = true\n    return p.planCostVer2, nil\n}<\/code><\/pre>\n<p>\u8fd9\u90e8\u5206\u4ee3\u7801\u7b80\u5355\u6613\u8bfb\u3002\u4ee3\u4ef7\u5c31\u662f\u5b50\u67e5\u8be2\u7684\u4ee3\u4ef7+\u7b5b\u9009\u7684\u4ee3\u4ef7\u3002<\/p>\n<p>\u90a3\u4e48\u95ee\u9898\u6765\u4e86\uff0c\u4e2d\u7d22\u5f15\u7684\u548c\u4e0d\u4e2d\u7d22\u5f15\u7684\u4ee3\u4ef7\u5e94\u8be5\u662f\u4e0d\u4e00\u6837\u7684\u3002\u8fd9\u91cc\u6ca1\u6709\u4f53\u73b0\u51fa\u6765\u554a\u3002\u4ed4\u7ec6\u770b<code>childCost, err := p.children[0].getPlanCostVer2(taskType, option)<\/code>\uff0c\u8fd9\u91cc\u662f\u4f1a\u53bb\u83b7\u53d6\u5b50\u7269\u7406\u6267\u884c\u8ba1\u5212\u7684\u4ee3\u4ef7\u3002<\/p>\n<pre><code class=\"go\">\/\/ getPlanCostVer2 returns the plan-cost of this sub-plan, which is:\nfunc (p *PointGetPlan) getPlanCostVer2(taskType property.TaskType, option *PlanCostOption) (costVer2, error) {\n    if p.planCostInit &amp;&amp; !hasCostFlag(option.CostFlag, CostFlagRecalculate) {\n        return p.planCostVer2, nil\n    }\n\n    if p.accessCols == nil { \/\/ from fast plan code path\n        p.planCostVer2 = zeroCostVer2\n        p.planCostInit = true\n        return zeroCostVer2, nil\n    }\n    rowSize := getAvgRowSize(p.stats, p.schema.Columns)\n    netFactor := getTaskNetFactorVer2(p, taskType)\n\n    p.planCostVer2 = netCostVer2(option, 1, rowSize, netFactor)\n    p.planCostInit = true\n    return p.planCostVer2, nil\n}\n\nfunc netCostVer2(option *PlanCostOption, rows, rowSize float64, netFactor costVer2Factor) costVer2 {\n    return newCostVer2(option, netFactor,\n        rows*rowSize*netFactor.Value,\n        func() string { return fmt.Sprintf(&quot;net(%v*rowsize(%v)*%v)&quot;, rows, rowSize, netFactor) })\n}\n\n\n\n\/\/ getPlanCostVer2 returns the plan-cost of this sub-plan, which is:\n\/\/ plan-cost = rows * log2(row-size) * scan-factor\n\/\/ log2(row-size) is from experiments.\nfunc (p *PhysicalTableScan) getPlanCostVer2(taskType property.TaskType, option *PlanCostOption) (costVer2, error) {\n    if p.planCostInit &amp;&amp; !hasCostFlag(option.CostFlag, CostFlagRecalculate) {\n        return p.planCostVer2, nil\n    }\n\n    rows := getCardinality(p, option.CostFlag)\n    var rowSize float64\n    if p.StoreType == kv.TiKV {\n        rowSize = getAvgRowSize(p.stats, p.tblCols) \/\/ consider all columns if TiKV\n    } else { \/\/ TiFlash\n        rowSize = getAvgRowSize(p.stats, p.schema.Columns)\n    }\n    rowSize = math.Max(rowSize, 2.0)\n    scanFactor := getTaskScanFactorVer2(p, p.StoreType, taskType)\n\n    p.planCostVer2 = scanCostVer2(option, rows, rowSize, scanFactor)\n\n    \/\/ give TiFlash a start-up cost to let the optimizer prefers to use TiKV to process small table scans.\n    if p.StoreType == kv.TiFlash {\n        p.planCostVer2 = sumCostVer2(p.planCostVer2, scanCostVer2(option, 10000, rowSize, scanFactor))\n    }\n\n    p.planCostInit = true\n    return p.planCostVer2, nil\n}\n\nfunc scanCostVer2(option *PlanCostOption, rows, rowSize float64, scanFactor costVer2Factor) costVer2 {\n    if rowSize &lt; 1 {\n        rowSize = 1\n    }\n    return newCostVer2(option, scanFactor,\n        \/\/ rows * log(row-size) * scanFactor, log2 from experiments\n        rows*math.Log2(rowSize)*scanFactor.Value,\n        func() string { return fmt.Sprintf(&quot;scan(%v*logrowsize(%v)*%v)&quot;, rows, rowSize, scanFactor) })\n}<\/code><\/pre>\n<p>scanFactor\u7684\u4ee3\u4ef7\u9ed8\u8ba4\u662f40.7\uff0cnetFactor\u7684\u4ee3\u4ef7\u9ed8\u8ba4\u662f3.96\u3002\u7ed3\u5408\u4ee3\u7801\u6765\u770b\uff0c\u547d\u4e2d\u7d22\u5f15\u7684\u4ee3\u4ef7\u66f4\u4f18\u3002<\/p>\n<h5>join\u4ee3\u4ef7\u8ba1\u7b97\u65b9\u5f0f<\/h5>\n<pre><code class=\"go\">\/\/ getPlanCostVer2 returns the plan-cost of this sub-plan, which is:\n\/\/ plan-cost = build-child-cost + build-filter-cost +\n\/\/ (probe-cost + probe-filter-cost) \/ concurrency\n\/\/ probe-cost = probe-child-cost * build-rows \/ batchRatio\nfunc (p *PhysicalIndexJoin) getPlanCostVer2(taskType property.TaskType, option *PlanCostOption) (costVer2, error) {\n    return p.getIndexJoinCostVer2(taskType, option, 0)\n}\n\nfunc (p *PhysicalIndexHashJoin) getPlanCostVer2(taskType property.TaskType, option *PlanCostOption) (costVer2, error) {\n    return p.getIndexJoinCostVer2(taskType, option, 1)\n}\n\nfunc (p *PhysicalIndexMergeJoin) getPlanCostVer2(taskType property.TaskType, option *PlanCostOption) (costVer2, error) {\n    return p.getIndexJoinCostVer2(taskType, option, 2)\n}\n\nfunc (p *PhysicalIndexJoin) getIndexJoinCostVer2(taskType property.TaskType, option *PlanCostOption, indexJoinType int) (costVer2, error) {\n    if p.planCostInit &amp;&amp; !hasCostFlag(option.CostFlag, CostFlagRecalculate) {\n        return p.planCostVer2, nil\n    }\n\n    build, probe := p.children[1-p.InnerChildIdx], p.children[p.InnerChildIdx]\n    buildRows := getCardinality(build, option.CostFlag)\n    buildRowSize := getAvgRowSize(build.Stats(), build.Schema().Columns)\n    probeRowsOne := getCardinality(probe, option.CostFlag)\n    probeRowsTot := probeRowsOne * buildRows\n    probeRowSize := getAvgRowSize(probe.Stats(), probe.Schema().Columns)\n    buildFilters, probeFilters := p.LeftConditions, p.RightConditions\n    probeConcurrency := float64(p.ctx.GetSessionVars().IndexLookupJoinConcurrency())\n    cpuFactor := getTaskCPUFactorVer2(p, taskType)\n    memFactor := getTaskMemFactorVer2(p, taskType)\n    requestFactor := getTaskRequestFactorVer2(p, taskType)\n\n    buildFilterCost := filterCostVer2(option, buildRows, buildFilters, cpuFactor)\n    buildChildCost, err := build.getPlanCostVer2(taskType, option)\n    if err != nil {\n        return zeroCostVer2, err\n    }\n    buildTaskCost := newCostVer2(option, cpuFactor,\n        buildRows*10*cpuFactor.Value,\n        func() string { return fmt.Sprintf(&quot;cpu(%v*10*%v)&quot;, buildRows, cpuFactor) })\n    startCost := newCostVer2(option, cpuFactor,\n        10*3*cpuFactor.Value,\n        func() string { return fmt.Sprintf(&quot;cpu(10*3*%v)&quot;, cpuFactor) })\n\n    probeFilterCost := filterCostVer2(option, probeRowsTot, probeFilters, cpuFactor)\n    probeChildCost, err := probe.getPlanCostVer2(taskType, option)\n    if err != nil {\n        return zeroCostVer2, err\n    }\n\n    var hashTableCost costVer2\n    switch indexJoinType {\n    case 1: \/\/ IndexHashJoin\n        hashTableCost = hashBuildCostVer2(option, buildRows, buildRowSize, float64(len(p.RightJoinKeys)), cpuFactor, memFactor)\n    case 2: \/\/ IndexMergeJoin\n        hashTableCost = newZeroCostVer2(traceCost(option))\n    default: \/\/ IndexJoin\n        hashTableCost = hashBuildCostVer2(option, probeRowsTot, probeRowSize, float64(len(p.LeftJoinKeys)), cpuFactor, memFactor)\n    }\n\n    \/\/ IndexJoin executes a batch of rows at a time, so the actual cost of this part should be\n    \/\/  `innerCostPerBatch * numberOfBatches` instead of `innerCostPerRow * numberOfOuterRow`.\n    \/\/ Use an empirical value batchRatio to handle this now.\n    \/\/ TODO: remove this empirical value.\n    batchRatio := 6.0\n    probeCost := divCostVer2(mulCostVer2(probeChildCost, buildRows), batchRatio)\n\n    \/\/ Double Read Cost\n    doubleReadCost := newZeroCostVer2(traceCost(option))\n    if p.ctx.GetSessionVars().IndexJoinDoubleReadPenaltyCostRate &gt; 0 {\n        batchSize := float64(p.ctx.GetSessionVars().IndexJoinBatchSize)\n        taskPerBatch := 1024.0 \/\/ TODO: remove this magic number\n        doubleReadTasks := buildRows \/ batchSize * taskPerBatch\n        doubleReadCost = doubleReadCostVer2(option, doubleReadTasks, requestFactor)\n        doubleReadCost = mulCostVer2(doubleReadCost, p.ctx.GetSessionVars().IndexJoinDoubleReadPenaltyCostRate)\n    }\n\n    p.planCostVer2 = sumCostVer2(startCost, buildChildCost, buildFilterCost, buildTaskCost, divCostVer2(sumCostVer2(doubleReadCost, probeCost, probeFilterCost, hashTableCost), probeConcurrency))\n    p.planCostInit = true\n    return p.planCostVer2, nil\n}<\/code><\/pre>\n<p>\u5173\u952e\u5728\u4e8e\uff1a<\/p>\n<pre><code class=\"go\">    switch indexJoinType {\n    case 1: \/\/ IndexHashJoin\n        hashTableCost = hashBuildCostVer2(option, buildRows, buildRowSize, float64(len(p.RightJoinKeys)), cpuFactor, memFactor)\n    case 2: \/\/ IndexMergeJoin\n        hashTableCost = newZeroCostVer2(traceCost(option))\n    default: \/\/ IndexJoin\n        hashTableCost = hashBuildCostVer2(option, probeRowsTot, probeRowSize, float64(len(p.LeftJoinKeys)), cpuFactor, memFactor)\n    }<\/code><\/pre>\n<p>\u5bf9\u5e94\u65b9\u6cd5\uff1a<\/p>\n<pre><code class=\"go\">func hashBuildCostVer2(option *PlanCostOption, buildRows, buildRowSize, nKeys float64, cpuFactor, memFactor costVer2Factor) costVer2 {\n    \/\/ TODO: 1) consider types of keys, 2) dedicated factor for build-probe hash table\n    hashKeyCost := newCostVer2(option, cpuFactor,\n        buildRows*nKeys*cpuFactor.Value,\n        func() string { return fmt.Sprintf(&quot;hashkey(%v*%v*%v)&quot;, buildRows, nKeys, cpuFactor) })\n    hashMemCost := newCostVer2(option, memFactor,\n        buildRows*buildRowSize*memFactor.Value,\n        func() string { return fmt.Sprintf(&quot;hashmem(%v*%v*%v)&quot;, buildRows, buildRowSize, memFactor) })\n    hashBuildCost := newCostVer2(option, cpuFactor,\n        buildRows*cpuFactor.Value,\n        func() string { return fmt.Sprintf(&quot;hashbuild(%v*%v)&quot;, buildRows, cpuFactor) })\n    return sumCostVer2(hashKeyCost, hashMemCost, hashBuildCost)\n}\n\nfunc newZeroCostVer2(trace bool) (ret costVer2) {\n    if trace {\n        ret.trace = &amp;costTrace{make(map[string]float64), &quot;&quot;}\n    }\n    return\n}<\/code><\/pre>\n<p>\u7b80\u5355\u7684\u770b\u4e00\u4e0b\u4ee3\u7801\uff0c\u6211\u4eec\u53ef\u4ee5\u53d1\u73b0\uff0c\u4ece\u5927\u591a\u6570\u7684\u573a\u666f\u6765\u770b\uff0c\u6309\u7167\u4ee3\u4ef7\u4ece\u5c0f\u5230\u5927\u6765\u6392\uff0c\u8fd9\u51e0\u79cdJoin\u662fMergeJoin&lt;HashJoin&lt;IndexJoin\u3002<\/p>\n<h4>5.4.2\u6267\u884c\u8ba1\u5212\u679a\u4e3e\u4e0e\u62e9\u4f18<\/h4>\n<p>\u603b\u5f97\u6765\u8bf4\u8fd9\u5757\u4ee3\u7801\u8f83\u4e3a\u7b80\u5355\uff0c\u672c\u8d28\u5c31\u662f\u679a\u4e3e\u6240\u6709\u7b26\u5408\u6761\u4ef6\u7684\u7269\u7406\u6267\u884c\u8ba1\u5212\uff0c\u5e76\u6311\u9009\u51fa\u4ee3\u4ef7\u6700\u5c0f\u7684\u6267\u884c\u8ba1\u5212\uff0c\u6545\u4e0d\u518d\u5217\u4e3e\u4ee3\u7801\u3002\u6709\u5174\u8da3\u7684\u540c\u5b66\u53ef\u4ee5\u6839\u636e\u4ee5\u4e0b\u5927\u7eb2\u81ea\u884c\u7ffb\u9605\uff1a<\/p>\n<pre><code class=\"log\">|--planner\/core\/find_best_task.go\n\n\/-- findBestTask\n\n\/-- enumeratePhysicalPlans4Task\n\n\/-- compareTaskCost\n\n\/-- getTaskPlanCost\n\n|-- planner\/core\/plan_cost_ver2.go\n\n\/-- getPlanCost<\/code><\/pre>\n<h2>6.\u5176\u4ed6<\/h2>\n<h3>6.1 \u53c2\u8003\u4e0e\u5f15\u7528\u7684\u6587\u7ae0<\/h3>\n<ul>\n<li>Cascades Optimizer\uff1a<a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=7q6VyBHhEVwzB0R2AQG1zg%3D%3D.%2B09QbvSGGW3pNCkyPIIEDS2rc%2FdRqIeRNn6xuv7ecpBfiz4XN3fBoM6ipe77Ymi%2B\" rel=\"noopener noreferrer\">https:\/\/zhuanlan.zhihu.com\/p\/73545345<\/a><\/li>\n<li>\u63ed\u79d8 TiDB \u65b0\u4f18\u5316\u5668\uff1aCascades Planner \u539f\u7406\u89e3\u6790\uff1a<a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=YrynnHhnCS6oUmcMQwT8hQ%3D%3D.zgD6W3MrL5R4xL4mCI%2FDSnP4prcGwagtMeqR15%2FU4DrinMCnZAuk1lnCMzJ%2Bike0EPXLvvnVHPQROARVX8UM8A%3D%3D\" rel=\"noopener noreferrer\">https:\/\/cn.pingcap.com\/blog\/tidb-cascades-planner\/<\/a><\/li>\n<li>TiDB\u6587\u6863-\u7edf\u8ba1\u4fe1\u606f\u7b80\u4ecb\uff1a<a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=imYv3P910e2VDnFfbwu40w%3D%3D.CxZTPNIVDV1z1M%2FHuaLjZ8GyrHQxPp0JlmQNjS9iN1GPsEvq8mpUSggJWzgTeWlPQ2SXfB1fMcb5j3K8DnMTl8JsO4FJxrqgEcXUusWnEoCiclpKSLVYB5SmuAQFT7kwNb%2FLAb1VyGX1Md1kGjy6Yw%3D%3D\" rel=\"noopener noreferrer\">https:\/\/docs.pingcap.com\/zh\/tidb\/v6.5\/statistics#%E7%BB%9F%E8&#8230;<\/a><\/li>\n<li>TiDB\u6587\u6863-\u9519\u8bef\u7d22\u5f15\u7684\u89e3\u51b3\u65b9\u6848\uff1a<a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=vTu0E2AAxlNPTcyaUGxkdg%3D%3D.AZZaF9xOJxYyV8aVBEAZonXPWMH%2FvQAQLILi0Eez6ewuhM9VrZbAZgoTqAqg1UaYO9jOpWEAesmxF6R9vZWzHqLh9zpRICCAD3ZKBf8wQ1%2BG5i27wjftybwgS91CF1evNOUs0Km6xtAOLUdhVDii7w%3D%3D\" rel=\"noopener noreferrer\">https:\/\/docs.pingcap.com\/zh\/tidb\/v6.5\/wrong-index-solution#%E&#8230;<\/a><\/li>\n<li>The Volcano Optimizer Generator: Extensibility and Efficient Search\uff1a<a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=j2pzlTFhh2oHljIyIfkl2Q%3D%3D.MS72tZELnQFABiDjUstpyujWSYR%2FVIemZBzF5J2SZN3ABMM54J8qXHnrHzIRUCSzPiOdKtZGwyxgUhYLFj2Ug1JzCO9JLkoegBGcWLN4M%2BQ7ydbm%2Bpn7gebKf%2B0Tc%2BTe\" rel=\"noopener noreferrer\">https:\/\/15721.courses.cs.cmu.edu\/spring2019\/papers\/22-optimiz&#8230;<\/a><\/li>\n<li>The Cascades Framework for Query Optimization\uff1a<a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=BVFGM1cb0giYcpsP6IJQGw%3D%3D.h5wT14PFLdB%2FKb1oe8W7ibWWbrNlyQtkm0jA7%2BfTopEvlGP3Uhzu7OorOiWYkHoWZtxQWDYI%2BUMrDzRQH9SLHO3nnuJjbFXsdIW1qs%2Ffo7vucKSL0MvqgVEfhcdh9ovP\" rel=\"noopener noreferrer\">https:\/\/15721.courses.cs.cmu.edu\/spring2018\/papers\/15-optimiz&#8230;<\/a><\/li>\n<\/ul>\n<h3>6.2 \u77e5\u8bc6\u8865\u5145\uff1acode generation &#038;&#038; vectorized execution<\/h3>\n<p>\u6570\u636e\u5e93\u5f15\u64ce\u6267\u884c\u5668\u4e2d\u975e\u5e38\u51fa\u540d\u7684\u4e24\u79cd\u4f18\u5316\u65b9\u5f0f\uff0ccode generation\u548c vectorized execution\u3002<\/p>\n<p>code generation\u4e3b\u8981\u662f\u6839\u636e\u4e0a\u4e0b\u6587\u6765\u751f\u6210\u4e00\u6574\u6bb5\u4f18\u5316\u8fc7\u7684\u4ee3\u7801\uff0c\u8fd9\u4e0e\u90a3\u79cd\u5d4c\u5957\u5927\u91cfif&#8230;else\u3001for\u5faa\u73af\u3001\u865a\u65b9\u6cd5\u7684\u4ee3\u7801\u5b8c\u5168\u76f8\u53cd\uff0c\u5b8c\u5168\u9762\u5411\u6027\u80fd\u8003\u8651\u3002<\/p>\n<p>vectorized execution\u57fa\u4e8e\u62c9\u6a21\u578b\u3002\u76f8\u6bd4\u4e8e\u4e00\u6b21\u62c9\u4e00\u4e2atuple\u6765\u8bf4\uff0c\u5b83\u7684\u6279\u91cf\u62c9\u53d6\u51cf\u5c11\u4e86\u591a\u6b21\u62c9\u53d6\u7684\u5f00\u9500\uff0c\u540c\u65f6\u8fd8\u53ef\u4ee5\u4f7f\u7528\u5230SIMD\u3002\u57fa\u4e8e\u8fd9\u79cd\u573a\u666f\uff0cvectorized execution\u7684\u4f18\u5316\u66f4\u52a0\u9002\u7528\u4e8e\u5217\u5f0f\u6570\u636e\u5e93\u3002<\/p>\n<p><a href=\"https:\/\/segmentfault.com\/a\/1190000044635798\">https:\/\/segmentfault.com\/a\/1190000044635798<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u7248\u672c \u65e5\u671f \u5907\u6ce8 1.0 2024.2.18 \u6587\u7ae0\u9996\u53d1 \u672c\u6587\u7684\u7684\u6e90\u7801\u5206\u6790\u5168\u90e8\u57fa\u4e8eTiDB6.5\u6765\u505a\u5206\u6790\u3002 1. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-10008363","post","type-post","status-publish","format-standard","hentry","category-share"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10008363","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10008363"}],"version-history":[{"count":0,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10008363\/revisions"}],"wp:attachment":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10008363"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10008363"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10008363"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}