数据库常见面试题总结

事务的概念和特性:
事务( (Transaction )是一个操作序列,不可分割的工作单位,以 BEGIN TRANSACTION 开始,以 ROLLBACK/COMMIT结束
特性(ACID)

  1. 原子性(Atomicity) ):逻辑上是不可分割的操作单元,事务的所有操作要么全部提交成功,要么全部失败回滚(用回滚日志实现,反向执行日志中的操作);
  2. 一致性(Consistency) ):事务的执行必须使数据库保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的;
  3. 隔离性( (Isolation) ):一个事务所做的修改在最终提交以前,对其它事务是不可见的(并发执行的事务之间不能相互影响);
  4. 持久性(Durability) ):一旦事务提交成功,对数据的修改是永久性的
    会出现哪些并发一致性问题?
  5. 丢失修改:一个事务对数据进行了修改,在事务提交之前,另一个事务对同一个数据进行了修改,覆盖了之前的修改;
  6. 脏读(Dirty Read):一个事务读取了被另一个事务修改、但未提交(进行了回滚)的数据,造成两个事务得到的数据不一致;
  7. 不可重复读(Nonrepeatable Read):在同一个事务中,某查询操作在一个时间读取某一行数据和之后一个时间读取该行数据,发现数据已经发生修改(可能被更新或删除了);
  8. 幻读(Phantom Read):当同一查询多次执行时,由于其它事务在这个数据范围内执行了插入操作,会导致每次返回不同的结果集(和不可重复读的区别:针对的是一个数据整体/范围;并且需要是插入操作)

mysql如何添加索引?
1.添加PRIMARY KEY(主键索引)

mysql>ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) 

2.添加UNIQUE(唯一索引)

mysql>ALTER TABLE `table_name` ADD UNIQUE ( `column` ) 

3.添加INDEX(普通索引)

mysql>ALTER TABLE `table_name` ADD INDEX index_name ( `column` ) 

4.添加FULLTEXT(全文索引)

mysql>ALTER TABLE `table_name` ADD FULLTEXT ( `column`) 

5.添加多列索引

mysql>ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )

下面是更加详细的方法:
MySQL中可以使用alter table这个SQL语句来为表中的字段添加索引。
使用alter table语句来为表中的字段添加索引的基本语法是:

ALTER TABLE <表名> ADD INDEX (<字段>);

我们来尝试为test中t_name字段添加一个索引。

mysql> alter table test add index(t_name);
Query OK, 0 rows affected (0.17 sec)
Records: 0  Duplicates: 0  Warnings: 0

执行成功后,我们来看看结果:

mysql> describe test;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| t_id       | int(11)     | YES  |     | NULL    |       |
| t_name     | varchar(50) | NO   | MUL | NULL    |       |
| t_password | char(32)    | YES  |     | NULL    |       |
| t_birth    | date        | YES  |     | NULL    |       |
+------------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

结果可以看出,t_name字段的Key这一栏由原来的空白变成了MUL。这个MUL是什么意思呢?简单解释一下:如果Key是MUL,那么该列的值可以重复,该列是一个非唯一索引的前导列(第一列)或者是一个唯一性索引的组成部分但是可以含有空值NULL。

Mysql的优化方案

  1. 选取最适用的字段属性
    MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用MEDIUMINT而不是BIGIN来定义整型字段。
    另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
    对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。
    只返回必要的列:最好不要使用 SELECT * 语句;
    只返回必要的行:使用 LIMIT 语句来限制返回的数据;
    将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联,
    这样做的好处有:
    让缓存更高效。对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它的查询缓存依然可以使用;
    分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余的查询;

  2. 使用连接(JOIN)来代替子查询(Sub-Queries)
    MySQL从4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询,如下所示:

DELETE  FROM  customerinfo
WHERE  CustomerID  NOT  in  (SELECT customerid  FROM  salesinfo)

使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN)…替代。例如,假设我们要将所有没有订单记录的用户取出来,可以用下面这个查询完成:

SELECT  *  FROM  customerinfo
WHERE  customerid  NOT IN (SELECT customerid   FROM   salesinfo)

如果使用连接(JOIN)…来完成这个查询工作,速度将会快很多。尤其是当salesinfo表中对CustomerID建有索引的话,性能将会更好,查询如下:

