标签 索引 下的文章

前面的章节(社区专栏《SQL调优》)我们已经写了很多篇幅关于 MySQL 执行计划的解读,今天我们来继续延伸介绍执行计划的链路跟踪功能,也就是 MySQL 的 Optimizer Trace

在这之前,先来回顾下 EXPLAIN 的结果:

mysql:ytt>explain select * from t1 a left join y1 b on a.id = b.id where a.r1<100 order by a.r2 desc;
+----+-------------+-------+------------+--------+---------------+---------+---------+----------+--------+----------+-----------------------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref      | rows   | filtered | Extra                       |
+----+-------------+-------+------------+--------+---------------+---------+---------+----------+--------+----------+-----------------------------+
|  1 | SIMPLE      | a     | NULL       | ALL    | idx_r1        | NULL    | NULL    | NULL     | 998222 |    50.00 | Using where; Using filesort |
|  1 | SIMPLE      | b     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | ytt.a.id |      1 |   100.00 | NULL                        |
+----+-------------+-------+------------+--------+---------------+---------+---------+----------+--------+----------+-----------------------------+
2 rows in set, 1 warning (0.00 sec)

EXPLAIN 展示出来的核心数据有:

  1. 表关联顺序
  2. 优化器筛选过的索引
  3. 实际使用的索引
  4. 每张表依据统计信息的扫描行数
  5. Extra 额外数据提示
  6. 两种执行计划(explain format=tree / explain format=json)展示出来的额外成本数据

如果想快速对于 SQL 进行优化,基于以上的结果完全可以满足。但是想深入了解 MySQL 优化器为什么选择这样的执行计划,基于以上的结果就无法满足。

举例说明:

  • 我想知道对于表 a 来讲,为什么有索引 idx_r1,但是实际却没有使用,而走的全表扫?
  • 两张表关联,为什么选择的顺序是表 a 驱动表 b,而不是表 b 驱动表 a
  • 为什么字段 r2 有索引,但是依然要走排序?

带着这些疑问,我们来介绍 MySQL 的 Optimizer Trace 功能。

1. 什么是 Optimizer Trace?

简单来讲,Optimizer Trace 是一个 SQL 执行计划的链路跟踪器,跟踪 SQL 的解析、优化、执行等过程,并且把结果记录到 MySQL 元数据表(information_schema.optimizer_trace),之后可以对这张表分析得到很多个执行计划的“为什么?”!

2. 如何使用 Optimizer Trace?

要使用 Optimizer Trace 功能,首先得打开控制开关。谨记:这个功能非常耗费资源,默认关闭的,可以通过调整以下变量开启:

mysql:ytt>show variables like 'optimizer_trace%';
+------------------------------+----------------------------------------------------------------------------+
| Variable_name                | Value                                                                      |
+------------------------------+----------------------------------------------------------------------------+
| optimizer_trace              | enabled=off,one_line=off                                                   |
| optimizer_trace_features     | greedy_search=on,range_optimizer=on,dynamic_range=on,repeated_subselect=on |
| optimizer_trace_limit        | 1                                                                          |
| optimizer_trace_max_mem_size | 1048576                                                                    |
| optimizer_trace_offset       | -1                                                                         |
+------------------------------+----------------------------------------------------------------------------+
5 rows in set (0.00 sec)

以上几个参数详细解释下:

  • optimizer_traceenabled=on/off 启用/禁用 Optmizer Trace 功能;one_line=on/off 启用/禁用 json 格式化存储,一般不需改动。
  • optimizer_trace_limit/optimizer_trace_offset:这两个参数和 LIMIT 子句一样,用来最终展示 Trace 的 SQL 条数。展示的条数越多,对内存消耗越大,默认展示最近的一条记录。比如设置 optimizer_trace_limit 为 10,optimizer_trace_offset 为 -10,就可以最多展示 10 条 Trace 记录。
  • optimizer_trace_max_mem_size:用来存储 Trace 结果的最大内存。
  • optimizer_trace_features:用来启动/禁用相关 Trace 特性开关。
  • end_markers_in_json:启用/禁用 注释功能。开启这个,Trace 结果可读性更强。
  • Optimizer Trace 可以跟踪的语句有:

    • SELECT、TABLE、VALUES、WITH、INSERT、REPLACE、UPDATE、DELETE
    • EXPLAIN
    • SET(排除设置 Optimizer Trace 相关参数)
    • DO
    • 存储函数内部、触发器内部等的 DECLARE、CASE、IF、RETURN 语句
    • CALL
