数据库相关------一些面试题

1.列举常见的关系型数据库和非关系型都有那些、它们的区别?

  关系型数据库:Oracle、DB二、Microsoft SQL Server、Microsoft Access、MySQLphp

  非关系型数据库:NoSql、Cloudant、MongoDB、Redis、HBasepython

  二者的区别:mysql

  关系型数据库 非关系型数据库
特性

  一、关系型数据库,是指采用了关系模型来组织数据的数据库;  
  二、关系型数据库的最大特色就是事务的一致性;
  三、简单来讲,关系模型指的就是二维表格模型,而一个关系型数据库就是由二维表及其之间的联系所组              成的一个数据组织。面试

  一、使用键值对存储数据;
  二、分布式
  三、通常支持ACID(原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability))特性;
  四、非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合。
优势   一、容易理解:二维表结构是很是贴近逻辑世界一个概念,关系模型相对网状、层次等其                                   他模型来讲更容易理解;
  二、使用方便:通用的SQL语言使得操做关系型数据库很是方便;
  三、易于维护:丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了                                  数据冗余和数据不一致的几率;
  四、支持SQL,可用于复杂的查询。
  一、无需通过sql层的解析,读写性能很高
  二、基于键值对,数据没有耦合性,容易扩展
  三、存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等而                   关系型数据库则只支持基础类型。
缺点   一、为了维护一致性所付出的巨大代价就是其读写性能比较差
  二、固定的表结构;
  三、高并发读写需求;
  四、海量数据的高效率读写;
   一、不提供sql支持,学习和使用成本较高;
   二、无事务处理,附加功能和报表等支持也很差;

2.MySQL常见数据库引擎及比较?

MySQL支持数个存储引擎做为对不一样表的类型的处理器。MySQL存储引擎包括处理事务安全表的引擎和处理非事务安全表的引擎:redis

MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。MyISAM在全部MySQL配置里被支持,它是默认的存储引擎,除非你配置MySQL默认使用另一个引擎。算法

MEMORY存储引擎提供“内存中”表。MERGE存储引擎容许集合将被处理一样的MyISAM表做为一个单独的表。就像MyISAM同样,MEMORY和MERGE存储引擎处理非事务表,这两个引擎也都被默认包含在MySQL中。sql

3.简述数据三大范式?

  数据库设计对数据的存储性能,还有开发人员对数据的操做都有莫大的关系。因此创建科学的,规范的的数据库是须要知足一些规范的来优化数据数据存储方式。在关系型数据库中这些规范就能够称为范式。mongodb

  第一范式:当关系模式R的全部属性都不能在分解为更基本的数据单位时,称R是知足第一范式的,简记为1NF。知足第一范式是关系模式规范化的最低要求,不然,将有不少基本操做在这样的关系模式中实现不了。数据库

  第二范式:若是关系模式R知足第一范式,而且R得全部非主属性都彻底依赖于R的每个候选关键属性,称R知足第二范式,简记为2NF。编程

  第三范式设R是一个知足第一范式条件的关系模式,X是R的任意属性集,若是X非传递依赖于R的任意一个候选关键字,称R知足第三范式,简记为3NF.

简答题:

  第一范式(确保每列保持原子性)即每列再也不须要拆分
  第二范式(确保表中的每列都和主键相关)
  第三范式(确保每列都和主键列直接相关,而不是间接相关)

4.一条 SQL 语句执行的很慢的缘由有哪些?

能够分两种状况回答。

  (1).大多数状况是正常的,只是偶尔会出现很慢的状况:

    -- 数据库在刷新脏页,例如redo log中写满了须要同步到磁盘。

      ps:往数据库插入或更新一条数据,数据库会在内存中把对应字段更新,但不会立刻同步持久化到磁盘中,而是写入redo log中,空闲时再将数据同步到磁盘

    -- 执行的时候,遇到了锁,表锁或者是行锁。

  (2).在数据量不变的状况下,这条SQL语句一直以来都执行的很慢:

    -- 没有用上索引,例如该字段没有索引,因为对字段进行运算、函数操做致使没法用索引。

    -- 数据库选错了索引。

5.讲讲MYSQL事务,说说ACID是什么?