SELECT  *  FROM  customerinfo
LEFT  JOIN  salesinfo  ON   customerinfo.customerid =salesinfo.customerid
WHERE  salesinfo.customerid   IS NULL

连接(JOIN)…之所以更有效率一些,是因为MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。
3. 使用联合(UNION)来代替手动创建的临时表
MySQL从4.0的版本开始支持union查询,它可以把需要使用临时表的两条或更多的select查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用UNION作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要想同。下面的例子就演示了一个使用UNION的查询。

SELECT   name,phone  FROM  client UNION
SELECT  name,birthdate  FROM  author  UNION
SELECT  name,supplier FROM product
  1. 事务
    尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。
BEGIN;
  INSERT   INTO   salesinfo   SET   customerid=14;
  UPDATE   inventory   SET   quantity =11   WHERE   item='book';
COMMIT;

事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其它的用户所干扰。

  1. 锁定表
    尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。
    其实,有些情况下我们可以通过锁定表的方法来获得更好的性能。下面的例子就用锁定表的方法来完成前面一个例子中事务的功能。
LOCK TABLE inventory WRITE SELECT quantity  FROM   inventory   WHERE Item='book';
...
 
UPDATE   inventory   SET   Quantity=11   WHERE  Item='book';UNLOCKTABLES

这里,我们用一个select语句取出初始数据,通过一些计算,用update语句将新值更新到表中。包含有WRITE关键字的LOCKTABLE语句可以保证在UNLOCKTABLES命令被执行之前,不会有其它的访问来对inventory进行插入、更新或者删除的操作。

  1. 使用外键
    锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。
    例如,外键可以保证每一条销售记录都指向某一个存在的客户。在这里,外键可以把customerinfo表中的customerid映射到salesinfo表中customerid,任何一条没有合法customerid的记录都不会被更新或插入到salesinfo中。
CREATE  TABLE   customerinfo( customerid   int primary key) engine = innodb;
CREATE  TABLE   salesinfo( salesid int not null,customerid  int not null, primary key(customerid,salesid),foreign key(customerid)  references  customerinfo(customerid) on delete cascade)engine = innodb;

注意例子中的参数“on delete cascade”。该参数保证当customerinfo表中的一条客户记录被删除的时候,salesinfo表中所有与该客户相关的记录也会被自动删除。如果要在MySQL中使用外键,一定要记住在创建表的时候将表的类型定义为事务安全表InnoDB类型。该类型不是MySQL表的默认类型。定义的方法是在CREATE TABLE语句中加上engine=INNODB。如例中所示。
7. 使用索引
索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(),MIN()和ORDERBY这些命令的时候,性能提高更为明显。
那该对哪些字段建立索引呢?一般说来,索引应建立在那些将用于JOIN,WHERE判断和ORDERBY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况。

例如customerinfo中的“province”…字段,在这样的字段上建立索引将不会有什么帮助;相反,还有可能降低数据库的性能。我们在创建表的时候可以同时创建合适的索引,也可以使用ALTERTABLE或CREATEINDEX在以后创建索引。此外,MySQL从版本3.23.23开始支持全文索引和搜索。全文索引在MySQL中是一个FULLTEXT类型索引,但仅能用于MyISAM类型的表。对于一个大的数据库,将数据装载到一个没有FULLTEXT索引的表中,然后再使用ALTERTABLE或CREATEINDEX创建索引,将是非常快的。但如果将数据装载到一个已经有FULLTEXT索引的表中,执行过程将会非常慢。
8. 优化的查询语句
绝大多数情况下,使用索引可以提高查询的速度,但如果SQL语句使用不恰当的话,索引将无法发挥它应有的作用。
下面是应该注意的几个方面:
a、 首先,最好是在相同类型的字段间进行比较的操作
在MySQL3.23版之前,这甚至是一个必须的条件。例如不能将一个建有索引的INT字段和BIGINT字段进行比较;但是作为特殊的情况,在CHAR类型的字段和VARCHAR类型字段的字段大小相同的时候,可以将它们进行比较。
b、 其次,在建有索引的字段上尽量不要使用函数进行操作
例如,在一个DATE类型的字段上使用YEAE()函数时,将会使索引不能发挥应有的作用。所以,下面的两个查询虽然返回的结果一样,但后者要比前者快得多。
c、第三,在搜索字符型字段时,我们有时会使用LIKE关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的
例如下面的查询将会比较表中的每一条记录。