在数据库里,语句调优一般说的是 SELECT 语句,所以大部分场景跟踪的也只有 SELECT 语句。

元数据表字段解析

mysql:ytt>desc information_schema.optimizer_trace;
+-----------------------------------+----------------+------+-----+---------+-------+
| Field                             | Type           | Null | Key | Default | Extra |
+-----------------------------------+----------------+------+-----+---------+-------+
| QUERY                             | varchar(65535) | NO   |     |         |       |
| TRACE                             | varchar(65535) | NO   |     |         |       |
| MISSING_BYTES_BEYOND_MAX_MEM_SIZE | int            | NO   |     |         |       |
| INSUFFICIENT_PRIVILEGES           | tinyint(1)     | NO   |     |         |       |
+-----------------------------------+----------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
  • QUERYTRACE 的 SQL 语句原文
  • TRACE:SQL 语句的 TRACE 结果,JSON 格式存储(由变量 end_markers_in_json 来控制)
  • MISSING_BYTES_BEYOND_MAX_MEM_SIZETRACE 结果超过变量 optimizer_trace_max_mem_size 设置的值后,截断的大小(BYTE)
  • INSUFFICIENT_PRIVILEGES:对存储过程、存储函数等包含有 SQL SECURITY DEFINER 的用户是否有对应的权限,有权限为 0,无权限为 1,并且 TRACE 字段为空。

Optimizer Trace 开启步骤

mysql:ytt>set optimizer_trace='enabled=on';
Query OK, 0 rows affected (0.00 sec)

mysql:ytt>set optimizer_trace_limit=10;
Query OK, 0 rows affected (0.00 sec)

mysql:ytt>set optimizer_trace_offset=-10;
Query OK, 0 rows affected (0.00 sec)

mysql:ytt>set end_markers_in_json=on;
Query OK, 0 rows affected (0.00 sec)

这里要注意的是,修改任何一个 Optimizer Trace 相关参数,元数据表 information_schema 表都会被清空。

mysql:ytt>select count(*) from information_schema.optimizer_trace;
+----------+
| count(*) |
+----------+
|       10 |
+----------+
1 row in set (0.00 sec)

mysql:ytt>set optimizer_trace_offset=-2;
Query OK, 0 rows affected (0.00 sec)

mysql:ytt>select count(*) from information_schema.optimizer_trace;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

3. Optimizer Trace 的结果

我们用一个最简单的例子来看看 Optimizer Trace 的大致结构:do 语句非常简单,只用来验证是否语法正确,不出结果。

mysql:ytt>do 1+1;
Query OK, 0 rows affected (0.00 sec)

下面是 Optimizer Trace 结果:

mysql:ytt>select query,trace from information_schema.optimizer_trace\G
*************************** 1. row ***************************
query: do 1+1
trace: {
  "steps": [
    {
      "join_preparation": {
        "select#": 1,
        "steps": [
          {
            "expanded_query": "/* select#1 */ select (1 + 1) AS `1+1`"
          }
        ]
      }
    },
    {
      "join_optimization": {
        "select#": 1,
        "steps": [
        ]
      }
    },
    {
      "join_execution": {
        "select#": 1,
        "steps": [
        ]
      }
    }
  ]
}
1 row in set (0.00 sec)

可以看到,Optimizer Trace 结果是一个 JSON 串,keystepsvalue 是一个数组,数组有三个 key,分别为:

  • join_preparation 准备阶段:这里会做一些 SQL 改写,关键字识别等等,可以看到 expanded_query 对应的值即为 SQL 语句被改写后的内部 SQL。
  • join_optimization 优化阶段:具体 SQL 优化,包括一些可能的逻辑优化,一些根据表统计信息预估的物理优化等等。
  • join_execution 最终执行阶段:最终 SQL 采用的执行计划等等。

本篇是Optimizer Trace的开端,由于内容太多,我特地拆分为几篇来写,欢迎继续订阅。

640 (84).webp

金仓数据库中包含各种数据库对象,常见的KingBase对象有:数据库、模式、表、索引、视图、存储过程、存储函数和触发器等等。这里将介绍金仓数据库中常见的数据库对象以及如何使用它们。视频讲解如下:
https://www.bilibili.com/video/BV1RQz3B9ERT/?aid=115930646454...