什么是事务?

  事务是由一步或几步数据库操做序列组成逻辑执行单元,这系列操做要么所有执行要么所有放弃执行。程序和事务是两个不一样的概念。

  通常而言:一段程序中可能包含多个事务。

  事务具备四大特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability)。简称ACID。

           (1)原子性:事务是应用中最小的执行单位,就如原子是天然界最小颗粒,具备不可再分的特征同样。事务是应用中不可再分的最小逻辑执行体。

           (2)一致性:事务执行的结果,必须使数据库从一个一致性状态,变到另外一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是经过原子性来保证的。

           (3)隔离性:各个事务的执行互不干扰,任意一个事务的内部操做对其余并发的事务,都是隔离的。也就是说:并发执行的事务之间不能看到对方的中间状态,并发执行的事务之间不能相互影响。

           (4)持续性:持续性也称为持久性,指事务一旦提交,对数据所作的任何改变,都要记录到永久存储器中,一般是保存进物理数据库。

MYSQL的事务处理主要有两种方法:

  1.用begin,rollback,commit来实现
    begin开始一个事务
    rollback事务回滚
       commit 事务确认
  2.直接用set来改变mysql的自动提交模式
          mysql默认是自动提交的,也就是你提交一个query,就直接执行!能够经过
          set autocommit = 0 禁止自动提交
          set autocommit = 1 开启自动提交

 

6.简述简述触发器、函数、视图、存储过程?

一、视图

  视图只是一种逻辑对象,是一种虚拟表,它并非物理对象,由于视图不占物理存储空间,在视图中被查询的表称为视图的基表,大多数的select语句均可以用在建立视图中(说白了,视图就是一种虚拟表,就像是一张电子照片)

  优势:集中用户使用的数据,掩码数据的复杂性,简化权限管理以及为向其余应用程序输出而从新组织数据等

二、触发器

  (1)触发器是一个特殊的存储过程,它是MySQL在insert、update、delete的时候自动执行的代码块。

  (2)触发器必须定义在特定的表上。

  (3)自动执行,不能直接调用,

三、函数

  它跟php或js中的函数几乎同样:须要先定义,而后调用。

  只是规定,这个函数,必需要返回数据——要有返回值

四、存储过程

  存储过程(procedure),概念相似于函数,就是把一段代码封装起来,当要执行这一段代码的时候,能够经过调用该存储过程来实现。

  在封装的语句体里面,能够同if/else ,case,while等控制结构,能够进行sql编程,查看现有的存储过程。

7.如何基于数据库实现商城商品计数器?

  见下题解;

8.MySQL索引种类。

单列:

  普通索引:加速查找
  惟一索引:加速查找 + 约束:不能重复(只能有一个空,否则就重复了)
       主键(primay key):加速查找 + 约束:不能重复 + 不能为空
多列:
  联合索引(多个列建立索引)-----> 至关于单列的普通索引
  联合惟一索引 -----> 至关于单列的惟一索引
  ps:联合索引的特色:遵循最左前缀的规则
其余:
  合并索引,利用多个单例索引查询;(例如在数据库查用户名和密码,分别给用户名和密码创建索引)
  覆盖索引,在索引表中就能将想要的数据查询到;

9.索引在什么状况下遵循最左前缀的规则?

答:联合索引

索引的最左前缀原理:
  一般咱们在创建联合索引的时候,也就是对多个字段创建索引,相信创建过索引的同窗们会发现,不管是oralce仍是mysql都会让咱们选择索引的顺序,好比咱们想在a,b,c三个字段上创建一个联合索引,咱们能够选择本身想要的优先级,a、b、c,或者是b、a、c 或者是c、a、b等顺序。为何数据库会让咱们选择字段的顺序呢?不都是三个字段的联合索引么?这里就引出了数据库索引的最左前缀原理。
  好比:索引index1:(a,b,c)有三个字段,咱们在使用sql语句来查询的时候,会发现不少状况下不按照咱们想象的来走索引。

10.主键和外键的区别?

主键:

  定义:惟一标识一条记录,不能有重复的,不容许为空。

  做用:用来保证数据完整性。

  个数:主键只能有一个。

ALTER TABLE “表名” ADD PRIMARY KEY (字段名)

外键:  

  定义:表的外键是另外一表的主键, 外键能够有重复的, 能够是空值。

  做用:用来和其余表创建联系用的。

  个数:一个表能够有多个外键。

ALTER TABLE “表名” ADD FOREIGN KEY (字段名) REFERENCES “另外一张表名”( 字段名)

11.MySQL常见的函数?

数学函数
字符串函数
日期和时间函数
条件判断函数
系统信息函数
加密函数
格式化函数

12.列举建立索引可是没法命中索引的8种状况。

1.- like '%xx';

select * from tb1 where name like '%cn';

2.- 使用函数;

select * from tb1 where reverse(name) = 'Clint';

3.- or;

select * from tb1 where nid = 1 or email = 'Clint@qq.com';

特别的:当or条件中有未创建索引的列才失效,如下会走索引;

select * from tb1 where nid = 1 or name = 'jack';
select * from tb1 where nid = 1 or email = 'rose@163.com' and name = 'rose'