SELECT  *  FROM  books  WHERE  name  like   "MySQL%"

但是如果换用下面的查询,返回的结果一样,但速度就要快上很多:

SELECT  *  FROM  books  WHERE  name >=  "MySQL"  and  name  <"MySQM"

最后,应该注意避免在查询中让MySQL进行自动类型转换,因为转换过程也会使索引变得不起作用。
9. 优化系统配置
优化操作系统:增加TCP 支持的队列数;
优化MySQL 配置文件:缓存池大小和个数设置
优化硬件:磁盘:固态硬盘;
CPU:多核且高频;
内存:增大内存

Mysql 表的连接方式

  1. 交叉连接,返回两表的笛卡尔积(对于所含数据分别为 m、n 的表,返回 m*n的结果)
    交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。
    sql语句:
select * from1,表2; 隐士笛卡尔积 
select * from1 crross join 表2;  显示笛卡尔积 

不带条件的内连接也是笛卡尔积:select * from 表1 inner join 表2;

  1. 内连接 inner join on 仅将两个表中满足连接条件的行组合起来作为结果集
    Sql语句:select * from 表1 inner join 表2 on 表1.id=表2.id;

注意:隐士笛卡尔积可以在后面跟where查询条件,查询结果和内连接相同(查询条件相 同),不能用on查询条件。显示笛卡尔积where和on都不能用,内连接inner join 后面可跟where查询条件和on查询条件,此处也可以说成笛卡尔带有查询条件后是内连接。

  1. 自然连接Natural join(natural join在sql server 中不支持)… 只考虑属性相同的元组对
    是一种特殊的等值连接,要求两个关系表中进行比较的属性组必须是名称相同的属性组,并且在结果中把重复的属性列去掉(即:留下名称相同的属性组中的其中一组)。两张表中的名称和类型完全一致的列进行内连接
    Sql语句:select * from 表1 natrual join 表2;

  2. 外连接分为全外连接,左外连接,右外连接on后面跟查询条件,不能用where等
    全外连接 :select * from 表1 full join 表2 on 表1.id=表2.id;
    全外连接在mysql中不能用,在oracle中可以使用;查询结果右为左的数据都显示,如果左边对应的右边没有与之相匹配的数据,则为null,同理左边的为nul。L

左外连接:select * from 表1 left join 表2 on 表1.id=表2.id;
查询结果为:左边的全部显示,右边的如果全部满足左边话,就和左边的显示数据条数相同;如果右边的不完全满足左边的话,则左边的表数据对应的右边为null;

右外连接:select * from 表1 right join 表2 on 表1.id=表2.id;
查询结果为:右边的全部显示,左边的如果全部满足右边话,就和右边的显示数据条数相同;如果左边的不完全满足右边的话,则右边的表数据对应的左边为null;

  1. Union 和Union all
    Union和union all是联合查询,union联合查询的条件是两个表的字段个数都是相同的
    Union Sql语句:select * from 表1 union select * from 表2;
    Union all Sql语句:select * from 表1 union all select * from 表2;
    Union的查询结果为,如果两个表中右相同数据的话,则之显示一条,将两个表合称为一个表。
    Union all的查询结果为,如果两个表中右相同数据的话,则全部显示,将两个表合称为一个表。

数据库的四种隔离级别

  1. 未提交读(ReadUncommited):在一个事务提交之前,它的执行结果对其它事务也
    是可见的。会导致脏读、不可重复读、幻读;
  2. 提交读(Read Commited):一个事务只能看见已经提交的事务所作的改变。可避免
    脏读问题;
  3. 可重复读(Repeatable Read):可以确保同一个事务在多次读取同样的数据时得到相
    同的结果。(MySQL 的默认隔离级别)。可避免不可重复读;
  4. 可串行化(Serializable):强制事务串行执行,使之不可能相互冲突,从而解决幻读
    问题。可能导致大量的超时现象和锁竞争,实际很少使用。

什么是乐观锁和悲观锁?
悲观锁:认为数据随时会被修改,因此每次读取数据之前都会上锁,防止其它事务读取或修改数据;应用于数据更新比较频繁的场景;
乐观锁:操作数据时不会上锁,但是更新时会判断在此期间有没有别的事务更新这个数据,若被更新过,则失败重试;适用于读多写少的场景。乐观锁的实现方式有:加一个版本号或者时间戳字段,每次数据更新时同时更新这个字段;
先读取想要更新的字段或者所有字段,更新的时候比较一下,只有字段没有变化才进行更新