一、 数据库与模式

数据库本身也是一个KingBase的数据库对象。数据库对象中包含其他所有的数据库对象,如:模式、表、视图、索引等等。使用命令create database可以创建一个新的数据库,下面展示了该命令的格式:

CREATE DATABASE name
     [ WITH ] [ OWNER [=] user_name ]
           [ TEMPLATE [=] template ]
           [ ENCODING [=] encoding ]
           [ LC_COLLATE [=] lc_collate ]
           [ LC_CTYPE [=] lc_ctype ]
           [ TABLESPACE [=] tablespace_name ]
           [ ALLOW_CONNECTIONS [=] allowconn ]
           [ CONNECTION LIMIT [=] connlimit ]
           [ IS_TEMPLATE [=] istemplate ]

一个数据库包含一个或多个模式(Schema),模式中又包含了表、函数及操作符等数据库对象。创建新数据库时,KingBase会自动创建名为public的模式。使用命令create schema可以创建一个新的模式,下面展示了该命令的格式:

CREATE SCHEMA schema_name [ AUTHORIZATION role_specification ] [ schema_element [ ... ] ]
CREATE SCHEMA AUTHORIZATION role_specification [ schema_element [ ... ] ]
CREATE SCHEMA IF NOT EXISTS schema_name [ AUTHORIZATION role_specification ]
CREATE SCHEMA IF NOT EXISTS AUTHORIZATION role_specification

其中 role_specification 可以是:

    user_name
  | CURRENT_USER
  | SESSION_USER

在了解到数据库与模式的概念后,下面通过具体的操作来演示如何创建和使用它们。
(1)创建一个新的数据库dbtest。

scott=# create database dbtest;

(2)查看已存在的数据库列表。

scott=# \l

# 输出的信息如下:
                                        数据库列表
   名称    | 拥有者 | 字元编码 |  校对规则   |    Ctype    | ICU 排序 |     存取权限      
-----------+--------+----------+-------------+-------------+----------+-------------------
 dbtest    | system | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 |          | 
 kingbase  | system | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 |          | 
 scott     | system | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 |          | 
 security  | system | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 |          | 
 template0 | system | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 |          | =c/system        +
           |        |          |             |             |          | system=CTc/system
 template1 | system | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 |          | =c/system        +
           |        |          |             |             |          | system=CTc/system
 test      | system | UTF8     | zh_CN.UTF-8 | zh_CN.UTF-8 |          | 
(7 行记录)

(3)切换到数据库dbtest。

scott=# \c dbtest 
您现在以用户名"system"连接到数据库"dbtest"。

(4)查看数据库dbtest中的模式。

dbtest=# \dn

# 输出的信息如下:
       架构模式列表
       名称       | 拥有者 
------------------+--------
 anon             | system
 dbms_job         | system
 dbms_scheduler   | system
 dbms_sql         | system
 kdb_schedule     | system
 perf             | system
 public           | system
 src_restrict     | system
 sys_hm           | system
 sysaudit         | system
 sysmac           | system
 wmsys            | system
 xlog_record_read | system
(13 行记录)

# 这里的public的模式是创建数据库对象的默认模式。

(5)创建一个新的模式。

dbtest=# create schema firstschema;

(6)重新查看数据库dbtest中的模式。

dbtest=# \dn

# 输出的信息如下:
       架构模式列表
       名称       | 拥有者 
------------------+--------
 anon             | system
 dbms_job         | system
 dbms_scheduler   | system
 dbms_sql         | system
 firstschema      | system
 kdb_schedule     | system
 perf             | system
 public           | system
 src_restrict     | system
 sys_hm           | system
 sysaudit         | system
 sysmac           | system
 wmsys            | system
 xlog_record_read | system
(14 行记录)

二、 创建与管理表

表是一种非常重要的数据库对象。金仓数据库的数据都是存储在表中。KingBase的表是一种二维结构,由行和列组成。表有列组成,列有列的数据类型。下面通过具体的步骤来演示如何操作金仓数据库的表。这些操作包括创建表、查看表、修改表和删除表。

(1)创建一张新的表test2.

dbtest=# create table test2(id int,name varchar(32),age int);

# 由于创建表时没有指定模式的名称,因此表将创建在public模式下。
# 如果要在指定的模式下创建表,可以使用下面的语句:
dbtest=# create table firstschema.test2(id int,name varchar(32),age int);

