Java面试知识点(全)- DB mysql面试知识点

news2024/11/30 20:49:51

[Java面试知识点(全)
导航: https://nanxiang.blog.csdn.net/article/details/130640392
注:随时更新

SQL优化

  1. r = m y s q l q u e r y ( " S E L E C T u s e r n a m e F R O M u s e r W H E R E s i g n u p d a t e > = ′ r = mysql_query("SELECT username FROM user WHERE signup_date >= ' r=mysqlquery("SELECTusernameFROMuserWHEREsignupdate>=today’");
    上面两条SQL语句的差别就是 CURDATE() ,MySQL的查询缓存对这个函数不起作用。所以,像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是会不定的易变的。所以,你所需要的就是用一个变量来代替MySQL的函数,从而开启缓存。
  2. EXPLAIN 你的 SELECT 查询
    使用 EXPLAIN 关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。
    EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的……等等,等等。
  3. 当只要一行数据时使用 LIMIT 1
    当你查询表的有些时候,你已经知道结果只会有一条结果,但因为你可能需要去fetch游标,或是你也许会去检查返回的记录数。
    在这种情况下,加上 LIMIT 1 可以增加性能。这样一样,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。
    下面的示例,只是为了找一下是否有“中国”的用户,很明显,后面的会比前面的更有效率。(请注意,第一条中是Select *,第二条是Select 1)
// 没有效率的:
$r = mysql_query("SELECT * FROM user WHERE country = 'China'");
if (mysql_num_rows($r) > 0) {
    // ...
}

// 有效率的:
$r = mysql_query("SELECT 1 FROM user WHERE country = 'China' LIMIT 1");
if (mysql_num_rows($r) > 0) {
    // ...
}
  1. 为搜索字段建索引
    数据库——索引
  2. 在Join表的时候使用相当类型的例,并将其索引
    如果你的应用程序有很多 JOIN 查询,你应该确认两个表中Join的字段是被建过索引的。这样,MySQL内部会启动为你优化Join的SQL语句的机制。
    而且,这些被用来Join的字段,应该是相同的类型的。例如:如果你要把 DECIMAL 字段和一个 INT 字段Join在一起,MySQL就无法使用它们的索引。对于那些STRING类型,还需要有相同的字符集才行。(两个表的字符集有可能不一样)
  3. 千万不要 ORDER BY RAND()
    如果你真的想把返回的数据行打乱了,你有N种方法可以达到这个目的。这样使用只会让你的数据库的性能呈指数级的下降。这里的问题是:MySQL会不得不去执行RAND()函数(很耗CPU时间),而且这是为了每一行记录去记行,然后再对其进行排序。就算你用了limit 1也无济于事(因为要排序)
    下面的示例是随机挑一条记录
-- 千万不要这样做
SELECT last_name FROM actor ORDER BY RAND() LIMIT 1;-- 时间: 0.252s

-- 这样会更好
SELECT COUNT(*) FROM actor;
-- 然后在程序代码里,返回0~count(*)-1的一个随机数rand,再通过以下sql去查表
SELECT last_name FROM actor limit rand, 1;
  1. 避免 SELECT *
    从数据库里面读出的数据越多,查询速度越慢
    如果数据库拂去其和WEB服务器是两台独立的机器时,还会增加网络传输的负载
    所以,你应该养成需要什么数据取什么数据的习惯。
  2. 永远为每张表设置一个主键
    我们应该为数据库里的每张表都设置一个ID作为其主键,而且最好是一个INT型的(推荐使用UNSIGN),并设置上自动增加的AUTO_INCREMENT标志。
    就算是你users表有一个主键叫“email”的字段,你也别让他成为主键。使用VARCHAR类型来当主键会使得性能下降。另外,在你的程序中,你应该使用表的主键来构造你的数据机构。

而且,在MySQL数据引擎下,还有一些操作需要使用主键,在这些情况下,主键的性能和设置变得非常重要,比如,集群,分区….(不懂这个。。)
在这里,只有一个情况是例外,那就是“关联表”的“外键”,也就是说,这个表的主键,通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如,有个“学生表”有学生的ID,有个“课程表”有课程ID,那么“成绩表”就是“关联表”,其关联了学生表和课程表,在成绩表中,学生ID和课程ID叫“外键”其共同组成主键。

  1. 使用 ENUM 而不是 VARCHAR
    ENUM类型是非常快和紧凑的。在实际上,其保存的是TINYINT,但其外表上显示为字符串。这样一来 ,用这个字段来做一些选项列表变得相当的完美。
    如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,你知道这些字段的取值是有限而且固定的,那么,你应该使用 ENUM 而不是 VARCHAR。
    MySQL也有一个“建议”(见第十条)告诉你怎么去重新组织你的表结构。当你有一个 VARCHAR 字段时,这个建议会告诉你把其改成 ENUM 类型。使用 PROCEDURE ANALYSE() 你可以得到相关的建议。

  2. 从 PROCEDURE ANALYSE() 取得建议
    PROCEDURE ANALYSE() 会让 MySQL 帮你去分析你的字段和其实际的数据,并会给你一些有用的建议。只有表中有实际的数据,这些建议才会变得有用,因为要做一些大的决定是需要有数据作为基础的。
    例如,如果你创建了一个 INT 字段作为你的主键,然而并没有太多的数据,那么,PROCEDURE ANALYSE()会建议你把这个字段的类型改成 MEDIUMINT 。或是你使用了一个 VARCHAR 字段,因为数据不多,你可能会得到一个让你把它改成 ENUM 的建议。这些建议,都是可能因为数据不够多,所以决策做得就不够准。
    一定要注意,这些只是建议,只有当你的表里的数据越来越多时,这些建议才会变得准确。一定要记住,你才是最终做决定的人。

  3. 尽可能的使用 NOT NULL
    除非你有一个很特别的原因去使用 NULL 值,你应该总是让你的字段保持 NOT NULL。这看起来好像有点争议,请往下看。
    首先,问问你自己“Empty”和“NULL”有多大的区别(如果是INT,那就是0和NULL)?如果你觉得它们之间没有什么区别,那么你就不要使用NULL。(你知道吗?在 Oracle 里,NULL 和 Empty 的字符串是一样的!)
    不要以为 NULL 不需要空间,其需要额外的空间,并且,在你进行比较的时候,你的程序会更复杂。 当然,这里并不是说你就不能使用NULL了,现实情况是很复杂的,依然会有些情况下,你需要使用NULL值。

  4. PreparedStatement
    PreparedStatement很像存储过程,是一种运行在后台的SQL语句集合,我们可以使用PreparedStatement获得很多好处,无论是性能问题还是安全问题。
    PreparedStatement可以检查一些你绑定好的变量,这样可以保护你的程序不会受到“SQL注入式”攻击。当然,你也可以手动去检查你的这些变量,然而,手动的检查容易出问题,而且很经常会被程序员忘了。当我们使用一些framework或是ORM的时候,这样的问题会好一些。
    在性能方面,当一个相同的查询被使用很多次的时候,这会为你带来可观的性能优势,你可以给这些PreparedStatement定义一些参数,而MySQL只会解析一次。

  5. 无缓冲的查询
    正常情况下,当你在你的脚本中执行一个SQL语句的时候,你的程序会停在那里直到这个SQL语句返回,然后你的程序再往下继续执行。你可以使用无缓冲查询来改变这个行为。尤其是那些会产生大量结果的查询语句,并且,你不需要等到所有第的结果都返回,只需要第一行的数据返回的时候,你就可以马上开始工作于查询结果了。

  6. 把IP地址存成 UNSIGNED INT
    很多程序员都会创建一个varchar(15)字段来存放字符串形式的IP而不是整形的IP。如果你用整形来存放,只需要4个字节,并且你可以有定长的字段。而且,这会为你带来查询上的优势,尤其是当你需要使用这样的WHERE条件:between ip1 and ip2。
    我们必须使用UNSIGNED INT,因为IP地址会使用整个32位的无符号整形。
    而你的查询,你可以使用INET_ATON()来把一个字符串IP转成一个整形,并使用INET_NTOA()把一个整形转成一个字符串IP。如下:

-- 字符串IP转成整形
select INET_ATON('127.0.0.1') from dual;
-- 整形转成字符串ip
select INET_NTOA(2130706433) from dual;
  1. 固定长度的表会更快
    如果表中的所有字段都是“固定长度的”,整个表会被认为是“static”或“”“fixed-length”。例如,表中没有如下类型的字段:VARCHAR, TEXT, BLOB。只要你包括了其中一个这些字段,那么这个表就不是“固定长度静态表”了,这样,MySQL引擎会用另外一种方法来处理。
    固定长度的表会提高性能,因为MySQL搜寻的会更快一些,因为这些固定的长度是很容易计算下一个数据的偏移量的,所以读取的自然也会很快。而如果字段不是定长的,那么,每一次要找下一条的话,需要程序找到主键。
    并且,固定长度的表更容易被缓存和重建。不过,唯一的副作用是,固定长度的字段会浪费一些空间,因为定长的字段无论你要弄个不用,它都是要分配那么多的空间。
    使用“垂直分割”技术(见下一条),你可以分割你的表成为两个一个是定长的,一个则不是定长的。

  2. 垂直分割
    “垂直分割”是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段的数目,从而达到优化的目的。
    示例一:
    在User表中有一个字段是家庭地址,这个字段是可选字段,相比起,而且你在数据库操作的时候除了个人信息外,你并不需要经常读取或是改写这个字段。那么,为什么不把这个字段放到另一张表呢?这样会让你的表有更好的性能。
    示例二:
    你有一个叫“last_login”字段,它会在每次用户登录的时候被更新。但是,每次更新时会导致该表的查询缓存被清空。所以,你可以把这个字段放到另外一个表中,这样就不会影响ID,用户名,用户角色的不停的读取了,因为查询缓存会帮你增加很多性能。
    另外,你需要注意的是,这些被分出去的字段所形成的表,你不会经常性的去join他们,不然的话,这样的性能会比不分割时还要差,而且,是指数级的下降。

  3. 拆分大的 DELETE 或 INSERT 语句
    如果你需要在一个在线的网站上去执行一个大的DELETE或INSERT查询,你需要非常小心,要避免你的操作会让你的整个网站停止响应。因为这两个操作是会锁表的,表一锁住了,别的操作都进不来了。
    Apache会有很多的子进程或线程。所以,其工作起来相当有效率,而我们的服务器也不希望有太多的子线程,线程和数据库链接,这时极大的占服务器资源的事情,尤其是内存。
    如果你把你的表锁上一段时间,比如30s,那么对于一个有很高访问量的站点来说,这30s所积累的访问进程/线程,数据库链接,打开的文件数,可能不仅仅会让你的web服务器crash,还可能会让你的整台服务器马上挂了。
    所以,如果你有一个大的处理,你要把他拆分,使用LIMIT条件是一个好的方法。下面是一个示例:

while(true){
  //每次只做1000条
  mysql_query("DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000");
  if(mysql_affected_rows()==0){
     //没得可删了,退出
      break;
  }
  usleep(5000);
}
  1. 越小的列会越快
    对于大多数的数据引擎来说,硬盘操作可能是最大的瓶颈。所以,把你的数据变得紧凑会对这种情况非常有帮助,因为这减少了对硬盘的访问。
    可以参考MySQL的文档Storage Requirements查看所有的数据类型。
    如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理由使用INT来做主键,使用MEDIUMINT ,SMALLINT或是更小的TINYINT会更经济一些。如果你不需要记录时间,使用DATE要比DATETIME好得多。
    当然,你也需要留足够的扩展空间,不然,你日后要干这个事,你会死的很难看,参考Slashdot的例子,一个简单的ALTER TABLE 语句花了3个多小时,因为里面有一千六百万条数据。

  2. 选择正确的存储引擎
    在MySQL中有两个存储引擎MyISAM和Innodb,每个引擎都会有利弊。MyISAM
    适合于一些需要大量查询的应用,但是对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM对于SELECT COUNT(*)这种操作是超快务必的。
    Innodb的趋势会是一个非常复杂的存储引擎,对于一些小的应用,他会比MyiSAM还慢。他是支持“行锁”的,所以在写操作比较多 的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。

  3. 使用一个对象关系映射器(Object Relational Mapper)
    使用ORM,你能够获得可靠的性能增涨。一个ORM可以做到所有事情,也能被手动的编写出来。但是,这需要一个高级专家。
    ORM的最重要的是“Lazy loading”,也就是说,只有在需要的去取值的时候才会去真正的去做。但你也需要小心这种机制的副作用,因为这很可能会因为要去创建很多很多小的查询反而会降低性能。(不懂最后这句。。)
    ORM还可以把你的SQL语句打包成一个事务,这会比单独执行他们快得多得多。(啥意思?)

  4. 小心“永久链接”
    “永久链接”的目的是用来减少重新创建MySQL链接的次数。当一个链接呗创建了,它会永远处在链接的状态,就算是数据库操作已经结束了,而且,自动我们的Apache开始重用它的子进程后——也就是说,下一次的HTTP请求会重用Apache的子进程,并重用相同的MySQL链接。
    在理论上来说,这听起来非常的不错。但是从个人经验(也是大多数人的)上来说,这个功能制造出来的麻烦事更多。因为,你只有有限的链接数,内存问题,文件句柄数,等等。
    而且,Apache 运行在极端并行的环境中,会创建很多很多的了进程。这就是为什么这种“永久链接”的机制工作地不好的原因。在你决定要使用“永久链接”之前,你需要好好地考虑一下你的整个系统的架构。

数据库优化

数据库访问优化法则简介
要正确的优化SQL,我们需要快速定位能性的瓶颈点,也就是说快速找到我们SQL主要的开销在哪里?而大多数情况性能最慢的设备会是瓶颈点,如下载时网络速度可能会是瓶颈点,本地复制文件时硬盘可能会是瓶颈点,为什么这些一般的工作我们能快速确认瓶颈点呢,因为我们对这些慢速设备的性能数据有一些基本的认识,如网络带宽是2Mbps,硬盘是每分钟7200转等等。因此,为了快速找到SQL的性能瓶颈点,我们也需要了解我们计算机系统的硬件基本性能指标,下图展示的当前主流计算机性能指标数据。

在这里插入图片描述

从图上可以看到基本上每种设备都有两个指标:
延时(响应时间):表示硬件的突发处理能力;
带宽(吞吐量):代表硬件持续处理能力。

从上图可以看出,计算机系统硬件性能从高到代依次为:
CPU——Cache(L1-L2-L3)——内存——SSD硬盘——网络——硬盘
由于SSD硬盘还处于快速发展阶段,所以本文的内容不涉及SSD相关应用系统。
根据数据库知识,我们可以列出每种硬件主要的工作内容:
CPU及内存:缓存数据访问、比较、排序、事务检测、SQL解析、函数或逻辑运算;
网络:结果数据传输、SQL请求、远程数据库访问(dblink);
硬盘:数据访问、数据写入、日志记录、大数据量排序、大表连接。

根据当前计算机硬件的基本性能指标及其在数据库中主要操作内容,可以整理出如下图所示的性能基本优化法则:

在这里插入图片描述

这个优化法则归纳为5个层次:
1、减少数据访问(减少磁盘访问)
尽量使用索引
2、返回更少数据(减少网络传输或磁盘访问)
返回需要的字段;
3、减少交互次数(减少网络传输)
批处理;查询增加fatch size;优化业务;存储过程
4、减少服务器CPU开销(减少CPU及内存开销)
使用绑定变量(直接变量,减少计算);合理使用排序;减少比较操作(<,>,like)
5、利用更多资源(增加资源)

并行,多线程

由于每一层优化法则都是解决其对应硬件的性能问题,所以带来的性能提升比例也不一样。传统数据库系统设计是也是尽可能对低速设备提供优化方法,因此针对低速设备问题的可优化手段也更多,优化成本也更低。我们任何一个SQL的性能优化都应该按这个规则由上到下来诊断问题并提出解决方案,而不应该首先想到的是增加资源解决问题。
以下是每个优化法则层级对应优化效果及成本经验参考:

优化法则 性能提升效果 优化成本
减少数据访问 1~1000 低
返回更少数据 1~100 低
减少交互次数 1~20 低
减少服务器CPU开销 1~5 低
利用更多资源 @~10 高

DB&SQL优化

当你在sql客户端(如命令行或者navicat)提交一条sql后,sql解析引擎会被启动,此时,sql引擎会去解析优化这条sql,比如有时候你发现你写的sql 查询条件中的数据类型虽然和数据库表定义的数据类型不一致,但是还是能够得到正确的执 行,是因为数据库引擎帮你做了自动转化,比如

select * from user where telephone=13937147647 telephone为varchar类型

虽然类型不一致,但是sql会得到正确执行,是因为数据库引擎帮你做了自动类型转化

不仅如此,数据库表定义了多个索引,你写的sql可能都会命中,sql引擎会帮你选择最优的一个,这些都是此阶段完成的
接下来,数据库引擎会拿着优化好的sql命令语义去硬盘中查找数据,然后将查找到的数据返回(如果此时返回的结果集过大,会造成数据库IO繁忙,会大大损伤sql效率,所以一般我们都使用分页的原因就在于此)
我们熟悉了sql执行过程之后,开始了解各种优化方式吧:

索引

0、索引,建立索引是数据库优化各种方案之中成本最低,见效最快的解决方案,一般来讲,数据库规模在几十万和几百万级别的时候见效最快,即便是有不太复杂的表关联,也能大幅度提高sql的运行效率,这个在我们以前的项目应用中,有非常深刻的体会,本来耗时50s的sql,在增加索引后可以提升到1-2s,而且不需要有代码改动,成本低廉,见效明显
建立索引需要注意的地方
a、索引一般加在查询条件的关键字上,如果有多个查询条件关键字,还可以添加组合索引,写sql的时候需要注意,索引字段和sql字段需要保持一致,否则索引会无效,比如
简单粗暴一点儿,我直接使用我们主数据数据库(测试库)中的md_house_property_info表中的source_house_code_no字段,这个字段在数据库中被定义为了varchar类型,定义了多个索引,都包含了source_house_code_no字段
大家看,source_house_code_no我写成varchar类型的时候,是可以走索引的
当我写成数字的时候,sql能够正确执行,但是却没有命中索引
大家再细心一点儿会发现,我这里面有个possiable_keys,这个是指的可能命中的索引,此处出现了两个,但是数据库引擎会选择最优的一个idx_source_house_code_no,这个过程我在开始有介绍了
b、不要在查询=前面使用函数,否则会导致索引不生效,举个栗子,where str=substring(“hello world”,6,8),这样是可以走索引的,但是where substring(str,6,8)=“hello world” 是不会命中索引的
c、建立索引的字段要区分度比较高,比如user表中有一个性别字段,性别字段无非男女两种值,区分度不好,建立索引效果不好,要选择区分度高的字段
d、建立组合索引,可以持续提升sql运行效率,但是也不要盲目,同样的要注意区分度,如果区分度不够高,就不要加了,多个字段,尽可能把区分度高的字段放在前面,另外,还要注意索引长度,这个索引要同时兼顾索引长度和区分度的平衡
e、索引会大幅提升查询效率,但是也会损耗查询后修改效率,要注意兼顾平衡,使用在一次插入,多次查询的表上效果最好,同时要注意的是,组合索引会不可避免的增加索引长度,会增加索引存储空间,注意索引长度和区分度平衡
f、后来因为工作需要,意外发现mysql居然支持全文索引,没测试过效率,正常使用全文索引都是使用 lunce,以及在其之上的solr和现在正火的elastisearch,后面可以单独来说

分库分表分区

分库,可以按照业务分库,分流数据库并发压力,使数据库表更加有条理性,最起码更加好找吧,我们当时是把查询库和系统库(增删改比较频繁的表)分开了,这样如果有大查询,不影响系统库
分表,刚才说了,索引适合应对百万级别的数据量,千万级别数据量使用的好,勉强也能凑合,但如果是上亿级别的数据量,索引就无能为力了,因为单索引文件可能就已经上百兆或者更多了,那么,轮到我们的分表分区登场了
分表的方法有很多种
a、如果这个业务是有流程的,那么我们通常会设计一个历史表或者归档表,用来存放历史数据,这样能保证实时数据效率比较高
b、针对某一张大表,可以根据查询条件分成多张表,比如时间,我们可以将半个月或者10天的数据放到一张表里(看具体数据量,个人认为3000W是个上限,最好控制到百万级别),每过10天,我们就自动创建一张数据库表,然后将数据插入,如此,按照时间查询,就要先定位去那种表中去取数,这样,效率能够得到大幅度提升,当然,这么解决也有问题,比如跨表,需要union多张表,而且跨表没法支持索引
c、上面的方法是我们直接通过程序和数据库实现的最原始的分表解决方案,现在市面上有一些成熟的软件如mycat,也是支持分表的,我们之前从事的公司有个专门做分布式数据库的,这些产品出现跨表,可以不使用程序union了,而且还是使索引生效,但是需要对产品有一定的掌握
d、一般来讲,数据库中的大表毕竟只是一少部分,仅需要对这少部分大表进行分表就可以了,没必要小表也进行分表,增加维护开发难度

分区
分区的实现道理和分表一样,也是将相应规则的数据放在一起,唯一不同的是分区你只需要设定好分区规则,插入的数据会被自动插入到指定的区里,当然查询的时候也能很快查询到需要区,相当于是分表对外透明了,出现跨表数据库自动帮我们合并做了处理,使用起来比分表更加方便,但是分区也有自己的问题,每一个数据库表的并发访问是有上限的,也就是说,分表能够抗高并发,而分区不能,如何选择,要考虑实际情况

数据库引擎

也是偶尔听一个dba同事提到的,有一次我跟dba同事抱怨,我的数据库查询太慢,有没有好的优化方法,他一开始就问,数据量多大,有没有索引,使用的什么数据库引擎,这时我才意识到原来数据库引擎也算是一种优化方案
mysql比较常用的数据库引擎有两种,一种是innodb、一种是myisam
我当时做过一个千万级数据量复杂sql测试,myisam的效率大概能够比innodb快1-2倍,虽然效率提升不是很明显,但是也有提升,后来查过一些资料,说之所以mysiam快,是因为他的数据存储结构、索引存储结构和innodb不一样的,mysiam的索引结构是在内存中存的
当然,mysiam也有弱点,那就是他是表级锁,而innodb是行级锁,所以,mysiam适用于一次插入,多次查询的表,或者是读写分离中的读库中的表,而对于修改插入删除操作比较频繁的表,就很不合适了

预处理

一般来说,实时数据(当天的数据)还是比较有限的,真正数据量比较大的是历史数据,基于大表历史数据的查询,如果再涉及一些大表关联,这种sql是非常难以优化的
a、实时数据(当天数据)
通过对对业务的抽象,可以放在缓存里面,提升系统运行效率
b、历史数据,大数据表历史数据且有表关联,通过常规sql难以优化,但是该数据通常有个共性,就是第二天去查询前一天的数据做分析报表,也就是说对时效性要求不高,这种情况的解决方案是预处理
做法是将这些复杂表关联sql写成个定时任务在半夜执行,将执行的结果存入到一张结果表中,第二天直接查询结果表,如此,效率能得到十分明显提升
c、和b类似,可以将表关联结果存入solr或者elastisearch中,以此提升效率,目前我们的项目就是如此处理
mysql like查询
大家都知道,like “%str%” 不支持索引, "str%"号是支持索引的
因此,如果业务允许,可以使用前匹配的方法是数据库快速定位到数据,在结果集中再进行like匹配,如果这个结果集不是很大,是可以大幅提升运行效率的,这个需要对业务和程序有灵活的变通
如果业务实在不允许前匹配,那就可以采用solr或者elastisearch来进行模糊匹配,但是进行模糊匹配有个前提,原始数据是字符串的话,不要带有特殊符号,如#,&,% 等,这样会造成分词不准,最终导致查询结果不正确

读写分离

 在数据库并发大的情况下,最好的做法就是进行横向扩展,增加机器,以提升抗并发能力,而且还兼有数据备份功能

MySQL事物

一、事务定义
Transaction,事务:一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)
一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成
事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同

二、转账操作理解事务
关于银行账户转账操作,账户转账是一个完整的业务,最小的单元,不可再分————————也就是说银行账户转账是一个事务
以下是银行账户表t_act(账号、余额),进行转账操作
actno balance
1 500
2 100
转账操作
update t_act set balance=400 where actno=1;
update t_act set balance=200 where actno=2;
以上两台DML语句必须同时成功或者同时失败。最小单元不可再分,当第一条DML语句执行成功后,并不能将底层数据库中的第一个账户的数据修改,只是将操作记录了一下;这个记录是在内存中完成的;当第二条DML语句执行成功后,和底层数据库文件中的数据完成同步。若第二条DML语句执行失败,则清空所有的历史操作记录,要完成以上的功能必须借助事务

三、事务四大特征(ACID)
原子性(A):事务是最小单位,不可再分
一致性©:事务要求所有的DML语句操作的时候,必须保证同时成功或者同时失败
隔离性(I):事务A和事务B之间具有隔离性
持久性(D):是事务的保证,事务终结的标志(内存的数据持久到硬盘文件中)

四、关于事务的一些术语
开启事务:Start Transaction
事务结束:End Transaction
提交事务:Commit Transaction
回滚事务:Rollback Transaction

五、和事务相关的两条重要的SQL语句(TCL)
commit:提交
rollback:回滚
六、事务开启的标志?事务结束的标志?
开启标志:

  • 任何一条DML语句(insert、update、delete)执行,标志事务的开启
    结束标志(提交或者回滚):
  • 提交:成功的结束,将所有的DML语句操作历史记录和底层硬盘数据来一次同步
  • 回滚:失败的结束,将所有的DML语句操作历史记录全部清空

七、事物与数据库底层数据
在事物进行过程中,未结束之前,DML语句是不会更改底层数据,只是将历史操作记录一下,在内存中完成记录。只有在事物结束的时候,而且是成功的结束的时候,才会修改底层硬盘文件中的数据

八、在MySQL中,事务提交与回滚
在MySQL中,默认情况下,事务是自动提交的,也就是说,只要执行一条DML语句就开启了事物,并且提交了事务
以上的自动提交机制是可以关闭的
对t_user进行提交和回滚操作
提交操作(事务成功)
start transaction
DML语句
commit

mysql> start transaction;#手动开启事务
mysql> insert into t_user(name) values('pp');
mysql> commit;#commit之后即可改变底层数据库数据
mysql> select * from t_user;
+----+------+
| id | name |
+----+------+
|  1 | jay  |
|  2 | man  |
|  3 | pp   |
+----+------+
3 rows in set (0.00 sec)

回滚操作(事务失败)
start transaction
DML语句
rollback

mysql> start transaction;
mysql> insert into t_user(name) values('yy');
mysql> rollback;
mysql> select * from t_user;
+----+------+
| id | name |
+----+------+
|  1 | jay  |
|  2 | man  |
|  3 | pp   |
+----+------+
3 rows in set (0.00 sec)
12

九、事务四大特性之一隔离性(isolation)

事物A和事物B之间具有一定的隔离性

隔离性有隔离级别(4个)
读未提交:read uncommitted
读已提交:read committed
可重复读:repeatable read
串行化:serializable

1、 read uncommitted

  • 事物A和事物B,事物A未提交的数据,事物B可以读取到
  • 这里读取到的数据叫做“脏数据”
  • 这种隔离级别最低,这种级别一般是在理论上存在,数据库隔离级别一般都高于该级别

2、read committed

  • 事物A和事物B,事物A提交的数据,事物B才能读取到
  • 这种隔离级别高于读未提交
  • 换句话说,对方事物提交之后的数据,我当前事物才能读取到
  • 这种级别可以避免“脏数据”
  • 这种隔离级别会导致“不可重复读取”
  • Oracle默认隔离级别

3、repeatable read

  • 事务A和事务B,事务A提交之后的数据,事务B读取不到
  • 事务B是可重复读取数据
  • 这种隔离级别高于读已提交
  • 换句话说,对方提交之后的数据,我还是读取不到
  • 这种隔离级别可以避免“不可重复读取”,达到可重复读取
  • 比如1点和2点读到数据是同一个
  • MySQL默认级别
  • 虽然可以达到可重复读取,但是会导致“幻像读”

4、serializable

  • 事务A和事务B,事务A在操作数据库时,事务B只能排队等待
  • 这种隔离级别很少使用,吞吐量太低,用户体验差
  • 这种级别可以避免“幻像读”,每一次读取的都是数据库中真实存在数据,事务A与事务B串行,而不并发

十、隔离级别与一致性关系

在这里插入图片描述

十一、设置事务隔离级别

方式一
可以在my.ini文件中使用transaction-isolation选项来设置服务器的缺省事务隔离级别。

该选项值可以是:
– READ-UNCOMMITTED
– READ-COMMITTED
– REPEATABLE-READ
– SERIALIZABLE
• 例如:

[mysqld]
transaction-isolation = READ-COMMITTED

方式二
通过命令动态设置隔离级别
• 隔离级别也可以在运行的服务器中动态设置,应使用SET TRANSACTION ISOLATION LEVEL语句。
• 其语法模式为:
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
其中的可以是:
– READ UNCOMMITTED
– READ COMMITTED
– REPEATABLE READ
– SERIALIZABLE
• 例如: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

十二、隔离级别的作用范围

• 事务隔离级别的作用范围分为两种:
– 全局级:对所有的会话有效
– 会话级:只对当前的会话有效
• 例如,设置会话级隔离级别为READ COMMITTED :

mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
或:
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
•   设置全局级隔离级别为READ COMMITTED : 
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED

十三、查看隔离级别
• 事务隔离级别的作用范围分为两种:
– 全局级:对所有的会话有效
– 会话级:只对当前的会话有效
• 例如,设置会话级隔离级别为READ COMMITTED :

mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
或:
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
•   设置全局级隔离级别为READ COMMITTED : 
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED

DB索引

MySQL索引的类型

  1. 普通索引
    这是最基本的索引,它没有任何限制,比如上文中为title字段创建的索引就是一个普通索引,MyIASM中默认的BTREE类型的索引,也是我们大多数情况下用到的索引。
  2. 唯一索引
    与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值(注意和主键不同)。如果是组合索引,则列值的组合必须唯一,创建方法和普通索引类似。
  3. 全文索引(FULLTEXT)
    MySQL从3.23.23版开始支持全文索引和全文检索,FULLTEXT索引仅可用于 MyISAM 表;他们可以从CHAR、VARCHAR或TEXT列中作为CREATE TABLE语句的一部分被创建,或是随后使用ALTER TABLE 或CREATE INDEX被添加。对于较大的数据集,将你的资料输入一个没有FULLTEXT索引的表中,然后创建索引,其速度比把资料输入现有FULLTEXT索引的速度更为快。不过切记对于大容量的数据表,生成全文索引是一个非常消耗时间非常消耗硬盘空间的做法。
  4. 组合索引(最左前缀)
    平时用的SQL查询语句一般都有比较多的限制条件,所以为了进一步榨取MySQL的效率,就要考虑建立组合索引。例如上表中针对title和time建立一个组合索引:ALTER TABLE article ADD INDEX index_titme_time (title(50),time(10))。建立这样的组合索引,其实是相当于分别建立了下面两组组合索引:
    –title,time
    –title
    为什么没有time这样的组合索引呢?这是因为MySQL组合索引“最左前缀”的结果。简单的理解就是只从最左面的开始组合。并不是只要包含这两列的查询都会用到该组合索引,如下面的几个SQL所示:
    –使用到上面的索引
   SELECT * FROM article WHREE title='测试' AND time=1234567890;
   SELECT * FROM article WHREE utitle='测试';
   -- 不使用上面的索引
   SELECT * FROM article WHREE time=1234567890;

索引原理

一 索引原理
索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等
本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
数据库也是一样,但显然要复杂的多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段…这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的。而数据库实现比较复杂,一方面数据是保存在磁盘上的,另外一方面为了提高性能,每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。

二 磁盘IO与预读
考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。

三、索引的数据结构
任何一种数据结构都不是凭空产生的,一定会有它的背景和使用场景,我们现在总结一下,我们需要这种数据结构能够做些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?就这样,b+树应运而生。

在这里插入图片描述

如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

b+树的查找过程

如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

b+树性质

1.索引字段要尽量的小:通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。
2.索引的最左匹配特性(即从左往右匹配):当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。

Mysql索引管理

一、功能
#1. 索引的功能就是加速查找
#2. mysql中的primary key,unique,联合唯一也都是索引,这些索引除了加速查找以外,还有约束的功能

二、MySQL的索引分类
索引分类
1.普通索引index :加速查找
2.唯一索引
主键索引:primary key :加速查找+约束(不为空且唯一)
唯一索引:unique:加速查找+约束 (唯一)
3.联合索引
-primary key(id,name):联合主键索引
-unique(id,name):联合唯一索引
-index(id,name):联合普通索引
4.全文索引fulltext :用于搜索很长一篇文章的时候,效果最好。
5.空间索引spatial :了解就好,几乎不用
索引的两大类型hash与btree比较
#我们可以在创建上述索引的时候,为其指定索引类型,分两类
hash类型的索引:查询单条快,范围查询慢
btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
#不同的存储引擎支持的索引类型也不一样
InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;

索引原则

#1.最左前缀匹配原则,非常重要的原则,
create index ix_name_email on s1(name,email,)

  • 最左前缀匹配:必须按照从左到右的顺序匹配

select * from s1 where name=‘egon’; #可以
select * from s1 where name=‘egon’ and email=‘asdf’; #可以
select * from s1 where email=‘alex@oldboy.com’; #不可以
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,
比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,
d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

#2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器
会帮你优化成索引可以识别的形式

#3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),
表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、
性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,
这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
#4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’
就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,
但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。
所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
索引无法命中

  • like ‘%xx’
    select * from tb1 where email like ‘%cn’; 可用’cn%’

  • 使用函数
    select * from tb1 where reverse(email) = ‘wupeiqi’;

  • or
    select * from tb1 where nid = 1 or name = ‘seven@live.com’;
    特别的:当or条件中有未建立索引的列才失效,以下会走索引
    select * from tb1 where nid = 1 or name = ‘seven’;
    select * from tb1 where nid = 1 or name = ‘seven@live.com’ and email = ‘alex’

  • 类型不一致
    如果列是字符串类型,传入条件是必须用引号引起来,不然…
    select * from tb1 where email = 999;
    普通索引的不等于不会走索引

  • !=
    select * from tb1 where email != ‘alex’
    特别的:如果是主键,则还是会走索引
    select * from tb1 where nid != 123
    select * from tb1 where email > ‘alex’
    特别的:如果是主键或索引是整数类型,则还是会走索引
    select * from tb1 where nid > 123
    select * from tb1 where num > 123

#排序条件为索引,则select字段必须也是索引字段,否则无法命中

  • order by
    select name from s1 order by email desc;
    当根据索引排序时候,select查询的字段如果不是索引,则不走索引
    select email from s1 order by email desc;
    特别的:如果对主键排序,则还是走索引:
    select * from tb1 order by nid desc;
  • 组合索引最左前缀
    如果组合索引为:(name,email)
    name and email – 使用索引
    name – 使用索引
    email – 不使用索引
  • count(1)或count(列)代替count(*)在mysql中没有差别了
  • create index xxxx on tb(title(19)) #text类型,必须制定长度

注意:

  • 避免使用select *
  • count(1)或count(列) 代替 count(*)
  • 创建表时尽量时 char 代替 varchar
  • 表的字段顺序固定长度的字段优先
  • 组合索引代替多个单列索引(经常使用多个条件查询时)
  • 尽量使用短索引
  • 使用连接(JOIN)来代替子查询(Sub-Queries)
  • 连表时注意条件类型需一致
  • 索引散列值(重复少)不适合建索引,例:性别不适合

慢查询

0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
3.order by limit 形式的sql语句让排序的表优先查
4.了解业务方使用场景
5.加索引时参照建索引的几大原则
6.观察结果,不符合预期继续从0分析

DB锁

InnoDB引擎的锁机制
(之所以以InnoDB为主介绍锁,是因为InnoDB支持事务,支持行锁和表锁用的比较多,Myisam不支持事务,只支持表锁)

共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

说明:
1)共享锁和排他锁都是行锁,意向锁都是表锁,应用中我们只会使用到共享锁和排他锁,意向锁是mysql内部使用的,不需要用户干预。
2)对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁,事务可以通过以下语句显示给记录集加共享锁或排他锁。
共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE。
排他锁(X):SELECT * FROM table_name WHERE … FOR UPDATE。
3)InnoDB行锁是通过给索引上的索引项加锁来实现的,因此InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!。