常见的封锁类型
排它锁(Exclusive Lock)/ X 锁:事务对数据加上 X 锁时,只允许此事务读取和修改此数据,并且其它事务不能对该数据加任何锁;
共享锁(Shared Lock)/ S 锁:加了 S 锁后,该事务只能对数据进行读取而不能修改,并且其它事务只能加 S 锁,不能加 X 锁
意向锁(Intention Locks):
一个事务在获得某个数据行对象的 S 锁之前,必须先获得整个表的 IS 锁或更强的锁;一个事务在获得某个数据行对象的 X 锁之前,必须先获得整个表的 IX 锁;IS/IX 锁之间都是兼容的;
好处:若一个事务想要对整个表加 X 锁,就需要先检测是否有其它事务对该表或者该表中的某一行加了锁,这种检测非常耗时。有了意向锁之后,只需要检测整个表是否存在 IX/IS/X/S锁就行了
锁的作用:用于管理对共享资源的并发访问,保证数据库的完整性和一致性

封锁粒度的概念
MySQL 中提供了两种封锁粒度:行级锁以及表级锁
封锁粒度小的好处与坏处:
好处:锁定的数据量越少,发生锁争用的可能就越小,系统的并发程度就越高;
坏处:系统开销大(加锁、释放锁、检查锁的状态都需要消耗资源)
MySQL 加锁

SELECT ... LOCK In SHARE MODE;
SELECT ... FOR UPDATE;

什么是三级封锁协议?
一级封锁协议:事务在修改数据之前必须先对其加 X 锁,直到事务结束才释放。可以解决丢失修改问题(两个事务不能同时对一个数据加 X 锁,避免了修改被覆盖);
二级封锁协议:在一级的基础上,事务在读取数据之前必须先加 S 锁,读完后释放。可以解
决脏读问题(如果已经有事务在修改数据,就意味着已经加了 X锁,此时想要读取数据的事务并不能加 S 锁,也就无法进行读取,避免了读取脏数据);
三级封锁协议:在二级的基础上,事务在读取数据之前必须先加 S 锁,直到事务结束才能释放。可以解决不可重复读问题(避免了在事务结束前其它事务对数据加 X锁进行修改,保证了事务期间数据不会被其它事务更新)

什么是两段锁协议?
事务必须严格分为两个阶段对数据进行加锁和解锁的操作,第一阶段加锁,第二阶段解锁。也就是说一个事务中一旦释放了锁,就不能再申请新锁了。
可串行化调度是指,通过并发控制,使得并发执行的事务结果与某个串行执行的事务结果相同。事务遵循两段锁协议是保证可串行化调度的充分条件。

什么是 MVCC ?
多版本并发控制(Multi-Version Concurrency Control, MVCC),MVCC 在每行记录后面都保存有两个隐藏的列,用来存储创建版本号和删除版本号。
创建版本号:创建一个数据行时的事务版本号(事务版本号:事务开始时的系统版本号;系统版本号:每开始一个新的事务,系统版本号就会自动递增);
删除版本号:删除操作时的事务版本号;
各种操作:
插入操作时,记录创建版本号;
删除操作时,记录删除版本号;
更新操作时,先记录删除版本号,再新增一行记录创建版本号;
查询操作时,要符合以下条件才能被查询出来:删除版本号未定义或大于当前事务版本号(删除操作是在当前事务启动之后做的);创建版本号小于或等于当前事务版本号(创建操作是事务完成或者在事务启动之前完成)
通过版本号减少了锁的争用 , 提高了系统性能 ; 可以实现提交读和可重复读两种隔离级别,未提交读无需使用MVCC。