(2)查看表的结构。

dbtest=# \d test2

# 输出的信息如下:
                    数据表 "public.test2"
 栏位 |            类型            | 校对规则 | 可空的 | 预设 
------+----------------------------+----------+--------+------
 id   | integer                    |          |        | 
 name | character varying(32 char) |          |        | 
 age  | integer                    |          |        | 

(3)在表中增加一个字段。

dbtest=# alter table test2 add gender varchar(1) default 'M';

# 这里增加了一个gender字段用于表示性别,默认是“M”。

(4)重新查看表的结构。

dbtest=# \d test2

# 输出的信息如下:
                         数据表 "public.test2"
  栏位  |            类型            | 校对规则 | 可空的 |     预设     
--------+----------------------------+----------+--------+--------------
 id     | integer                    |          |        | 
 name   | character varying(32 char) |          |        | 
 age    | integer                    |          |        | 
 gender | character varying(1 char)  |          |        | 'M'::varchar

(5)修改表将gender字段的长度改为10个字符。

dbtest=# alter table test2 alter gender type varchar(10);

(6)删除gender字段。

dbtest=# alter table test2 drop column gender;

(7)删除表test2。

dbtest=# drop table test2;

三、 在查询时使用索引

数据库查询是数据库的主要功能之一,最基本的查询算法是顺序查找(linear search)时间复杂度为O(n),显然在数据量很大时效率很低。优化的查找算法如二分查找(binary search)、二叉树查找(binary tree search)等,虽然查找效率提高了。但是各自对检索的数据都有要求:二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,但是数据本身的组织结构不可能完全满足各种数据结构。所以在数据之外,数据库系统还维护着满足特定查找算法的数据结构。这些数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构就是索引。金仓数据库官方对索引的定义为:索引(Index)是帮助KingBase高效获取数据的数据结构。索引是一种数据结构。金仓数据库默认的索引类型是B树索引。下图是一颗简单的B树,可见它与二叉树最大的区别是它允许一个节点有多于2个的元素,每个节点都包含key和数据,查找时可以使用二分的方式快速搜索数据。

image.png

在了解到了KingBase索引的基本知识以后,下面将通过具体的步骤演示来说明如何在KingBase中创建索引,并且在查询语句中使用它。
(1)查看scott数据库中部门表dept和员工表emp上的索引信息。

scott=# select index_name,index_type,table_name,status
        from user_indexes where table_name in ('DEPT','EMP');
        
# 输出的信息如下:
 index_name | index_type | table_name | status 
------------+------------+------------+--------
 DEPT_PKEY  | BTREE      | DEPT       | VALID
 EMP_PKEY   | BTREE      | EMP        | VALID
(2 行记录)

# user_indexes是一个视图,可以通过它获取某个用户创建的索引信息。

(2)使用create index命令在员工表emp的薪水sal字段上创建完全索引。

scott=# create index index_full on emp using btree(sal);

# 完全索引会基于该字段上的所有值创建索引。
# 同时,在创建索引的时候会进行锁表的操作,可以使用 CIC (create index concurrently),
# 但创建索引的时间相对较长。例如:
scott=# create index concurrently index1 on emp using btree(sal);

(3)下面的语句将在员工表上创建一个部分索引。

scott=# create index index_part on emp using btree(sal) where sal<3000;

# 部分索引是对于表的部分数据创建索引。
# 如果发现表的某一部分数据查询次数较多时,可以考虑在这部分数据上创建一个部分索引。
# 部分索引相较于完全索引,查询的性能将得到提高,并且部分索引文件所占的空间也会小于全索引。

(4)在员工表emp的员工姓名ename上创建表达式索引。

scott=# create index index_exp on emp(lower(ename));

# 对于表达式索引的维护代价比较高,因为在每一行插入或更新时需要重新计算相应表达式的值,
# 但是针对于表达式索引在查询时的效率更高,因为表达式的值会直接存储在索引中。

(5)使用explain语句查看SQL查询时的执行计划。

scott=# explain select * from emp where lower(ename) like 'king';

# 输出的信息如下:
                     QUERY PLAN                     
----------------------------------------------------
 Seq Scan on emp  (cost=0.00..1.21 rows=1 width=42)
   Filter: (lower((ename)::text) ~~ 'king'::text)
(2 行记录)