乐观锁悲观锁

在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。
乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。

无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。其实不仅仅是关系型数据库系统中有乐观锁和悲观锁的概念,像memcache、hibernate、tair等都有类似的概念。
针对于不同的业务场景,应该选用不同的并发控制方式。所以,不要把乐观并发控制和悲观并发控制狭义的理解为DBMS中的概念,更不要把他们和数据中提供的锁机制(行锁、表锁、排他锁、共享锁)混为一谈。其实,在DBMS中,悲观锁正是利用数据库本身提供的锁机制来实现的。

下面来分别学习一下悲观锁和乐观锁。

悲观锁

在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作都某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。
悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度(悲观),因此,在整个数据处理过程中,将数据处于锁定状态。 悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)

在数据库中,悲观锁的流程如下:
在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。
如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。
如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。

MySQL InnoDB中使用悲观锁

要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。set autocommit=0;

//0.开始事务
begin;/begin work;/start transaction; (三者选一就可以)
//1.查询出商品信息
select status from t_goods where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_goods set status=2;
//4.提交事务
commit;/commit work;

上面的查询语句中,我们使用了select…for update的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在t_goods表中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。
上面我们提到,使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。

优点与不足

悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数

乐观锁

在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。乐观事务控制最早是由孔祥重(H.T.Kung)教授提出。

乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。
相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。

数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。

实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。
使用版本号实现乐观锁
使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。
1.查询出商品信息
select (status,status,version) from t_goods where id=#{id}
2.根据商品信息生成订单
3.修改商品status为2
update t_goods
set status=2,version=version+1
where id=#{id} and version=#{version};

优点与不足

乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题

外传

😜 原创不易,如若本文能够帮助到您的同学
🎉 支持我:关注我+点赞👍+收藏⭐️
📝 留言:探讨问题,看到立马回复
💬 格言:己所不欲勿施于人 扬帆起航、游历人生、永不言弃!🔥

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/527709.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

美国原油CFD价格波动受到哪些因素影响?

经过多年的发展&#xff0c;石油技术已经从煤炭转向原油作为主要能源&#xff0c;这种商品用于各种产品&#xff0c;包括汽油、塑料、药品等。因此&#xff0c;它的价值很高&#xff0c;美国原油CFD价格变动时全世界都在关注。对于交易者来说&#xff0c;美国原油CFD价格波动性…

从零开始Vue3+Element Plus的后台管理系统(五)——尝试多种图标ICON方案iconify

iconfont是过去使用较多的图标方案&#xff0c;后来更倾向于使用SVG&#xff0c;因为SVG更灵活兼容性更好。在最近的几个Vue2项目中&#xff0c;因为强调功能弱化设计所以直接用Element UI自带的图标即可满足需求。 Element Plus的图标库相对Element UI更加丰富&#xff0c;然…