数据库的范式
第一范式(1NF ,Normal Form) ) :属性不应该是可分的。举例:如果将“电话”作为一个属性(一列),是不符合 1NF 的,因为电话这个属性可以分解为家庭电话和移动电话…如果将“移动电话”作为一个属性,就符合 1NF;
第二范式 2NF :每个非主属性完全依赖于主属性集(候选键集) );
B 完全依赖于 A,就是说 A中的所有属性唯一决定 B,属性少了就不能唯一决定,属性多了则有冗余(叫依赖不叫完全依赖)。举例:(学号,课程名)这个主属性集可以唯一决定成绩,但是对于学生姓名这个属性,(学号,课程名)这个属性集就是冗余的,所以学生姓名不完全依赖于(学号,课程名)这一属性集;主属性集/候选码集:某一组属性能够唯一确定其它的属性(主键就是从候选键集中选的一个键),而其子集不能,这样的属性组中的属性就是主属性;不在候选码集中的属性成为非主属性;可以通过分解来满足 2NF:将(学号,课程名,成绩)做成一张表;(学号,学生姓名)做成另一张表,避免大量的数据冗余;
第三范式 3NF在 :在 2NF 的基础上,非主属性不传递依赖于主属性
传递依赖:如果 C 依赖于 B,B 依赖于 A,那么 C 传递依赖于 A;
3NF 在 2NF 的基础上,消除了非主属性之间的依赖;比如一个表中,主属性有(学号),非主属性有(姓名,院系,院长名),可以看到院长名这个非主属性依赖于院系,传递依赖于学号。消除的办法是分解。

不符合范式会出现哪些异常?
冗余数据:某些同样的数据多次出现(如学生姓名);
修改异常:修改了一个记录中的信息,另一个记录中相同的信息却没有修改;
删除异常:删除一个信息,那么也会丢失其它信息(删除一个课程,丢失了一个学生的信息);
插入异常:无法插入(插入一个还没有课程信息的学生)

什么是存储过程?有哪些优缺点?
存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合。想要实现相应的功能时,只需要调用这个存储过程就行了(类似于函数,输入具有输出参数)。
优点:
1.预先编译,而不需要每次运行时编译,提高了数据库执行效率;
2.封装了一系列操作,对于一些数据交互比较多的操作,相比于单独执行 SQL 语句,可以减少网络通信量;
3.具有可复用性,减少了数据库开发的工作量;
4.安全性高,可以让没有权限的用户通过存储过程间接操作数据库;
5.更易于维护
缺点:
1.可移植性差,存储过程将应用程序绑定到了数据库上;
2.开发调试复杂:没有好的IDE;
3.修改复杂,需要重新编译,有时还需要更新程序中的代码以更新调用

Drop/Delete/Truncate 的区别?

  1. Delete 用来删除表的全部或者部分数据,执行 delete 之后,用户需要提交之后才会执行,
    会触发表上的 DELETE触发器(包含一个 OLD 的虚拟表,可以只读访问被删除的数据),DELETE之后表结构还在,删除很慢,一行一行地删,因为会记录日志,可以利用日志还原数据;
  2. Truncate 删除表中的所有数据,这个操作不能回滚,也不会触发这个表上的触发器。操
    作比 DELETE 快很多(直接把表 drop 掉,再创建一个新表,删除的数据不能找回)。如果表
    中有自增(AUTO_INCREMENT)列,则重置为 1;
  3. Drop 命令从数据库中删除表,所有的数据行,索引和约束都会被删除;不能回滚,不会
    触发触发器;

什么是触发器?
触发器(TRIGGER)是由事件(比如 INSERT/UPDATE/DELETE)来触发运行的操作(不能被直
接调用,不能接收参数)。在数据库里以独立的对象存储,用于保证数据完整性(比如可以
检验或转换数据)。
触发器有哪些约束类型?
约束(Constraint)类型:主键(Primary Key)约束,唯一约束(Unique),检查约束,非空约束,外键(Foreign Key)约束。

什么是视图?什么是游标?
视图:从数据库的基本表中通过查询选取出来的数据组成的虚拟表(数据库中存放视图的定义)。可以对其进行增/删/改/查等操作。特别地,对视图的修改不影响基本表。
好处:
通过只给用户访问视图的权限,保证数据的安全性;
简化复杂的 SQL 操作,隐藏数据的复杂性(比如复杂的连接);
游标(Cursor):用于定位在查询返回的结果集的特定行,以对特定行进行操作。使用游标可以方便地对结果集进行移动遍历,根据需要滚动或对浏览/修改任意行中的数据。主要用于交互式应用。

什么是主从复制?实现原理是什么?
主从复制(Replication)是指数据可以从一个 MySQL数据库主服务器复制到一个或多个从服务器,从服务器可以复制主服务器中的所有数据库或者特定的数据库,或者特定的表。默认采用异步模式。
实现原理:
主服务器 binary线程:将主服务器中的数据更改(增删改)日志写入 Binary log 中;
从服务器 I/O 线程:负责从主服务器读取 binary log,并写入本地的 Relay log;
从服务器 SQL 线程:负责读取 Relay log,解析出主服务器已经执行的数据更改,并在从服务器中重新执行(Replay),保证主从数据的一致性。