# 从输出的执行计划可以看出,此时并没有使用到表达式索引。
# 这是由于KingBase并不能强制使用特定的索引,或者完全阻止KingBase进行Seq Scan的顺序扫描。
# 但可以通过将参数enable_seqscan设置为 off的方式让KingBase尽可能避免执行某些扫描类型,
# 但这样的方式多用于开发和调试中。

(6)禁止金仓数据库使用顺序扫描。

scott=# set enable_seqscan = off;

(7)重新使用explain语句查看SQL查询时的执行计划。

scott=# explain select * from emp where lower(ename) like 'king';

# 输出的信息如下:
                              QUERY PLAN                              
----------------------------------------------------------------------
 Index Scan using index_exp on emp  (cost=0.14..8.16 rows=1 width=42)
   Index Cond: (lower((ename)::text) = 'king'::text)
   Filter: (lower((ename)::text) ~~ 'king'::text)
(3 行记录)

四、 使用视图简化查询语句

当SQL的查询语句比较复杂并且需要反复执行,如果每次都重新书写该SQL语句显然不是很方便。因此金仓数据库数据库提供了视图用于简化复杂的SQL语句。视图(View)是一种虚表,其本身并不包含数据。它将作为一个select语句保存在数据字典中的。视图依赖的表叫做基表。通过视图可以展现基表的部分数据;视图数据来自定义视图的查询中使用的基表。在金仓数据库中创建视图的基本语法格式如下:

CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] [ RECURSIVE ] [ FORCE ] VIEW name [ ( column_name [, ...] ) ]
    [ WITH ( view_option_name [= view_option_value] [, ... ] ) ]
    [ BEQUEATH { CURRENT_USER | DEFINER } ]
    AS query
    [ WITH { [ CASCADED | LOCAL ] CHECK OPTION } | READ ONLY ]

在了解的视图的作用后,下面通过具体的步骤来演示如何使用视图。
(1)基于员工表emp创建视图。

scott=# create or replace view view1
as
select * from emp where deptno=10;

# 视图也可以基于多表进行创建,例如:
scott=# create or replace view view2
as
select emp.ename,emp.sal,dept.dname
from emp,dept
where emp.deptno=dept.deptno;

(2)查看视图view2的结构。

scott=# \d view2

# 输出的信息如下:
                      视图 "public.view2"
 栏位  |            类型            | 校对规则 | 可空的 | 预设 
-------+----------------------------+----------+--------+------
 ename | character varying(10 char) |          |        | 
 sal   | integer                    |          |        | 
 dname | character varying(10 char) |          |        | 

(3)从视图中查询数据。

scott=# select * from view2;

# 输出的信息如下:
 ename  | sal  |   dname    
--------+------+------------
 MILLER | 1300 | ACCOUNTING
 CLARK  | 2450 | ACCOUNTING
 KING   | 5000 | ACCOUNTING
 SCOTT  | 3000 | RESEARCH
 JONES  | 2975 | RESEARCH
 SMITH  |  800 | RESEARCH
 ADAMS  | 1100 | RESEARCH
 FORD   | 3000 | RESEARCH
 WARD   | 1250 | SALES
 TURNER | 1500 | SALES
 ALLEN  | 1600 | SALES
 BLAKE  | 2850 | SALES
 MARTIN | 1250 | SALES
 JAMES  |  950 | SALES
(14 行记录)

(4)通过视图执行DML操作,例如:给10号部门员工涨100块钱工资。

scott=# update view1 set sal=sal+100;

# 并不是所有的视图都可以执行DML操作。在视图定义时含义以下内容,视图则不能执行DML操作:
# 1.  查询子句中包含distinct和组函数
# 2.  查询语句中包含group by子句和order by子句
# 3.  查询语句中包含union 、union all等集合运算符
# 4.  where子句中包含相关子查询
# 5.  from子句中包含多个表
# 6.  如果视图中有计算列,则不能执行update操作
# 7.  如果基表中有某个具有非空约束的列未出现在视图定义中,则不能做insert操作

(5)创建视图时使用WITH CHECK OPTION约束 。

scott=# create or replace view view3
as
select * from emp where sal<1000
with check option;

# WITH CHECK OPTION表示对视图所做的DML操作,不能违反视图的WHERE条件的限制。

(6)在view3上执行update操作。

scott=# update view3 set sal=2000;