4.- 类型不一致;
  若是列是字符串类型,传入条件是必须用引号引发来;

select * from tb1 where name = 999;

5.- !=

select * from tb1 where name != 'rose'

特别的:若是是主键,则仍是会走索引

select * from tb1 where nid != 123

6.- >

select * from tb1 where name > 'alex'

特别的:若是是主键或索引是整数类型,则仍是会走索引

select * from tb1 where nid > 123
select * from tb1 where num > 123

7.- order by

select email from tb1 order by name desc;

当根据索引排序时候,选择的映射若是不是索引,则不走索引
特别的:若是对主键排序,则仍是走索引:

select * from tb1 order by nid desc;

8.- 组合索引最左前缀
若是组合索引为:(name,email)
name and email -- 使用索引
name -- 使用索引
email -- 不使用索引

13.如何开启慢日志查询?

为何要开启慢查询日志:

  开启慢查询日志,可让MySQL记录下查询超过指定时间的语句,经过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。

怎么开启:

  参数说明:

      slow_query_log 慢查询开启状态
      slow_query_log_file 慢查询日志存放的位置(这个目录须要MySQL的运行账号的可写权限,通常设置为MySQL的数据存放目录)
      long_query_time 查询超过多少秒才记录

设置步骤:

1.查看慢查询相关参数

mysql> show variables like 'slow_query%';
+---------------------------+----------------------------------+
| Variable_name             | Value                            |
+---------------------------+----------------------------------+
| slow_query_log            | OFF                              |
| slow_query_log_file       | /mysql/data/localhost-slow.log   |
+---------------------------+----------------------------------+

mysql> show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+

2.设置方法
方法一:全局变量设置
  将 slow_query_log 全局变量设置为“ON”状态

mysql> set global slow_query_log='ON'; 

  设置慢查询日志存放的位置

mysql> set global slow_query_log_file='/usr/local/mysql/data/slow.log';

  查询超过1秒就记录

mysql> set global long_query_time=1;

方法二:配置文件设置
  修改配置文件my.cnf,在[mysqld]下的下方加入

[mysqld]
slow_query_log = ON
slow_query_log_file = /usr/local/mysql/data/slow.log
long_query_time = 1

3.重启MySQL服务

service mysqld restart

4.查看设置后的参数

复制代码
mysql> show variables like 'slow_query%';
+---------------------+--------------------------------+
| Variable_name       | Value                          |
+---------------------+--------------------------------+
| slow_query_log      | ON                             |
| slow_query_log_file | /usr/local/mysql/data/slow.log |
+---------------------+--------------------------------+

mysql> show variables like 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+
复制代码

5.测试

  1.执行一条慢查询SQL语句

mysql> select sleep(2);

  2.查看是否生成慢查询日志

ls /usr/local/mysql/data/slow.log

若是日志存在,MySQL开启慢查询设置成功!

14.数据库导入导出命令(结构+数据)?

导出数据库:

mysqldump -u 用户名 -p 数据库名 > 导出的文件名,如我输入的命令行:mysqldump -u root -p news > news.sql (输入后会让你输入进                          入MySQL的密码),(若是导出单张表的话在数据库名后面输入表名便可)

导入数据库:

1,将要导入的.sql文件移至bin文件下,这样的路径比较方便
2,同上面导出的第1步
3,进入MySQL:mysql -u 用户名 -p ,如我输入的命令行:mysql -u root -p (输入一样后会让你输入MySQL的密码)
4,在MySQL-Front中新建你要建的数据库,这时是空数据库,如新建一个名为news的目标数据库
5,输入:mysql>use 目标数据库名,如我输入的命令行:mysql>use news;
6,导入文件:mysql>source 导入的文件名;如我输入的命令行:mysql>source news.sql;

15.数据库优化方案?

1.对查询进行优化,避免全表扫描

2.避免在where子句中对字段进行null值判断

16.char和varchar的区别?

1.定长和变长: char长度固定,varchar长度可变

2.存储容量不一样:char最多只能存放字符个数255,和编码无关;而varchar 最对能够存65532个字符

17.简述MySQL的执行计划?

* * *

18.在对name作了惟一索引前提下,简述如下区别:
 
        select * from tb where name = ‘CRM-Clint’ 
 
        select * from tb where name = ‘CRM-Clint’ limit 1

19.1000w条数据,使用limit offset 分页时,为何越日后翻越慢?如何解决?

1:先查主键,在分页;

select * from tb where id in (
select id from tb where limit 10 offset 30
)

2:按照也无需求是否能够设置只让用户看200页;