为什么要主从复制?
读写分离:主服务器负责写,从服务器负责读
缓解了锁的争用,即使主服务器中加了锁,依然可以进行读操作;
从服务器可以使用 MyISAM,提升查询性能以及节约系统开销;
增加冗余,提高可用性。
数据实时备份,当系统中某个节点发生故障时,可以方便的故障切换
降低单个服务器磁盘 I/O 访问的频率,提高单个机器的 I/O 性能

关系型数据库和非关系型数据库的区别?
一、关系型数据库
关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织
优点:
1、易于维护:都是使用表结构,格式一致;
2、使用方便:SQL 语言通用,可用于复杂查询;
3、复杂操作:支持 SQL,可用于一个表以及多个表之间非常复杂的查询。
缺点:
1、读写性能比较差,尤其是海量数据的高效率读写;
2、固定的表结构,灵活度稍欠;
3、高并发读写需求,传统关系型数据库来说,硬盘 I/O 是一个很大的瓶颈。
二、非关系型数据库
非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。
优点:
1、格式灵活:存储数据的格式可以是 key,value 形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
2、速度快:nosql 可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
3、高扩展性;
4、成本低:nosql 数据库部署简单,基本都是开源软件。
缺点:
1、不提供 sql 支持,学习和使用成本较高;
2、无事务处理;
3、数据结构相对复杂,复杂查询方面稍欠。
非关系型数据库的分类和比较:
1、文档型
2、key-value 型
3、列式数据库
4、图形数据库

Redis常见面试题
Redis支持的数据类型?
String字符串:
格式: set key value
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个键最大能存储512MB。
Hash(哈希)
格式: hmset name key1 value1 key2 value2
Redis hash 是一个键值(key=>value)对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
格式: lpush name value
在 key 对应 list 的头部添加字符串元素
格式: rpush name value
在 key 对应 list 的尾部添加字符串元素
格式: lrem name index
key 对应 list 中删除 count 个和 value 相同的元素
格式: llen name
返回 key 对应 list 的长度
Set(集合)
格式: sadd name value
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
zset(sorted set:有序集合)
格式: zadd name score value
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。

如何修改 Redis 数据库的库的个数?
windows 下,修改 redis.windows.conf 文件中的 databases 16 ,改为你想要的的个数;
linux 下,修改 redis.conf 文件中的 databases 16 ,改为你想要的的个数。

Redis 数据库如何实现持久化
在 Redis 中,持久化存储分为两种。一种是 aof 日志追加的方式,另外一种是 rdb 数据快照的方式。
RDB 持久化存储即是将 redis 存在内存中的数据以快照的形式保存在本地磁盘中;
AOF 持久化存储便是以日志的形式将 redis 存储在 aof_buf 缓冲区中的数据写入到磁盘中。简而言之,就是记录 redis 的操作日志,将 redis 执行过的命令记录下载,当我们需要数据恢复时,redis 去重新执行一次日志文件中的命令.
比较:
1、aof文件比rdb更新频率高,优先使用aof还原数据。
2、aof比rdb更安全也更大
3、rdb性能比aof好
4、如果两个都配了优先加载AOF

了解redis通讯协议(RESP )吗?能解释下什么是RESP?有什么特点?
RESP 是redis客户端和服务端之前使用的一种通讯协议;
RESP 的特点:实现简单、快速解析、可读性好
For Simple Strings the first byte of the reply is “+” 回复
For Errors the first byte of the reply is “-” 错误
For Integers the first byte of the reply is “:” 整数
For Bulk Strings the first byte of the reply is “$” 字符串
For Arrays the first byte of the reply is “*” 数组

Redis 有哪些架构模式?讲讲各自的特点
特点:简单
问题:
1、 内存容量有限 2、处理能力有限 3、无法高可用。

Redis常用命令