# 此时将出现下面的错误信息:
# ERROR:  新行违反了视图"view3"的检查选项
# DETAIL:  失败, 行包含(7369, SMITH, CLERK, 7902, 1980/12/17, 2000, null, 20).

  纯情博客为您提供最新网络安全黑客博客信息资讯

  前言

  今天就和大家聊一聊如何在日常开发中减少bug? 本文将从数据库、代码层面、缓存使用三大方向总结共计50多个关注点,帮助大家成为开发品质之星。

  1.数据库

  慢查询

  数据库章节,哪些地方容易出bug? 我总结了7个方面:慢查询、数据库字段注意点、事务失败场景、死锁、主从延迟、新旧数据兼容、一些SQL经典注意点。

  1.1 慢查询

  慢查询.gif1.1.1是否命中索引

  说到慢查询,我们马上就会想到索引。 如果一个SQL没有索引,或者没有命中索引,就会产生一个慢查询。

  什么情况下索引会失效?

  1.1.2 数据量大,考虑分库分表

  如果单表数据量过大脚本源码,会影响SQL执行的性能。 我们知道,索引数据结构一般是B+树,一棵高度为3的B+树可以存储大约2000万条数据。 如果超过这个数,B+树会变高,查询性能会下降。

  因此,当数据量较大时,建议将数据库分表。 分库分表的中间件包括mycat、-jdbc

  1.1.3 不合理的SQL

  在日常开发中,笔者见过很多不合理的SQL:比如一条SQL居然用了6个表连接,表太多会影响查询性能; 又比如一张表,实际上加了10个索引等,索引会降低插入和更新SQL的性能,所以一般不建议索引太多,一般不要超过5个。

  1.2 数据库字段注意事项

  数据库字段的内容容易出现bug。 比如你在测试环境修改了表结构,增加了某个字段,生产环境忘记带脚本,那肯定是版本有问题。

  1.2.1 字段是否会过长

  假设您的数据库字段是:

  

`name varchar(255) DEFAULT NOT NULL    `

  如果请求参数自带变量名,字段长度为300,插入表时会报错。 因此需要对参数进行校验,防止字段过长。

  1.2.2 如果字段为空,会不会造成空指针等

  我们在设计数据库表字段的时候,尽量把字段设置为not null。

  如果数据库字段设置为NULL值,很容易导致程序出现空指针; 如果数据库字段设置为NULL值,需要注意使用count(特定列),会有陷阱。

  1.2.3 缺失字段

  对于我们日常的开发任务,如果我们在测试环境修改表,比如增加新的字段,我们必须把SQL脚本带到生产环境,否则字段会丢失,发布时就会出现问题。

  1.2.4 字段类型是否支持表情

  如果表字段需要支持表达式存储,使用 .

  1.2.5 谨慎使用文本和blob字段

  如果您要在字段中存储文件,请考虑存储文件的路径而不是保存整个文件。 使用文本时,注意在查询条件时创建前缀索引。

  1.3 事务失败的场景 1.3.1 @ on non-

  @注解,添加到非修饰方法时,事务不会生效。 事务是基于AOP的思想,也是通过动态代理实现的。 事务本身在调用动态代理之前,已经过滤了非方法,所以非方法,事务不会生效。

  1.3.2 本地方法直接调用

  在下面的场景中,@ 也是无效的

  

public class TransactionTest{      public void A(){        //插入一条数据        //调用方法B (本地的类调用,事务失效了)        B();      }            @Transactional      public void B(){        //插入数据      }    }    