3:记录当前页 数据ID最大值和最小值,在翻页时,根据条件先进行筛选;筛选完毕以后,再根据limit offset 查询;

select * from (select * from tb where id > 22222222) as B limit 10 offset 0

若是用户本身修改页码,也可能致使慢;此时对url种的页码进行加密(rest framework );

20.什么是索引合并?

说明: 

一、索引合并是把几个索引的范围扫描合并成一个索引。
二、索引合并的时候,会对索引进行并集,交集或者先交集再并集操做,以便合并成一个索引。
三、这些须要合并的索引只能是一个表的。不能对多表进行索引合并。

怎么肯定?

在使用explain对sql语句进行操做时,若是使用了索引合并,那么在输出内容的type列会显示 index_merge,key列会显示出全部使用的索引。

21.什么是覆盖索引?

定义:索引是高效找到行的一个方法,当能经过检索索引就能够读取想要的数据,那就不须要再到数据表中读取行了。若是一个索引包含了(或覆盖了)知足查询语句中字段与条件的数据就叫作覆盖索引

查看覆盖索引:只须要在select关键字以前添加explain这个命令查看。当发起一个被索引覆盖的查询时,在explain的Extra列能够看到 Using index的标识。

22.简述数据库读写分离?

对于数据存储层高并发问题,最早想到的可能就是读写分离,在网站访问量大而且读写不平均的状况下,将存储分为master,slave两台,全部的写都路由到master上,全部的读都路由到slave上,而后master和slave同步。若是一台salve不够,能够加多台,好比一台master,3台slave。对于什么是读写分离,以及读写分离有什么好处,这里再也不叙述,有兴趣的能够参考 这里 。 
在设计读写分离的时候,有几种解决方案:
    1. 将读写分离放在dao层,在dao层, 全部的insert/update/delete都访问master库,全部的select 都访问salve库,这样对于业务层是透明的。 
    2. 将读写分离放在ORM层,好比mybatis能够经过mybatis plus拦截sql语句,全部的insert/update/delete都访问master库,全部的select 都访问salve库,这样对于dao层都是透明。 
    3. 放在代理层,好比MySQL-Proxy,这样针对整个应用程序都是透明的。 
对于绝大多数情景,读写分离都适用,可是读写分离有一个问题是master slave同步,这个同步是会有必定延迟。

23.简述数据库分库分表?(水平、垂直)

数据库瓶颈:

  IO瓶颈:

      磁盘读IO瓶颈:热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO,下降查询速度    ---->  分表

      网络IO瓶颈:请求的数据太多,网络带宽不够                                                                                   ----->  分库

  CPU瓶颈:

      单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈          --->  水平分表

 

  水平分库:以字段为依据,按照必定的策略(hash、range等),将一个中的数据拆分到多个

  水平分表:同理,...,将一个中的数据拆分到多个

 

  垂直分库:为依据,按照业务归属不一样,将不一样的拆分到不一样的

  垂直分表:字段为依据,按照字段的活跃性,将表中的字段拆到不一样的表(主表和扩展表)中

分库分表工具:

  1. sharding-sphere:jar,前身是sharding-jdbc;
  2. TDDL:jar,Taobao Distribute Data Layer;

  3. Mycat:中间件。

24.redis和memcached还有MongoDB比较?

1.数据库类型方面  

  memcache数据结构单一,Redis不只仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储;
  Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其余,例如图片、视频等;

 

二、操做的便利性
  redis丰富一些,数据操做方面,redis更好一些,较少的网络IO次数;
  mongodb支持丰富的数据表达,索引,最相似关系型数据库,支持的查询语言很是丰富;

三、内存空间的大小和数据量的大小
  redis在2.0版本后增长了本身的VM特性,突破物理内存的限制;能够对key value设置过时时间(相似memcache);
  memcache能够修改最大可用内存,采用LRU算法
  mongoDB适合大数据量的存储,依赖操做系统VM作内存管理,吃内存也比较厉害,服务不要和别的服务在一块儿;

四、可用性(单点问题)
  redis,依赖客户端来实现分布式读写;主从复制时,每次从节点从新链接主节点都要依赖整个快照,无增量复制,因性能和效率问题,因此单点问题比较复杂;不支持自动sharding,须要依赖程序设定一致hash 机制;
  Memcache自己没有数据冗余机制,也不必;对于故障预防,采用依赖成熟的hash或者环状的算法,解决单点故障引发的抖动问题;
  mongoDB支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制;

