1、基本命令
show databases; --查看当前所有的数据库 use 数据库名; --打开指定的数据库 show tables; --查看所有的表 describe/desc 表名; --显示表的信息 create database 数据库名; --创建一个数据库 exit --退出连接
2、操作数据库
1、创建数据库:CREATE DATABASE [IF NOT EXISTS] 数据库名;
2、删除数据库:DROP DATABASE [if EXISTS] 数据库名;
3、使用数据库:如果表名或者字段名是特殊字符,则需要带``
use 数据库名;
4、查看数据库:SHOW DATABASES;
1、修改数据库
修改表名 : ALTER TABLE 旧表名 RENAME AS 新表名
添加字段 : ALTER TABLE 表名 ADD字段名 列属性[属性]
修改字段 :
ALTER TABLE 表名 MODIFY 字段名 列类型[属性] ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[属性] 删除字段 : ALTER TABLE 表名 DROP 字段名
-- 删除表(如果存在再删除) DROP TABLE IF EXISTS teachers;
3、数据表的约束
1、主键约束
主键约束即primary key用于唯一的标识表中的每一行。被标识为主键的数据在表中是唯一的且其值不能为空。这点类似于我们每个人都有一个身份证号,并且这个身份证号是唯一的。 主键约束基本语法:字段名 数据类型 primary key;
create table student( id int primary key, name varchar(20) );
2、非空约束
非空约束即 NOT NULL指的是字段的值不能为空,基本的语法格式如下所示:
字段名 数据类型 NOT NULL;
3、默认值约束
默认值约束即DEFAULT用于给数据表中的字段指定默认值,即当在表中插入一条新记录时若未给该字段赋值,那么,数据库系统会自动为这个字段插人默认值;其基本的语法格式如下所示:
字段名 数据类型 DEFAULT 默认值;
4、唯一性约束
唯一性约束即UNIQUE用于保证数据表中字段的唯一性,即表中字段的值不能重复出现,其基本的语法格式如下所示:
字段名 数据类型 UNIQUE;
5、外键约束
外键约束即FOREIGN KEY常用于多张表之间的约束。基本语法如下:
-- 在创建数据表时语法如下: CONSTRAINT 外键名 FOREIGN KEY (从表外键字段) REFERENCES 主表 (主键字段) -- 将创建数据表创好后语法如下: ALTER TABLE 从表名 ADD CONSTRAINT 外键名 FOREIGN KEY (从表外键字段) REFERENCES 主表 (主键字段);
示例:创建一个学生表 MySQL命令:
create table student05( id int primary key, name varchar(20) );
示例:创建一个班级表 MySQL命令:
create table class( classid int primary key, studentid int );
示例:学生表作为主表,班级表作为副表设置外键, MySQL命令:
alter table class add constraint fk_class_studentid foreign key(studentid) references student05(id);
1、数据一致性概念
大家知道:建立外键是为了保证数据的完整和统一性。但是,如果主表中的数据被删除或修改从表中对应的数据该怎么办呢?很明显,从表中对应的数据也应该被删除,否则数据库中会存在很多无意义的垃圾数据。
2、删除外键
语法如下:
alter table 从表名 drop foreign key 外键名;
示例:删除外键 MySQL命令:
alter table class drop foreign key fk_class_studentid;
3、关于外键约束需要注意的细节
1、从表里的外键通常为主表的主键 2、从表里外键的数据类型必须与主表中主键的数据类型一致 3、主表发生变化时应注意主表与从表的数据一致性问题
4、DML语句
数据操作语言
1、INSERT基本语法
INSERT INTO 表名(字段名1,字段名2,...) VALUES (值 1,值 2,...); -- 示例 insert into student (id,name,age,gender) values (1,'bob',16,'male');
同时插入多条记录:在该方式中:(字段名1,字段名2,…)是可选的,它用于指定插人的字段名;(值 1,值 2,…),(值 1,值 2,…)表示要插人的记录,该记录可有多条并且每条记录之间用逗号隔开。 语法如下:
INSERT INTO 表名 [(字段名1,字段名2,...)]VALUES (值 1,值 2,…),(值 1,值 2,…),...;
2、UPDATE基本语法
UPDATE 表名 SET 字段名1=值1[,字段名2 =值2,…] [WHERE 条件表达式];
示例:将name为tom的记录的age设置为20并将其gender设置为female MySQL命令:
update student set age=20,gender='female' where name='tom';
3、DELETE基本语法
在该语法中:表名用于指定要执行删除操作的表;[WHERE 条件表达式]为可选参数用于指定删除的条件。
DELETE FROM 表名 [WHERE 条件表达式];
示例:删除age等于14的所有记录 MySQL命令:
delete from student where age=14
示例:删除student表中的所有记录 MySQL命令:
delete from student;
5、DQL查询数据
数据查询语言
-- 查询全部学生 SELECT * FROM student; -- 查询指定的字段 SELECT `LoginPwd`,`StudentName` FROM student; -- 别名 AS(可以给字段起别名,也可以给表起别名) SELECT `StudentNo` AS 学号,`StudentName` AS 学生姓名 FROM student AS 学生表; -- 函数 CONCAT(str1,str2,...) SELECT CONCAT('姓名',`StudentName`) AS 新名字 FROM student; -- 查询系统版本(函数) SELECT VERSION(); -- 用来计算(计算表达式) SELECT 100*53-90 AS 计算结果; -- 查询自增步长(变量) SELECT @@auto_increment_increment; -- 查询有哪写同学参加了考试,重复数据要去重 SELECT DISTINCT `StudentNo` FROM result;
1、条件查询
语法:
select 查询列表 from 表名 where 筛选条件; -- 查询考试成绩在95~100之间的,在MySQL中可使用AND关键字可以连接两个或者多个查询条件。 SELECT `StudentNo`,`StudentResult` FROM result WHERE `StudentResult`>=95 AND `StudentResult`<=100; -- &&,在使用SELECT语句查询数据时可使用OR关键字连接多个査询条件。在使用OR关键字时,只要记录满足其中任意一个条件就会被查询出来 SELECT `StudentNo`,`StudentResult` FROM result WHERE `StudentResult`>=95 && `StudentResult`<=100; -- BETWEEN AND,BETWEEN AND用于判断某个字段的值是否在指定的范围之内。如果字段的值在指定范围内,则将所在的记录将查询出来 SELECT `StudentNo`,`StudentResult` FROM result WHERE `StudentResult`BETWEEN 95 AND 100; -- 查询除了1000号以外的学生 SELECT `StudentNo`,`StudentResult` FROM result WHERE `StudentNo`!=1000; -- NOT SELECT `StudentNo`,`StudentResult` FROM result WHERE NOT `StudentNo`=1000; -- 查询名字含d的同学,%用于匹配任意长度的字符串。例如,字符串“a%”匹配以字符a开始任意长度的字符串,MySQL中可使用LIKE关键字可以判断两个字符串是否相匹配 SELECT `StudentNo`,`StudentName` FROM student WHERE `StudentName` LIKE '%d%'; -- limit 使用LIMIT限制查询结果的数量 select * from student order by age asc limit 3; -- 查询名字倒数第二个为d的同学,下划线通配符只匹配单个字符,如果要匹配多个字符,需要连续使用多个下划线通配符。例如,字符串“ab_”匹配以字符串“ab”开始长度为3的字符串,如abc、abp等等;字符串“a__d”匹配在字符“a”和“d”之间包含两个字符的字符串,如"abcd"、"atud"等等。 SELECT `StudentNo`,`StudentName` FROM student WHERE `StudentName` LIKE '%d_'; -- 查询1000,1001学员,IN关键字用于判断某个字段的值是否在指定集合中。如果字段的值恰好在指定的集合中,则将字段所在的记录将査询出来。 SELECT `StudentNo`,`StudentName` FROM student WHERE `StudentNo` IN (1000,1001);
2、分组查询
语法:
select 分组函数,分组后的字段 from 表 【where 筛选条件】 group by 分组的字段 【having 分组后的筛选】 【order by 排序列表】
使用GROUP BY进行分组查询
-- 创建数据库 DROP DATABASE IF EXISTS mydb; CREATE DATABASE mydb; USE mydb; -- 创建员工表 CREATE TABLE employee ( id int, name varchar(50), salary int, departmentnumber int ); -- 向员工表中插入数据 INSERT INTO employee values(1,'tome',2000,1001); INSERT INTO employee values(2,'lucy',9000,1002); INSERT INTO employee values(3,'joke',5000,1003); INSERT INTO employee values(4,'wang',3000,1004); INSERT INTO employee values(5,'chen',3000,1001); INSERT INTO employee values(6,'yukt',7000,1002); INSERT INTO employee values(7,'rett',6000,1003); INSERT INTO employee values(8,'mujk',4000,1004); INSERT INTO employee values(9,'poik',3000,1001);
1、 GROUP BY和聚合函数一起使用
统计各部门员工个数 MySQL命令:
select count(*), departmentnumber from employee group by departmentnumber;
统计部门编号大于1001的各部门员工个数 MySQL命令:
select count(*), departmentnumber from employee where departmentnumber>1001 group by departmentnumber;
2、 GROUP BY和聚合函数以及HAVING一起使用
WHERE和HAVING有什么区别?
WHERE是一个约束声明,使用WHERE约束来自数据库的数据,WHERE是在结果返回之前起作用的,WHERE中不能使用聚合函数。
HAVING是一个过滤声明,是在查询返回结果集以后对查询结果进行的过滤操作,在HAVING中可以使用聚合函数。另一方面,HAVING子句中不能使用除了分组字段和聚合函数之外的其他字段。
从性能的角度来说,HAVING子句中如果使用了分组字段作为过滤条件,应该替换成WHERE子句。因为WHERE可以在执行分组操作和计算聚合函数之前过滤掉不需要的数据,性能会更好。
统计工资总和大于8000的部门 MySQL命令:
select sum(salary),departmentnumber from employee group by departmentnumber having sum(salary)>8000;
3、使用ORDER BY对查询结果排序
从表中査询出来的数据可能是无序的或者其排列顺序不是我们期望的。为此,我们可以使用ORDER BY对查询结果进行排序
其语法格式如下所示:
SELECT 字段名1,字段名2,… FROM 表名 ORDER BY 字段名1 [ASC 丨 DESC],字段名2 [ASC | DESC];
在该语法中:字段名1、字段名2是查询结果排序的依据;参数 ASC表示按照升序排序,DESC表示按照降序排序;默认情况下,按照ASC方式排序。通常情况下,ORDER BY子句位于整个SELECT语句的末尾。
查询所有学生并按照年纪大小升序排列 MySQL命令:
select * from student order by age asc;
4、为表取别名
在查询操作时,假若表名很长使用起来就不太方便,此时可为表取一个別名,用该别名来代替表的名称。语法格式如下所示:
SELECT * FROM 表名 [AS] 表的别名 WHERE .... ;
将student改为stu查询整表 MySQL命令:
select * from student as stu;
5、为字段取别名
在查询操作时,假若字段名很长使用起来就不太方便,此时可该字段取一个別名,用该别名来代替字段的名称。语法格式如下所示:
SELECT 字段名1 [AS] 别名1 , 字段名2 [AS] 别名2 , ... FROM 表名 WHERE ... ;
将student中的name取别名为“姓名” 查询整表 MySQL命令:
select name as '姓名',id from student;
3、排序和分页
排序
语法:
select 查询列表 from 表 where 筛选条件 order by 排序列表 asc/desc
分页
语法:
select 查询列表 from 表 limit offset,pagesize;
6、表的关联关系
在实际开发中数据表之间存在着各种关联关系。在此,介绍MySQL中数据表的三种关联关系。
多对一
多对一(亦称为一对多)是数据表中最常见的一种关系。例如:员工与部门之间的关系,一个部门可以有多个员工;而一个员工不能属于多个部门只属于某个部门。在多对一的表关系 中,应将外键建在多的一方否则会造成数据的冗余。
多对多
多对多是数据表中常见的一种关系。例如:学生与老师之间的关系,一个学生可以有多个老师而且一个老师有多个学生。通常情况下,为了实现这种关系需要定义一张中间表(亦称为连接表)该表会存在两个外键分别参照老师表和学生表。
一对一
在开发过程中,一对一的关联关系在数据库中并不常见;因为以这种方式存储的信息通常会放在同一张表中。 接下来,我们来学习在一对多的关联关系中如果添加和删除数据。先准备一些测试数据,代码如下:
DROP TABLE IF EXISTS student; DROP TABLE IF EXISTS class; -- 创建班级表 CREATE TABLE class( cid int(4) NOT NULL PRIMARY KEY, cname varchar(30) ); -- 创建学生表 CREATE TABLE student( sid int(8) NOT NULL PRIMARY KEY, sname varchar(30), classid int(8) NOT NULL ); -- 为学生表添加外键约束 ALTER TABLE student ADD CONSTRAINT fk_student_classid FOREIGN KEY(classid) REFERENCES class(cid); -- 向班级表插入数据 INSERT INTO class(cid,cname)VALUES(1,'Java'); INSERT INTO class(cid,cname)VALUES(2,'Python'); -- 向学生表插入数据 INSERT INTO student(sid,sname,classid)VALUES(1,'tome',1); INSERT INTO student(sid,sname,classid)VALUES(2,'lucy',1); INSERT INTO student(sid,sname,classid)VALUES(3,'lili',2); INSERT INTO student(sid,sname,classid)VALUES(4,'domi',2);
1、关联查询
查询Java班的所有学生 MySQL命令:
select * from student where classid=(select cid from class where cname='Java');
2、关于关联关系的删除数据
请从班级表中删除Java班级。在此,请注意:班级表和学生表之间存在关联关系;要删除Java班级,应该先删除学生表中与该班相关联的学生。否则,假若先删除Java班那么学生表中的cid就失去了关联 删除Java班 MySQL命令:
delete from student where classid=(select cid from class where cname='Java'); delete from class where cname='Java';
7、多表连接查询
1、交叉连接查询
交叉连接返回的结果是被连接的两个表中所有数据行的笛卡儿积;比如:集合A={a,b},集合B={0,1,2},则集合A和B的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1),(b,2)}。所以,交叉连接也被称为笛卡尔连接,其语法格式如下:
SELECT * FROM 表1 CROSS JOIN 表2;
在该语法中:CROSS JOIN用于连接两个要查询的表,通过该语句可以查询两个表中所有的数据组合。
2、内连接查询
内连接(Inner Join)又称简单连接或自然连接,是一种非常常见的连接查询。内连接使用比较运算符对两个表中的数据进行比较并列出与连接条件匹配的数据行,组合成新的记录。也就是说在内连接查询中只有满足条件的记录才能出现在查询结果中。其语法格式如下:
SELECT 查询字段1,查询字段2, ... FROM 表1 [INNER] JOIN 表2 ON 表1.关系字段=表2.关系字段
在该语法中:INNER JOIN用于连接两个表,ON来指定连接条件;其中INNER可以省略。
准备数据,代码如下:
-- 若存在数据库mydb则删除 DROP DATABASE IF EXISTS mydb; -- 创建数据库mydb CREATE DATABASE mydb; -- 选择数据库mydb USE mydb; -- 创建部门表 CREATE TABLE department( did int (4) NOT NULL PRIMARY KEY, dname varchar(20) ); -- 创建员工表 CREATE TABLE employee ( eid int (4) NOT NULL PRIMARY KEY, ename varchar (20), eage int (2), departmentid int (4) NOT NULL ); -- 向部门表插入数据 INSERT INTO department VALUES(1001,'财务部'); INSERT INTO department VALUES(1002,'技术部'); INSERT INTO department VALUES(1003,'行政部'); INSERT INTO department VALUES(1004,'生活部'); -- 向员工表插入数据 INSERT INTO employee VALUES(1,'张三',19,1003); INSERT INTO employee VALUES(2,'李四',18,1002); INSERT INTO employee VALUES(3,'王五',20,1001); INSERT INTO employee VALUES(4,'赵六',20,1004);
查询员工姓名及其所属部门名称 MySQL命令:
select employee.ename,department.dname from department inner join employee on department.did=employee.departmentid;
3、外连接查询
在使用内连接查询时我们发现:返回的结果只包含符合查询条件和连接条件的数据。但是,有时还需要在返回查询结果中不仅包含符合条件的数据,而且还包括左表、右表或两个表中的所有数据,此时我们就需要使用外连接查询。外连接又分为左(外)连接和右(外)连接。其语法格式如下:
SELECT 查询字段1,查询字段2, ... FROM 表1 LEFT | RIGHT [OUTER] JOIN 表2 ON 表1.关系字段=表2.关系字段 WHERE 条件
由此可见,外连接的语法格式和内连接非常相似,只不过使用的是LEFT [OUTER] JOIN、RIGHT [OUTER] JOIN关键字。其中,关键字左边的表被称为左表,关键字右边的表被称为右表;OUTER可以省略。 在使用左(外)连接和右(外)连接查询时,查询结果是不一致的,具体如下: 1、LEFT [OUTER] JOIN 左(外)连接:返回包括左表中的所有记录和右表中符合连接条件的记录。 2、RIGHT [OUTER] JOIN 右(外)连接:返回包括右表中的所有记录和左表中符合连接条件的记录。
先准备数据,代码如下:
-- 若存在数据库mydb则删除 DROP DATABASE IF EXISTS mydb; -- 创建数据库mydb CREATE DATABASE mydb; -- 选择数据库mydb USE mydb; -- 创建班级表 CREATE TABLE class( cid int (4) NOT NULL PRIMARY KEY, cname varchar(20) ); -- 创建学生表 CREATE TABLE student ( sid int (4) NOT NULL PRIMARY KEY, sname varchar (20), sage int (2), classid int (4) NOT NULL ); -- 向班级表插入数据 INSERT INTO class VALUES(1001,'Java'); INSERT INTO class VALUES(1002,'C++'); INSERT INTO class VALUES(1003,'Python'); INSERT INTO class VALUES(1004,'PHP'); -- 向学生表插入数据 INSERT INTO student VALUES(1,'张三',20,1001); INSERT INTO student VALUES(2,'李四',21,1002); INSERT INTO student VALUES(3,'王五',24,1002); INSERT INTO student VALUES(4,'赵六',23,1003); INSERT INTO student VALUES(5,'Jack',22,1009);
准备这组数据有一定的特点,为的是让大家直观的看出左连接与右连接的不同之处 1、班级编号为1004的PHP班级没有学生 2、学号为5的学生Jack班级编号为1009,该班级编号并不在班级表中
1、 左(外)连接查询
左(外)连接的结果包括LEFT JOIN子句中指定的左表的所有记录,以及所有满足连接条件的记录。如果左表的某条记录在右表中不存在则在右表中显示为空。 查询每个班的班级ID、班级名称及该班的所有学生的名字 MySQL命令:
select class.cid,class.cname,student.sname from class LEFT OUTER JOIN student ON class.cid=student.classid;
展示结果分析: 1、分别找出Java班、C++班、Python班的学生 2、右表的Jack不满足查询条件故其没有出现在查询结果中 3、虽然左表的PHP班没有学生,但是任然显示了PHP的信息;但是,它对应的学生名字为NULL
2、右(外)连接查询
右(外)连接的结果包括RIGHT JOIN子句中指定的右表的所有记录,以及所有满足连接条件的记录。如果右表的某条记录在左表中没有匹配,则左表将返回空值。 查询每个班的班级ID、班级名称及该班的所有学生的名字 MySQL命令:
select class.cid,class.cname,student.sname from class right outer join student on class.cid=student.classid;
展示结果分析: 1、分别找出Java班、C++班、Python班的学生 2、左表的PHP班不满足查询条件故其没有出现在查询结果中 3、虽然右表的jack没有对应班级,但是任然显示Jack的信息;但是,它对应的班级以及班级编号均为NULL
8、子查询
子查询是指一个查询语句嵌套在另一个查询语句内部的查询;该查询语句可以嵌套在一个 SELECT、SELECT…INTO、INSERT…INTO等语句中。在执行查询时,首先会执行子查询中的语句,再将返回的结果作为外层查询的过滤条件。在子査询中通常可以使用比较运算符和IN、EXISTS、ANY、ALL等关键字。 本质:在 where子句中嵌套一个子查询语句
准备数据,代码如下:
DROP TABLE IF EXISTS student; DROP TABLE IF EXISTS class; -- 创建班级表 CREATE TABLE class( cid int (4) NOT NULL PRIMARY KEY, cname varchar(20) ); -- 创建学生表 CREATE TABLE student ( sid int (4) NOT NULL PRIMARY KEY, sname varchar (20), sage int (2), classid int (4) NOT NULL ); -- 向班级表插入数据 INSERT INTO class VALUES(1001,'Java'); INSERT INTO class VALUES(1002,'C++'); INSERT INTO class VALUES(1003,'Python'); INSERT INTO class VALUES(1004,'PHP'); INSERT INTO class VALUES(1005,'Android'); -- 向学生表插入数据 INSERT INTO student VALUES(1,'张三',20,1001); INSERT INTO student VALUES(2,'李四',21,1002); INSERT INTO student VALUES(3,'王五',24,1003); INSERT INTO student VALUES(4,'赵六',23,1004); INSERT INTO student VALUES(5,'小明',21,1001); INSERT INTO student VALUES(6,'小红',26,1001); INSERT INTO student VALUES(7,'小亮',27,1002);
1、带比较运算符的子查询
比较运算符前面我们提到过得,就是>、<、=、>=、<=、!=等 查询张三同学所在班级的信息 MySQL命令:
select * from class where cid=(select classid from student where sname='张三');
查询比张三同学所在班级编号还大的班级的信息 MySQL命令:
select * from class where cid>(select classid from student where sname='张三');
2、带EXISTS关键字的子查询
EXISTS关键字后面的参数可以是任意一个子查询, 它不产生任何数据只返回TRUE或FALSE。当返回值为TRUE时外层查询才会 执行 假如王五同学在学生表中则从班级表查询所有班级信息 MySQL命令:
select * from class where exists (select * from student where sname='王五');
3、带ANY关键字的子查询
ANY关键字表示满足其中任意一个条件就返回一个结果作为外层查询条件。 查询比任一学生所属班级号还大的班级编号 MySQL命令:
select * from class where cid > any (select classid from student);
4、带ALL关键字的子查询
ALL关键字与ANY有点类似,只不过带ALL关键字的子査询返回的结果需同时满足所有内层査询条件。
查询比所有学生所属班级号还大的班级编号 MySQL命令:
select * from class where cid > all (select classid from student);
9、SQL注入
参考答案
SQL注入的原理是将SQL代码伪装到输入参数中,传递到服务器解析并执行的一种攻击手法。也就是说,在一些对SERVER端发起的请求参数中植入一些SQL代码,SERVER端在执行SQL操作时,会拼接对应参数,同时也将一些SQL注入攻击的“SQL”拼接起来,导致会执行一些预期之外的操作。
举个例子:
比如我们的登录功能,其登录界面包括用户名和密码输入框以及提交按钮,登录时需要输入用户名和密码,然后提交。此时调用接口/user/login/ 加上参数username、password,首先连接数据库,然后后台对请求参数中携带的用户名、密码进行参数校验,即SQL的查询过程。假设正确的用户名和密码为ls和123456,输入正确的用户名和密码、提交,相当于调用了以下的SQL语句。
SELECT * FROM user WHERE username = 'ls' AND password = '123456'
SQL中会将#及--以后的字符串当做注释处理,如果我们使用 ' or 1=1 # 作为用户名参数,那么服务端构建的SQL语句就如下:
select * from user where username='' or 1=1 #' and password='123456'
而#会忽略后面的语句,而1=1属于常等型条件,因此这个SQL将查询出所有的登录用户。其实上面的SQL注入只是在参数层面做了些手脚,如果是引入了一些功能性的SQL那就更危险了,比如上面的登录功能,如果用户名使用这个 ' or 1=1;delete * from users; #,那么在";"之后相当于是另外一条新的SQL,这个SQL是删除全表,是非常危险的操作,因此SQL注入这种还是需要特别注意的。
如何解决SQL注入
1.严格的参数校验 参数校验就没得说了,在一些不该有特殊字符的参数中提前进行特殊字符校验即可。 2.PreparedStatement对象 PreparedStatement
是Statement
的子类,与其相比,可以防止SQL注入,并且效率更高
各种锁机制
1、数据库悲观锁 (select/update)等待
select * from xxxx where id = 1 for update; 悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,需要根据实际情况选用。 另外要注意的是,id 字段一定是主键或者唯一索引,不然可能造成锁表的结果,处理起来会 非常麻烦。
2、数据库乐观锁(update)版本号叠加
这种方法适合在更新的场景中, update t_goods set count = count -1 , version = version + 1 where good_id=2 and version = 1 根据 version 版本,也就是在操作库存前先获取当前商品的 version 版本号,然后操作的时候 带上此 version 号。 我们梳理下,我们第一次操作库存时,得到 version 为 1,调用库存服务 version 变成了 2;但返回给订单服务出现了问题,订单服务又一次发起调用库存服务,当订 单服务传如的 version 还是 1,再执行上面的 sql 语句时,就不会执行;因为 version 已经变 为 2 了,where 条件就不成立。这样就保证了不管调用几次,只会真正的处理一次。 乐观锁主要使用于处理读多写少的问题
3、业务层分布式锁
如果多个机器可能在同一时间同时处理相同的数据,比如多台机器定时任务都拿到了相同数 据处理,我们就可以加分布式锁,锁定此数据,处理完成后释放锁。获取到锁的必须先判断 这个数据是否被处理过