Keys pattern*  # 区配所有以bit开头的
Exists  key    # 查看是否存在
Set     # 设置 key 对应的值为 string 类型的 value。
Setnx   # 设置 key 对应的值为 string 类型的 value,若 key 已经存在,返回 0,nx 是 not exist 的意思。
删除某个key :# 第一次返回1 删除了 第二次返回0
Expire    # 设置过期时间(单位秒)
TTL      # 剩下多少时间,返回负数则key失效,key不存在了
Setex    # 设置 key 对应的值为 string 类型的 value,并指定此键值对应的有效期。
Mset     # 一次设置多个 key 的值,成功返回 ok 表示所有的值都设置了,失败返回 0 表示没有任何值被设置。
Getset   # 设置 key 的值,并返回 key 的旧值。
Mget    # 一次获取多个 key 的值,如果对应 key 不存在,则对应返回 nil。
Incr     # 对 key 的值做加加操作,并返回新的值。注意 incr 一个不是 int 的 value 会返回错误,incr 一个不存在的 key,则设置 key 为 1
Incrby   # 同 incr 类似,加指定值 ,key 不存在时候会设置 key,并认为原来的 value 是 0
Decr   # 对 key 的值做的是减减操作,decr 一个不存在 key,则设置 key 为-1
Decrby    # 同 decr,减指定值。
Append    # 给指定 key 的字符串值追加 value,返回新字符串值的长度。
Strlen    # 取指定 key 的 value 值的长度。
persist xxx    # (取消过期时间)
选择数据库(0-15库)   # Select 0 //选择数据库
move age 1//    # 把age 移动到1库
Randomkey   # 随机返回一个key
Rename      # 重命名
Type    # 返回数据类型

使用过Redis分布式锁么,它是怎么实现的?
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?
set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!

使用过Redis做异步队列么,是怎么用的?有什么缺点?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
缺点:
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
能不能生产一次消费多次呢?
使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?
缓存穿透:
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。

缓存雪崩:
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

Redis 在web开发中的应用
Redis是一个功能强大、性能高效的开源数据结构服务器,Redis最典型的应用是NoSQL。Redis除了作为NoSQL数据库使用之外,还能广泛应用消息队列,数据堆栈以及数据缓存等众多场合。
Redis的应用场景
(1)海量的简单读写
根据Redis官方的测试结果,在50个并发的情况下请求10万次,写的书都市11000次/s,读的速度是81000次/s。所以用于处理类似于微博热点事件、交单排序、关注排行之类实时但功能单一的局部应用是最理想的
(2)实时的反垃圾系统
反垃圾系统通常都是基于关键词的,使用Redis储存关系词,能够利用Redis的高性能,为监控系统提供稳定及精确的实时监控功能,典型的由邮件系统、评论系统等。
(3)公开的统计系统
利用Redis的简单易用的群体部署功能,能够轻易地开发大规模的统计系统,例如广告系统、万展统计系统、简单的股市分析系统等。
(4)消息队列
消息队列是提高关系型数据库数据插入的有效手段,Redis是NoSQL中为数不多支持消息队列的数据库。常用的应有由网站邮件、站内短信、品论系统等。
(5 )消息堆栈系统
消息堆栈与消息队列在处理顺序上是相反的,消息队列的规则为先进先出;而消息堆栈则是先进后出。典型的应用有论坛回帖排序等。
(6)日志或缓存系统
Virtual Memory功能非常适用于存放安全日志、网站日志以及数据缓存等。国内的新浪微博所构建的缓存系统是全球最大的Redis缓存系统。
在缓存应用方面,Redis同样也是一个内存数据库,拥有Memcached的快速、稳定等特性,并且支持数据快照功能,开发人员可以通过配置文件制定数据快照时间间隔,Redis会将数据快照自动存放于硬盘中,这样就算服务器突然停止服务,Redis也极少会出现丢失数据的现象。所以近些年来越来越多的大型物联网公司开始使用Rdeis作为缓存服务器。
相比于MongoDB,Redis是更加彻底的Key-Value存储系统,它没有专门的查询语言,也没有明确的数据类型。一个字符串,可以代替所有的储存类型,例如直接使用string类型存放传统文本、代码、序列等;也可以直接存放数据流,例如图片、视频等,并且没有数据大小的限制。虽然没有数据类型的限制,单位了方便数据管理,Redis提供了多种数据结构类型,分别为string(字符串)、list(列表)、sets(集合)或者是ordered sets(有序集合)。所有的数据类型都支持push/pop、add/remove、服务端并集、交集、sets集合差别等操作,这些操作都具有原子性的,Redis还支持各种不同的排序功能。
与Memcached一样,Redis的储存方式给予内存的,所有的数据读写都在内存中完成。单Memcache使用的libevent库,而Redis则原生使用epoll异步通信模型,所以在性能上比Memcache更加优秀。同时,Redis还提供了Virtual Memory功能,使得数据能够在指定间隔时间内保存到硬盘(由后台自动完成),避免数据在内存中丢失。
与MongoDB一样,虽然统称为NoSQL,但Redis无论在使用方式上,还是数据处理流程上都有较大的区别。
与MongoDB相比,Redis主要优点分别如下:
☞ Redis数据储存在内存中完成,所以在数度上比较有优势,MongoDB使用memory-mapped处理方式本质上还是磁盘操作
☞ Redis与MongoDB在设计之初均考虑到分布式处理能力,所以这两者都提供了集群部署配置接口,但相对而言Redis及全部输更加容积及稳定。
☞ Redis提供了简单的事务支持,MongoDB不支持事务。
Redis的缺点:
☞ Redis没有字段的概念,所以在数据查询上功能比较弱,支持的特性比较简单。
☞ Redis单个value的最大容量可达1GB,虽然MongoDB单个文档最大容量为16MB,但MongoDB提供了GridFS用于实现超大文件存储。
☞ 由于Redis本质上是一个内存书库,所以内存硬件的容量大小直接决定了Redis可用的数据库空间(Redis2.0新增了Virtual Memory功能解决了容量问题,但虚拟内存本质上就是磁盘)。
☞ Redis虽然在内存中查询数据,但为了确保数据的安全,Redis默认情况下使用子线程对数据进行持久化处理,如果配置不当(默认刷新间隔为20秒),将会是系统运行效率适得其反。
☞ MongoDB提供了mapreduce数据分析功能,Redis则没有数据分析功能。
随着大数据的爆发,无论是Redis还是MongoDB,都将会迎来越来越多的功能更新,选择哪一个作为NoSQL数据库,要看项目需要及开发人员技术水平。现在对这两种数据库进行总结及对比还为时过早。而Memcache已经好久没更新了,所以无论从性能、功能还是后续更新考虑,Redis都是Memcach的理想替代品。