flac格式如何转mp3?这几种方法快收藏起来吧

将FLAC格式转换为MP3格式&#xff0c;可以使用一些转换工具&#xff0c;如优速音频处理器等&#xff0c;也可以使用其他专业音频转换软件。FLAC是一种无损音频编码格式&#xff0c;通常用于保留原始音频质量&#xff0c;但相应的文件大小较大。而MP3是一种有损压缩音频编码格式…

MFC类继承实现CEdit自绘文字颜色,边框,背景色

效果 新建一个继承于CEdit的类CMyEdit 在他的窗体OnNcPaint函数中实现绘制边框,它所响应的消息是WM_NCPAINT void CMyEdit::OnNcPaint() {// TODO: 在此处添加消息处理程序代码// 不为绘图消息调用 CEdit::OnNcPaint()////执行默认的窗体处理过程Default();

linux环境安装使用FastDFS,FastDHT详解

01-简介 GitHub&#xff1a;happyfish100 (YuQing) GitHub FastDFS高性能的分布式文件系统&#xff0c;主要功能包含文件的存储、同步、访问&#xff08;上传和下载&#xff09;&#xff0c;特别适用于以文件为主体的网络站点&#xff08;图片分享和视频分享&#xff09;。 Fa…

2023年Java面试正确姿势(1000+面试题附答案解析)

前言 2023 跳槽不迷茫&#xff0c;大家可以先收藏再看&#xff0c;后续跳槽都能用上的&#xff01; 一键获取最新java文档。 Java程序员绝大部分工作的时间都是增删改查&#xff0c;很多人觉得这项工作没什么技术含量&#xff0c;任何一件事情都要站在不同的角度去考虑&#xf…

shell的数组

数组定义方法 方法一&#xff1a;数组名&#xff08; value0 value1 value2 ...&#xff09; 方法二&#xff1a;数组名&#xff08;[0]value [1]value [2]value...&#xff09; 方法三&#xff1a;列表名"value0 value1 value2 ..." 数组名&#xff08;$列表名&…

【WLAN从入门到精通-基础篇】

WLAN定义和基本架构 本文参考https://forum.huawei.com/enterprise/zh/thread/580888898454831104 1. WLAN定义 WLAN的全称是Wireless Local Area Network&#xff0c;中文含义是无线局域网&#xff0c;WLAN的定义有广义和狭义两种&#xff1a;广义上讲WLAN是以各种无线电波…

使用Kotlin函数式编程技术构建强大的 Android 应用程序

使用Kotlin函数式编程技术构建强大的 Android 应用程序 Kotlin 是一种现代的多用途编程语言&#xff0c;在 Android 应用程序开发人员中迅速流行起来。Kotlin 有别于其他编程语言的关键特性之一是它对函数式编程的强大支持。Kotlin 函数式编程允许开发人员编写更清晰、更简洁、…

本地生活服务:老将、新秀同台竞技

配图来自Canva可画 本地生活服务领域又热闹起来了。 事实上&#xff0c;本地生活服务一直为各路玩家所看好。最近几年&#xff0c;互联网大厂频频布局本地生活服务领域&#xff0c;其中抖音、快手等短视频平台更是持续加大在本地生活服务领域的布局力度。 前不久&#xff0c…

Grafana系列-统一展示-10-Explore Jaeger

系列文章 Grafana 系列文章 Explore Jaeger 你可以通过Explore查询和显示 Jaeger 的 trace。有 3 种方法: Query by searchQuery by trace IDUpload a JSON trace file Query by search 要使用该方法: 从 Query 类型选择器中选择 Search。填写搜索表格&#xff1a;(和 J…

2023第十三届中国数字营销与电商创新峰会 嘉宾揭晓

2023第十三届中国数字营销与电商创新峰会将于7月6日-7日在上海召开。 本次峰会将通过七大热点主题帮助品牌主突破模式能力&#xff0c;回归初心、精耕细作&#xff0c;实现可持续增长&#xff1b;提升产品能力&#xff0c;实现可持续长期增长&#xff1b;优化营销能力&#xf…

HTTP第11讲——HTTP的特点

灵活可扩展 首先&#xff0c; HTTP 协议是一个“灵活可扩展”的传输协议。 HTTP 协议最初诞生的时候就比较简单&#xff0c;本着开放的精神只规定了报文的基本格式&#xff0c;比如用空格分隔单词&#xff0c;用换行分隔字段&#xff0c;“headerbody”等&#xff0c;报文里的…

分析车载蓝牙通话只有前喇叭声音,后面喇叭无声背后原因

车载蓝牙通话只有前喇叭声音&#xff0c;后面喇叭无声背后原因 大家有没有注意到车载蓝牙连接后通话的时候只有前喇叭的有声音&#xff0c;后面喇叭没声音呢&#xff1f;特别是后装的车载多媒体上基本都是这样&#xff0c;细思下为什么这样的原因&#xff0c; 采访后装车载技术…

【软考备战·五月模考】希赛网五月模考软件设计师上午题

文章目录 一、成绩报告二、错题总结第一题第二题第三题第四题第五题第六题第七题第八题第九题第十题第十一题第十二题 三、知识查缺 题目及解析来源&#xff1a;2023上半年软考-模考大赛 一、成绩报告 二、错题总结 第一题 解析&#xff1a; 瀑布模型 中各个活动按 线性顺序 连…

【Spring全家桶系列】AspectJ表达式的书写与SpringAOP下的五种通知类型

⭐️前面的话⭐️ 本文已经收录到《Spring框架全家桶系列》专栏&#xff0c;本文将介绍AspectJ表达式的书写与SpringAOP下的五种通知类型。 &#x1f4d2;博客主页&#xff1a;未见花闻的博客主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4…

基于ArcGIS实现陕西省1:250000比例尺地形图分幅和编号

1地形图的分幅与编号原理 我国1:1000000地形图的分幅采用国际1:1000000地图分幅标准&#xff0c;而其他比例尺地形图分幅以1:1000000比例尺地形图为基准进行分幅。每幅1:1000000地形图范围是经差6、纬差4&#xff1b;纬度60~ 76之间经差12、纬差4&#xff1b;纬度76~ 88之间经…

数据结构之数组对栈的实现

目录 1.什么是栈 2.栈的基本操作 3.栈的实现 1.结构体与函数接口 2.初始化栈 3.销毁栈 4.入栈 5.出栈 6.返回栈元素数量大小 7.判空 8.打印栈 1.什么是栈 栈是一种特殊的线性表&#xff0c;其中只允许固定的一端进行插入与删除&#xff0c;进行输出插入删除的那一端我…

振幅调制器【Multisim】【高频电子线路】

目录 一、实验目的与要求 二、实验仪器 三、实验内容与测试结果 1、观测集电极调幅器输出信号波形&#xff0c;测量调幅度 2、观察集电极调幅器输出信号频谱&#xff08;Fourier analysis&#xff09; 3、改变V1幅度为0.8Vpk&#xff0c;观测输出波形&#xff0c;说明原…

MySQL函数详细

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️树高千尺&#xff0c;落叶归根人生不易&…