  1.3.3异常被try...catch吃掉,导致事务失败。

  

@Transactional    public void method(){      try{        //插入一条数据        insertA();        //更改一条数据        updateB();      }catch(Exception e){        logger.error("异常被捕获了,那你的事务就失效咯",e);      }    }        

  1.3.4 属性设置错误

  默认抛出未检查异常(继承异常)或Error来回滚事务; 其他异常不会触发回滚事务。 如果事务中抛出其他类型的异常,则需要指定该属性。

  1.3.5 底层数据库引擎不支持事务

  存储引擎不支持事务,支持事务

  1.3.6 事务和业务逻辑代码必须在一个线程中

  业务代码必须和事务的源码在同一个线程中才能被事务控制。 比如下面的代码,方法的子线程,内部的事务操作都不会被方法上的事务控制。 每个人都应该注意这一点。 这是因为在事务实现中使用它来实现同一个线程中的数据共享。

  

@Transactional    public void mothed() {        new Thread() {          事务操作        }.start();    }    

  1.4 死锁

  死锁是指两个或多个事务在同一个资源上相互占用wordpress插件,并请求锁定对方的资源,从而形成恶性循环的现象。

  MySQL内部有一套死锁检测机制。 一旦发生死锁,它会立即回滚一个事务,让另一个事务继续执行。 但是带数据库的网站源码,死锁存在资源利用率降低、进程无法获得正确结果等危害。

  1.4.1 SQL锁9种情况分析

  为了避免死锁,你需要学会分析:SQL锁是如何工作的? 可以在九种情况下讨论 SQL 锁:

  1.4.2 如何分析和解决死锁?

  分析和解决死锁的步骤如下:

  有兴趣的朋友可以看看我之前写的这篇文章:教你分析Mysql死锁问题

  1.5 主从延时注意事项

  先插入,后查询。 这种代码逻辑比较常见,可能会有问题。 一般数据库有主库和从库。 写是写到主库,读一般是从库读。 如果出现主从延时,很有可能你已经插入成功,但是无法查询。

  1.5.1 需要强一致性,考虑阅读主库

  如果是重要业务,需要强一致性,考虑直接读主库

  1.5.2 不需要强一致性,从库中读取

  如果是一般业务,可以接受暂时的数据不一致,优先从库读取。 因为从库可以分担主库的读写压力,提高系统吞吐量。

  1.6 新旧数据兼容1.6.1新增字段,考虑股票数据默认值

  在我们的日常开发中,随着业务需求的变化,我们经常需要在某个数据库表中添加一个字段。 比如在一个APP配置表中,需要增加一个场景编号字段,比如它的枚举值为01、02、03,那么我们要和业务对齐,新增的字段,默认值是多少旧数据的,是空的还是默认01。如果是NULL,程序代码必须处理空指针。

  1.6.2 如果新业务使用旧字段,考虑旧数据的值是否有坑

  如果开发时需要继续使用数据库表的老字段,有股票数据,就需要考虑老股票数据库的值是否存在陷阱。 比如我们的表中有一个字段。 在旧数据中,它的枚举值为01:超级管理员 02:管理员 03:普通用户。 假设业务需求是一般用户分为03查询用户和04操作用户,那我们在开发的时候就得考虑旧数据值的问题。

  1.7 一些经典SQL注意点 1.7.1 限制大分页问题

  limit large 是一个非常经典的SQL问题,我们一般都有这三种对应的解决方案

  方案一:如果id是连续的,可以返回上次查询的最大记录(偏移量),然后往下限制

  

select id,name from employee where id>1000000 limit 10.    

  选项 2:如果业务允许,限制页数:

  建议和业务商量,最后是否需要检查分页。 因为绝大多数用户是不会翻太多页的。 搜索页面也限制了页面数量,所以不存在限制大分页的问题。

  方案三:使用延迟关联或者子查询来优化超多分页场景。 (先快速定位到需要获取的id段,再进行关联)

  带数据库的网站源码

  

SELECT a.* FROM employee a, (select id from employee where 条件 LIMIT 1000000,10 ) b where a.id=b.id    

  1.7.2 当修改和查询大量数据时,考虑批处理。

  我们在更新或者查询数据库数据的时候,尽量避免循环操作数据库,可以考虑分批进行。 比如要插入10万条数据,一次插入500条带数据库的网站源码,执行200次即可。

  正例:

  

remoteBatchQuery(param);    

  反例:

    for(int i=0;i
[1]: https://xiaohack.oss-cn-zhangjiakou.aliyuncs.com/typecho/caiji/20230316/7de053ca8b6c6c2d6eb0b47b062b8b08_0.png
[2]: https://blog.xiaohack.org/3852.html
[3]: https://blog.xiaohack.org/3849.html
[4]: https://blog.xiaohack.org/3850.html
[5]: https://blog.xiaohack.org/3846.html
[6]: https://blog.xiaohack.org/3858.html
[7]: https://blog.xiaohack.org/attachment/3844/
[8]: https://blog.xiaohack.org/3839.html
[9]: https://blog.xiaohack.org/3847.html
[10]: https://blog.xiaohack.org/3848.html
[11]: https://blog.xiaohack.org/attachment/3844/
[12]: https://blog.xiaohack.org/?s=%E6%BA%90%E7%A0%81
[13]: https://blog.xiaohack.org/3853.html