Redis在django中的使用
记录评论数、热度、浏览量等。(使用hash)
记录我的收藏、我的文章等列表类型的数据。(使用zset)
记录某篇文章的点赞人员列表。(使用zset)
缓存频繁访问但又不太多的东西,例如:热门推荐。(使用hash)
记录与当前浏览的对象相关的对象,例如:与当前文章相关的文章。(使用list)
记录分类排行榜。(使用zset)
缓存历史记录,如:登录历史等。(使用zset或hash)
string的应用
常见的用途:缓存用户基本信息。
将用户的基本信息序列化成JSON字符串,然后将序列化后的字符串保存到 Redis 来缓存。反过来,当取用户信息时,会经过一次反序列化。
list的应用
通常用作异步队列、存储列表数据。
hash的应用
无序字典,字典的值只能是字符串。
hash的最后一个元素删除后,该数据结构自动被删除。
hash也可以存储用户的信息,和string不同的是,hash可以对用户信息的每个字段单独存储。
set集合的应用
set的最后一个元素删除后,该数据结构自动被删除。
set中不存在重复元素,利用这个特性,我们可以用来存储防止重复事件发生的情况。例如:在抽奖活动系统中存储中奖用户,防止重复中奖。

数据库的数据是实时更新的吗?每点击一次,数据库数据修改一次?
要想做到实时更新,最好是数据库支持通知机制,也就是数据库某表数据发生变化自动通知前台,也就是数据库自动推数据。通常条件比较苛刻, 我们通常用程序定时拉数据,做不到真正的实时,主要看你拉数据的间隔,和执行效率。 当然也有一些黑科技,比如用程序通
知程序而不是导数据库去拉数据。
Redis hash 的个数: Redis 集群中内置了 16384 个哈希槽

redis 一个 hash 可以存多少数据?
内存允许的情况下,可以存超过 40 亿数据。
Hash 也可以用来存储用户信息,和 String 不同的是 Hash 可以对用户信息的每个字段单独存储,String 则需要序列化用户的所有字段后存储.并且 String 需要以整个字符串的形式获取用户,而 hash 可以只获取部分数据,从而节约网络流量.不过 hash 内存占用要大于String,这是 hash 的缺点.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值