六、数据一致性(事务支持
  Memcache 在并发场景下,用cas保证一致性;
  redis事务支持比较弱,只能保证事务中的每一个操做连续执行;
  mongoDB不支持事务;

七、数据分析
  mongoDB内置了数据分析的功能(mapreduce),其余不支持;

八、应用场景
  redis:数据量较小的、更小性能操做和运算上;
  memcache:用于在动态系统中减小数据库负载,提高性能;作缓存,提升性能(适合读多写少,对于数据量比较大,能够采用sharding);
  MongoDB:主要解决海量数据的访问效率问题;   

25.redis中数据库默认是多少个db 及做用?

redis下,数据库是由一个整数索引标识,而不是由一个数据库名称。默认状况下,一个客户端链接到数据库0。redis配置文件中下面的参数来控制数据库总数:
/etc/redis/redis.conf;该文件中,有个配置项 databases = 16 //默认有16个数据库

26.python操做redis的模块?

- 链接
- 直接链接:
    import redis 
    r = redis.Redis(host='10.211.55.4', port=6379)
    r.set('foo', 'Bar')
    print r.get('foo')
- 链接池:
    import redis
    pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
     
    r = redis.Redis(connection_pool=pool)
    r.set('foo', 'Bar')
    print r.get('foo')

27.若是redis中的某个列表中的数据量很是大,若是实现循环显示每个值?

- 若是一个列表在redis中保存了10w个值,我须要将全部值所有循环并显示,请问如何实现?
一个一个取值,列表没有iter方法,但能自定义

def list_scan_iter(name,count=3):
    start = 0
    while True:
        result = conn.lrange(name, start, start+count-1)
        start += count
        if not result:
            break
        for item in result:
            yield item

for val in list_scan_iter('num_list'):
    print(val)
场景:投票系统,script-redis                            

28.redis如何实现主从复制?以及数据同步机制?

和Mysql主从复制的缘由同样,Redis虽然读取写入的速度都特别快,可是也会产生读压力特别大的状况。为了分担读压力,Redis支持主从复制,Redis的主从结构能够采用一主多从或者级联结构,Redis主从复制能够根据是不是全量分为全量同步增量同步

29.redis中的sentinel的做用?

Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis作Master-slave的高可用方案时,假如master宕机了,Redis自己(包括它的不少客户端)都没有实现自动进行主备切换,而Redis-sentinel自己也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。

主要做用: 

  不时地监控redis是否按照预期良好地运行;

  若是发现某个redis节点运行出现情况,可以通知另一个进程(例如它的客户端);

  可以进行自动切换。当一个master节点不可用时,可以选举出master的多个slave(若是有超过一个slave的话)中的一个来做为新的master,其它的slave节点会将它所追随的master的地址改成被提高为master的slave的新地址;

30.如何实现redis集群?

redis集群、分片、分布式redis
redis-py-cluster
集群方案:
- redis cluster 官方提供的集群方案。
- codis,豌豆荚技术团队。
- tweproxy,Twiter技术团队。
redis cluster的原理?
- 基于分片来完成。
- redis将全部能放置数据的地方建立了 16384 个哈希槽。
- 若是设置集群的话,就能够为每一个实例分配哈希槽:
- 192.168.1.20【0-5000】
- 192.168.1.21【5001-10000】
- 192.168.1.22【10001-16384】
- 之后想要在redis中写值时,
set k1 123
将k1经过crc16的算法,将k1转换成一个数字。而后再将该数字和16384求余,若是获得的余数 3000,那么就将该值写入到 192.168.1.20 实例中。

31.redis中默认有多少个哈希槽?

Redis 集群中内置了 16384 个哈希槽,当须要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,而后把结果对 16384 求余数,这样每一个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大体均等的将哈希槽映射到不一样的节点.

Redis 集群没有使用一致性hash, 而是引入了哈希槽的概念。

Redis 集群有16384个哈希槽,每一个key经过CRC16校验后对16384取模来决定放置哪一个槽.集群的每一个节点负责一部分hash槽。这种结构很容易添加或者删除节点,而且不管是添加删除或者修改某一个节点,都不会形成集群不可用的状态。

使用哈希槽的好处就在于能够方便的添加或移除节点。

当须要增长节点时,只须要把其余节点的某些哈希槽挪到新节点就能够了;

当须要移除节点时,只须要把移除节点上的哈希槽挪到其余节点就好了;

在这一点上,咱们之后新增或移除节点的时候不用先停掉全部的 redis 服务。

**"用了哈希槽的概念,而没有用一致性哈希算法,不都是哈希么?这样作的缘由是为何呢?"
Redis Cluster是本身作的crc16的简单hash算法,没有用一致性hash。Redis的做者认为它的crc16(key) mod 16384的效果已经不错了,虽然没有一致性hash灵活,但实现很简单,节点增删时处理起来也很方便。

**"为了动态增删节点的时候,不至于丢失数据么?"
节点增删时不丢失数据和hash算法没什么关系,不丢失数据要求的是一份数据有多个副本。

**“还有集群总共有2的14次方,16384个哈希槽,那么每个哈希槽中存的key 和 value是什么?”
当你往Redis Cluster中加入一个Key时,会根据crc16(key) mod 16384计算这个key应该分布到哪一个hash slot中,一个hash slot中会有不少key和value。你能够理解成表的分区,使用单节点时的redis时只有一个表,全部的key都放在这个表里;改用Redis Cluster之后会自动为你生成16384个分区表,你insert数据时会根据上面的简单算法来决定你的key应该存在哪一个分区,每一个分区里有不少key。

32.简述redis的有哪几种持久化策略及比较?

RDB:每隔一段时间对redis进行一次持久化。
- 缺点:数据不完整
- 优势:速度快
AOF:把全部命令保存起来,若是想到从新生成到redis,那么就要把命令从新执行一次。
- 缺点:速度慢,文件比较大
- 优势:数据完整

33.列举redis支持的过时策略。

voltile-lru:    从已设置过时时间的数据集(server.db[i].expires)中挑选最近频率最少数据淘汰
  volatile-ttl:   从已设置过时时间的数据集(server.db[i].expires)中挑选将要过时的数据淘汰
  volatile-random:从已设置过时时间的数据集(server.db[i].expires)中任意选择数据淘汰

  
  allkeys-lru:       从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  allkeys-random:    从数据集(server.db[i].dict)中任意选择数据淘汰
  no-enviction(驱逐):禁止驱逐数据

34.MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中都是热点数据? 

相关知识:redis 内存数据集大小上升到必定大小的时候,就会施行数据淘汰策略(回收策略)。redis 提供 6种数据淘汰策略:

  volatile-lru:从已设置过时时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  volatile-ttl:从已设置过时时间的数据集(server.db[i].expires)中挑选将要过时的数据淘汰
  volatile-random:从已设置过时时间的数据集(server.db[i].expires)中任意选择数据淘汰
  allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  no-enviction(驱逐):禁止驱逐数据

35.写代码,基于redis的列表实现 先进先出、后进先出队列、优先级队列。

参看script—redis源码
from scrapy.utils.reqser import request_to_dict, request_from_dict

  from . import picklecompat


  class Base(object):
      """Per-spider base queue class"""

      def __init__(self, server, spider, key, serializer=None):
          """Initialize per-spider redis queue.

          Parameters
          ----------
          server : StrictRedis
              Redis client instance.
          spider : Spider
              Scrapy spider instance.
          key: str
              Redis key where to put and get messages.
          serializer : object
              Serializer object with ``loads`` and ``dumps`` methods.

          """
          if serializer is None:
              # Backward compatibility.
              # TODO: deprecate pickle.
              serializer = picklecompat
          if not hasattr(serializer, 'loads'):
              raise TypeError("serializer does not implement 'loads' function: %r"
                              % serializer)
          if not hasattr(serializer, 'dumps'):
              raise TypeError("serializer '%s' does not implement 'dumps' function: %r"
                              % serializer)

          self.server = server
          self.spider = spider
          self.key = key % {'spider': spider.name}
          self.serializer = serializer

      def _encode_request(self, request):
          """Encode a request object"""
          obj = request_to_dict(request, self.spider)
          return self.serializer.dumps(obj)

      def _decode_request(self, encoded_request):
          """Decode an request previously encoded"""
          obj = self.serializer.loads(encoded_request)
          return request_from_dict(obj, self.spider)

      def __len__(self):
          """Return the length of the queue"""
          raise NotImplementedError

      def push(self, request):
          """Push a request"""
          raise NotImplementedError

      def pop(self, timeout=0):
          """Pop a request"""
          raise NotImplementedError

      def clear(self):
          """Clear queue/stack"""
          self.server.delete(self.key)


  class FifoQueue(Base):
      """Per-spider FIFO queue"""

      def __len__(self):
          """Return the length of the queue"""
          return self.server.llen(self.key)

      def push(self, request):
          """Push a request"""
          self.server.lpush(self.key, self._encode_request(request))

      def pop(self, timeout=0):
          """Pop a request"""
          if timeout > 0:
              data = self.server.brpop(self.key, timeout)
              if isinstance(data, tuple):
                  data = data[1]
          else:
              data = self.server.rpop(self.key)
          if data:
              return self._decode_request(data)


  class PriorityQueue(Base):
      """Per-spider priority queue abstraction using redis' sorted set"""

      def __len__(self):
          """Return the length of the queue"""
          return self.server.zcard(self.key)

      def push(self, request):
          """Push a request"""
          data = self._encode_request(request)
          score = -request.priority
          # We don't use zadd method as the order of arguments change depending on
          # whether the class is Redis or StrictRedis, and the option of using
          # kwargs only accepts strings, not bytes.
          self.server.execute_command('ZADD', self.key, score, data)

      def pop(self, timeout=0):
          """
          Pop a request
          timeout not support in this queue class
          """
          # use atomic range/remove using multi/exec
          pipe = self.server.pipeline()
          pipe.multi()
          pipe.zrange(self.key, 0, 0).zremrangebyrank(self.key, 0, 0)
          results, count = pipe.execute()
          if results:
              return self._decode_request(results[0])


  class LifoQueue(Base):
      """Per-spider LIFO queue."""

      def __len__(self):
          """Return the length of the stack"""
          return self.server.llen(self.key)

      def push(self, request):
          """Push a request"""
          self.server.lpush(self.key, self._encode_request(request))

      def pop(self, timeout=0):
          """Pop a request"""
          if timeout > 0:
              data = self.server.blpop(self.key, timeout)
              if isinstance(data, tuple):
                  data = data[1]
          else:
              data = self.server.lpop(self.key)

          if data:
              return self._decode_request(data)


  # TODO: Deprecate the use of these names.
  SpiderQueue = FifoQueue
  SpiderStack = LifoQueue
  SpiderPriorityQueue = PriorityQueue
View Code

36.如何基于redis实现消息队列?

# 经过发布订阅模式的PUB、SUB实现消息队列
# 发布者发布消息到频道了,频道就是一个消息队列。
# 发布者:
import redis
conn = redis.Redis(host='127.0.0.1',port=6379)
conn.publish('104.9MH', "hahahahahaha")
# 订阅者:
import redis
conn = redis.Redis(host='127.0.0.1',port=6379)
pub = conn.pubsub()
pub.subscribe('104.9MH')
while True:
    msg= pub.parse_response()
    print(msg)
对了,redis 作消息队列不合适
业务上避免过分复用一个redis,用它作缓存、作计算,还作任务队列,压力太大,很差。

37.如何基于redis实现发布和订阅?以及发布订阅和消息队列的区别?

发布和订阅,只要有任务就给全部订阅者没人一份
  发布者:
      import redis

      conn = redis.Redis(host='127.0.0.1',port=6379)
      conn.publish('104.9MH', "hahaha")
  订阅者:
      import redis

      conn = redis.Redis(host='127.0.0.1',port=6379)
      pub = conn.pubsub()
      pub.subscribe('104.9MH')

      while True:
          msg= pub.parse_response()
          print(msg)

38.什么是codis及做用?

Codis 是一个分布式 Redis 解决方案, 对于上层的应用来讲, 链接到 Codis Proxy 和链接原生的 Redis Server 没有明显的区别
(不支持的命令列表), 上层应用能够像使用单机的 Redis 同样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工做,
全部后边的一切事情, 对于前面的客户端来讲是透明的, 能够简单的认为后边链接的是一个内存无限大的 Redis 服务.

39.什么是twemproxy及做用?

概念:

  Twemproxy是由Twitter开源的Redis代理,其基本原理是:Redis客户端把请求发送到Twemproxy,Twemproxy根据路由规则发送到正确的Redis实例,最后Twemproxy把结果聚集返回给客户端;
  Twemproxy经过引入一个代理层,将多个Redis实例进行统一管理,使Redis客户端只须要在Twemproxy上进行操做,而不须要关心后面有多少个Redis实例;

做用:

  实现Redis集群;

40.写代码实现redis事务操做。

41.redis中的watch的命令的做用?

watch 用于在进行事务操做的最后一步也就是在执行exec 以前对某个key进行监视;
若是这个被监视的key被改动,那么事务就被取消,不然事务正常执行;
通常在MULTI 命令前就用watch命令对某个key进行监控.若是想让key取消被监控,能够用unwatch命令; 

在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。
假设咱们经过WATCH命令在事务执行以前监控了多个Keys,假若在WATCH以后有任何Key的值发生了变化,
EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务执行失败。

面试题:你如何控制剩余的数量不会出问题?
方式一:- 经过redis的watch实现

import redis
conn = redis.Redis(host='127.0.0.1',port=6379)

# conn.set('count',1000)
val = conn.get('count')
print(val)

with conn.pipeline(transaction=True) as pipe:

# 先监视,本身的值没有被修改过
conn.watch('count')

# 事务开始
pipe.multi()
old_count = conn.get('count')
count = int(old_count)
print('如今剩余的商品有:%s',count)
input("问媳妇让不让买?")
pipe.set('count', count - 1)

# 执行,把全部命令一次性推送过去
pipe.execute()

方式二 - 数据库的锁

42.基于redis如何实现商城商品数量计数器?

import redis

conn = redis.Redis(host='192.168.1.41',port=6379)

conn.set('count',1000)

with conn.pipeline() as pipe:

    # 先监视,本身的值没有被修改过
    conn.watch('count')

    # 事务开始
    pipe.multi()
    old_count = conn.get('count')
    count = int(old_count)
    if count > 0:  # 有库存
        pipe.set('count', count - 1)

    # 执行,把全部命令一次性推送过去
    pipe.execute()

43.简述redis分布式锁和redlock的实现机制。

在不一样进程须要互斥地访问共享资源时,分布式锁是一种很是有用的技术手段。
有不少三方库和文章描述如何用Redis实现一个分布式锁管理器,可是这些库实现的方式差异很大
,并且不少简单的实现其实只需采用稍微增长一点复杂的设计就能够得到更好的可靠性。
用Redis实现分布式锁管理器的算法,咱们把这个算法称为RedLock。

实现
- 写值并设置超时时间
- 超过一半的redis实例设置成功,就表示加锁完成。
- 使用:安装redlock-py

from redlock import Redlock
dlm = Redlock(
[
{"host": "localhost", "port": 6379, "db": 0},
{"host": "localhost", "port": 6379, "db": 0},
{"host": "localhost", "port": 6379, "db": 0},
]
)
# 加锁,acquire
my_lock = dlm.lock("my_resource_name",10000)
if my_lock:
# J进行操做
# 解锁,release
dlm.unlock(my_lock)
else:
print('获取锁失败')

redis分布式锁?

# 不是单机操做,又多了一/多台机器
# redis内部是单进程、单线程,是数据安全的(只有本身的线程在操做数据)
----------------------------------------------------------------
\A、B、C,三个实例(主)
一、来了一个'隔壁老王'要操做,且不想让别人操做,so,加锁;
加锁:'隔壁老王'本身生成一个随机字符串,设置到A、B、C里(xxx=666)
二、来了一个'邻居老李'要操做A、B、C,一读发现里面有字符串,擦,被加锁了,不能操做了,等着吧~
三、'隔壁老王'解决完问题,不用锁了,把A、B、C里的key:'xxx'删掉;完成解锁
四、'邻居老李'如今能够访问,能够加锁了
# 问题:
一、若是'隔壁老王'加锁后忽然挂了,就没人解锁,就死锁了,其余人干看着无法用咋办?
二、若是'隔壁老王'去给A、B、C加锁的过程当中,刚加到A,'邻居老李'就去操做C了,加锁成功or失败?
三、若是'隔壁老王'去给A、B、C加锁时,C忽然挂了,此次加锁是成功仍是失败?
四、若是'隔壁老王'去给A、B、C加锁时,超时时间为5秒,加一个锁耗时3秒,这次加锁能成功吗?
# 解决
一、安全起见,让'隔壁老王'加锁时设置超时时间,超时的话就会自动解锁(删除key:'xxx')
二、加锁程度达到(1/2)+1个就表示加锁成功,即便没有给所有实例加锁;
三、加锁程度达到(1/2)+1个就表示加锁成功,即便没有给所有实例加锁;
四、不能成功,锁还没加完就过时,没有意义了,应该合理设置过时时间

44.什么是一致性哈希?Python中是否有相应模块?

一致性哈希 一致性hash算法(DHT)能够经过减小影响范围的方式,解决增减服务器致使的数据散列问题,从而解决了分布式环境下负载均衡问题; 若是存在热点数据,能够经过增添节点的方式,对热点区间进行划分,将压力分配至其余服务器,从新达到负载均衡的状态。

Python模块--hash_ring,即Python中的一致性hash

45.如何高效的找到redis中全部以clint开头的key?

redis 有一个keys命令。# 语法:KEYS pattern# 说明:返回与指定模式相匹配的所用的keys。该命令所支持的匹配模式以下:一、?:用于匹配单个字符。例如,h?llo能够匹配hello、hallo和hxllo等;二、*:用于匹配零个或者多个字符。例如,h*llo能够匹配hllo和heeeello等;二、[]:能够用来指定模式的选择区间。例如h[ae]llo能够匹配hello和hallo,可是不能匹配hillo。同时,可使用“/”符号来转义特殊的字符# 注意KEYS 的速度很是快,但若是数据太大,内存可能会崩掉,若是须要从一个数据集中查找特定的key,最好仍是用Redis的集合结构(set)来代替。

相关文章
相关标签/搜索