python后端面试第三部分:数据储存与缓存相关--长期维护

##################    数据储存与缓存相关        #######################

mysql数据库,

redis数据库, 

9. MySQL的索引一般是怎么实现的?
B-Tree(一般是B+Tree)和Hash,然后再简单介绍一下。

存储

存储可能包含rdbms,nosql以及缓存等,我以MySQL,redis举例。

mysql相关 mysql 是流行的RDBMS 关系型数据库

1.谈谈mysql字符集和排序规则?


2.var char与char的区别是什么?大小限制?utf8字符集下varchar最多能存多少个字符

char的长度是不可变的,而varchar的长度是可变的,也就是说,定义一个char[10]和varchar[10],如果存进去的是‘csdn’,那么char所占的长度依然为10, 除了字符‘csdn’外,后面跟六个空格,而varchar就立马把长度变为4了,取数据的时候,char类型的要用trim()去掉多余的空格,而varchar是不需要的。

尽管如此,char的存取数度还是要比varchar要快得多,因为其长度固定,方便程序的存储与查找;但是char也为此付出的是空间的代价,因为其长度固定,所以难免会有多余的空格占位符占据空间,可谓是以空间换取时间效率,而varchar是以空间效率为首位的。

再者,char的存储方式是,对英文字符(ASCII)占用1个字节,对一个汉字占用两个字节;而varchar的存储方式是,对每个英文字符占用2个字节,汉字也占用2个字节,两者的存储数据都非unicode的字符数据。

首先要确定mysql版本4.0版本以下,varchar(50),指的是50字节,如果存放UTF8汉字时,只能存16个(每个汉字3字节)

5.0版本以上,varchar(50),指的是50字符,无论存放的是数字、字母还是UTF8汉字(每个汉字3字节),都可以存放50个。

3.primary key和unique的区别?


4.外键有什么用,是否该用外键?外键一定需要索引吗?


5.myisam与innodb的区别?innodb的两阶段锁定协议是什么情况?


6.索引有什么用,大致原理是什么?设计索引有什么注意点? 

redis相关 

1.什么场景用redis,为什么mysql不适合?

redis 所有数据放在内存中,内存数据库。nosql(不过也可以持久化,持久化使用RDB或AOF方式。)

mysql无论数据还是索引都放在硬盘中,使用时才交换到内存中,能够处理远超内存总量的数据。

mongodb,是一个内存数据库,数据都放在内存中。持久化:mongodb的所有数据实际上是存放在硬盘的,所有要操作的数据通过mmap的方式映射到内存某个区域内。然后,mongodb就在这块区域里面进行数据修改,避免了零碎的硬盘操作。

数据量和性能:
当物理内存够用的时候,redis>mongodb>mysql
当物理内存不够用的时候,redis和mongodb都会使用虚拟内存。
实际上如果redis要开始虚拟内存,那很明显要么加内存条,要么你换个数据库了。
但是,mongodb不一样,只要,业务上能保证,冷热数据的读写比,使得热数据在物理内存中,mmap的交换较少。
mongodb还是能够保证性能。有人使用mongodb存储了上T的数据。
mysql,mysql根本就不需要担心数据量跟内存下的关系。不过,内存的量跟热数据的关系会极大地影响性能表现。
当物理内存和虚拟内存都不够用的时候,估计除了mysql你没什么好选择了。
其实,从数据存储原理来看,我更倾向于将mongodb归类为硬盘数据库,但是使用了mmap作为加速的手段而已。

2.谈谈redis的事务?用事务模拟原子+1操作?原子操作还有其它解决方案吗?


MULTI、EXEC、DISCARD和WATCH命令是Redis事务功能的基础。
Redis事务允许在一次单独的步骤中执行一组命令,并且可以保证如下两个重要事项:

1. Redis会将一个事务中的所有命令序列化,然后按顺序执行。Redis不可能在一个Redis事务的执行过程中插入执行
另一个客户端发出的请求。这样便能保证Redis将这些命令作为一个单独的隔离操作执行。
2.在一个Redis事务中,Redis要么执行其中的所有命令,要么什么都不执行。因此,Redis事务能够保证原子性。

3.redis内存满了会怎么样?

可以配置文件,选择删除某些键值或者直接报错。。。

数据库部分

    MySQL锁有几种;死锁是怎么产生的;

    为何,以及如何分区、分表;

    MySQL的char varchar text的区别;

    了解join么,有几种,有何区别,A LEFT JOIN B,查询的结果中,B没有的那部分是如何显示的(NULL);

    索引类型有几种,BTree索引和hash索引的区别(我没答上来这俩在磁盘结构上的区别);

    手写:如何对查询命令进行优化;

    NoSQL了解么,和关系数据库的区别;redis有几种常用存储类型;

数据库的索引,说一下非主键索引是怎么实现的?

数据储存与缓存相关

1.数据库事务的四个特性及含义

数据库事务的4个特性:原子性、持久性、一致性、隔离性

原子性:整个事务中的所有操作要么全部完成, 要么全部都不完成, 如果在事务中操作出现异常,那么事务将会进行回滚, 就像这个事务从来没有执行过一样。

持久性:在事务完成后,该事务所有的操作都将持久化在数据库中, 不会被回滚。

一致性:在事务开始之前和事务结束之后, 数据库的完整性约束并没有被破坏。

隔离性:确保在同一时间类只有一个事务处理某个数据。

2.数据库索引使用了什么数据结构?

数据库索引对于非主键索引使用B树, 对于主键索引使用B+树

3.数据库优化的思路

SQL语句优化:

1. 尽量避免在where语句后面使用 !=、<>操作符以及对NULL值得判断, 否则引擎将放弃索引而使用全表扫描。

2. 使用exists替换in。

3. 尽量放弃使用select *, 需要什么数据就取出什么数据。

4. 使用join代替子查询。

5. 设置合适的字段属性:例如尽量把字段设置为NOT NULL, 这样引擎就不要对比NULL值

4.MySQL中myisam与innodb的区别

1. innodb支持事物, myisam不支持事物

2. innodb支持行级锁, myisam支持表级锁

3. innodb支持MVC, myisam不支持

4. innodb支持外键, myisam不支持

5. innodb不支持全文索引,myisam支持

5.Redis支持的数据类型

字符串,集合, 有序集合,哈希, 列表

6.redis持久化的几种方式

1. 快照: 默认使用这种方式,将数据快照存放在特定的二进制文件中。

2. AOF: 将每一条命令都储存, 恢复时再将每一天命令进行运行。

7. redis如何实现热数据缓存?

当redis的内存数据大小上升到一定大小时, 就会实施数据淘汰策略, redis提供了6中数据淘汰策略

volatile-LRU:从已经设置过期时间的数据中挑选最近最少使用的数据淘汰

volatile-TTL: 从已经设置过期时间的数据中挑选即将要过期的数据淘汰

volatile-RANDOM: 从已经设置过期时间的数据中随机选择数据进行淘汰

allkeys-LRU:从数据集中挑选最近最少使用的数据淘汰

allkeys-random:从数据集中任意选择数据淘汰

no-enviction:禁止驱逐数据

8.Redis 常见的性能问题都有哪些?

1. master写内存快照, save命令调度rdbSave函数会阻塞主线程的工作, 可能会导致间断性的暂停服务。

2. master AOF持久化, 最好不要使用AOF来进行持久化, 这个持久化方式对性能有着极大的影响。

3. redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。

9.mysql字符集和排序规则?

mysql中一个字符集至少有一个或者多个排序方式, 比如utf8就有:utf8_general_ci , utf8_general_cs等等。

排序规则命名规则:字符集名字_语言_后缀, 其中

1. _ci:不区分大小写的排序方式

2. _cs:区分大小写的排序方式

3. _bin:二进制排序方式,大小比较将根据字符编码,不涉及人类语言,因此_bin的排序方式不包含人类语言

字符集最常用的包括utf8, utf8md4。

10.varchar与char的区别是什么?大小限制?utf8字符集下varchar最多能存多少个字符?

char和varchar最大的不同就是一个是固定长度,一个是可变长度。由于是可变长度,因此存储的是实际字符串再加上一个记录字符串长度的字节。如果分配给char或varchar列的值超过 列的最大长度,则对值进行裁剪。

varchar(M)和char(M),M都表示字符数.varchar的最大长度为65535个字节,不同的编码所对应的最大可存储的字符数不同。char最多可以存放255个字符,不同的编码最大可用字节数不同。

字符类型若为utf8,每个字符最多占3个字节,varchar最大长度不能超过21845。

11.primary key和unique的区别?

一个表只能有一个primary key, 一个表可以有多个unique key。

unique key约束只针对非主键列, 可以为空值, primary key约束针对主键列, 不允许有空值。

12.外键有什么用,是否该用外键?外键一定需要索引吗?

外键是为了一张表记录的数据不会太过冗余,也是为了数据的一致性和完整性。

如果业务逻辑相当的复杂那么建议使用外键来保证数据的一致性和完整性, 如果业务逻辑不复杂则可以不使用外键, 仅靠程序中来保证数据的一致性和完整性, 或者业务对数据的一致性完整性要求相当的高, 那么一定要用外键。同时如果为了不让mysql在性能有任何的形象应该避免使用外键。 所有应该视当前业务,数据等情况决定是否使用外键。

外键需要索引, 因为外键在查询,更新,删除数据时会对数据进行查找, 所以需要对外键建立索引。

13.索引有什么用?

对于建立索引的列, mysql的查询效率会提高很多。

14.谈谈redis的事务?用事务模拟原子+1操作?原子操作还有其它解决方案吗?

redis的事务使用关键字multi开启事务, 使用exec执行事务中的语句,它可以执行多条语句, 所有的命令按照先进先运行的的运行, 不会被其他的命令加塞。

用事务模拟原子+1操作:

multi

incr xx

exec

原子操作 可以使用 incr操作实现

15.redis集群?

redis-cluster

安全相关

sql注入是怎么产生的,如何防止?

sql注入产生原因:因为在程序开发过程中没有对sql语句进行检查或未进行关键字检查, 导致客户端可以提交sql语句到服务器运行。

如何防止:

1.对sql与进行检查, 过滤。

2.不要使用sql拼接的方式来拼接sql语句, 对常用的方法进行封装避免暴露sql语句(使用ORM)。

2.xss如何预防?

对用户输入做严格的防范, , 在服务端对用户数据进行转义处理。

1. 列举常见的关系型数据库和非关系型都有哪些?
MySQL/SqlServer MongoDB/Redis
https://db-engines.com/en/ranking
2. MySQL常见数据库引擎及比较?
MyISAM 适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。

InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。他是它支持“行锁” ,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。

mysql 数据库引擎: http://www.cnblogs.com/0201zcr/p/5296843.html
MySQL存储引擎--MyISAM与InnoDB区别: https://segmentfault.com/a/1190000008227211
3. 简述数据三大范式?
第一范式:确保每列的原子性.
如果每列(或者每个属性)都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式.
例如:顾客表(姓名、编号、地址、……)其中"地址"列还可以细分为国家、省、市、区等。
第二范式:在第一范式的基础上更进一层,目标是确保表中的每列都和主键相关.
如果一个关系满足第一范式,并且除了主键以外的其它列,都依赖于该主键,则满足第二范式.
例如:订单表(订单编号、产品编号、定购日期、价格、……),"订单编号"为主键,"产品编号"和主键列没有直接的关系,即"产品编号"列不依赖于主键列,应删除该列。
第三范式:在第二范式的基础上更进一层,目标是确保每列都和主键列直接相关,而不是间接相关.
如果一个关系满足第二范式,并且除了主键以外的其它列都不依赖于主键列,则满足第三范式.
为了理解第三范式,需要根据Armstrong公里之一定义传递依赖。假设A、B和C是关系R的三个属性,如果A-〉B且B-〉C,则从这些函数依赖中,可以得出A-〉C,如上所述,依赖A-〉C是传递依赖。
例如:订单表(订单编号,定购日期,顾客编号,顾客姓名,……),初看该表没有问题,满足第二范式,每列都和主键列"订单编号"相关,再细看你会发现"顾客姓名"和"顾客编号"相关,"顾客编号"和"订单编号"又相关,最后经过传递依赖,"顾客姓名"也和"订单编号"相关。为了满足第三范式,应去掉"顾客姓名"列,放入客户表中。 
4. 什么是事务?MySQL如何支持事务?
数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
彻底理解数据库事务: http://www.hollischuang.com/archives/898
5. 简述数据库设计中一对多和多对多的应用场景?
一对一关系示例:
一个学生对应一个学生档案材料,或者每个人都有唯一的身份证编号。
一对多关系示例:(下拉单选)
一个学生只属于一个班,但是一个班级有多名学生。
多对多关系示例:(下拉多选)
一个学生可以选择多门课,一门课也有多名学生。
6. 如何基于数据库实现商城商品计数器?

http://www.cnblogs.com/phpcoder/p/4665850.html
7. 常见SQL(必备)

## 详见武沛齐博客:https://www.cnblogs.com/wupeiqi/articles/5729934.html
8. 简述触发器、函数、视图、存储过程?
1)存储过程?
一组为了完成特定功能的SQL 语句集,经编译后存储在数据库。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程在创建时即在服务器上进行编译,所以执行起来比单个SQL语句快,因为调用存储过程比直接向服务端发送大量SQL语句在执行速度快。
对于存储过程,可以接收参数,其参数有三类:
 in 仅用于传入参数用
 out 仅用于返回值用
 inout 既可以传入又可以当作返回值
2)函数?
封装一段sql代码,完成一种特定的功能,必须返回结果。其余特性基本跟存储过程相同
3)函数与存储过程的区别?
3.1) 函数有且只有一个返回值,而存储过程不能有返回值。
3.2) 存储过程可以实现很复杂的业务逻辑,函数有很多限制。不能在函数中使用insert,update,delete,create等语句
3.3)存储过程可以调用函数。但函数不能调用存储过程。
3.4)存储过程一般是作为一个独立的部分来调用。而函数可以作为查询语句的一个部分来调用。
4)视图?
视图是基于 SQL 语句的结果集的可视化虚拟表。
视图中的字段来自一个或多个数据库中的真实表的字段。视图并不在数据库中以存储数据值集形式存在,而存在于实际引用的数据库表中,视图的构成可以是单表查询,多表联合查询,分组查询以及计算(表达式)查询等。行和列数据在引用视图时动态生成
5)触发器?
触发器(TRIGGER)与函数类似,需要声明、执行。但是触发器的执行不是由程序调用,而是由事件来触发从而实现执行。对某个表进行【增/删/改】操作的前后如果希望触发某个特定的行为时,可以使用触发器,触发器用于定制用户对表的行进行【增/删/改】前后的行为
9. MySQL索引种类
MySQL目前主要有以下几种索引类型:
1.普通索引
2.唯一索引
3.主键索引
4.组合索引
5.全文索引
https://www.cnblogs.com/luyucheng/p/6289714.html
10. 索引在什么情况下遵循最左前缀的规则?

https://www.cnblogs.com/jamesbd/p/4333901.html
11. 主键和外键的区别?
1.主键是能确定一条记录的唯一标识,比如,一条记录包括身份正号,姓名,年龄。身份证号是唯一能确定你这个人的,其他都可能有重复,所以,身份证号是主键。 
2.外键用于与另一张表的关联。是能确定另一张表记录的字段,用于保持数据的一致性。一个表可以有多个外键。
12. MySQL常见的函数?

https://blog.csdn.net/sugang_ximi/article/details/6664748
13. 列举 创建索引但是无法命中索引的8种情况。
- like '%xx'
select * from tb1 where name like '%cn';
- 使用函数
select * from tb1 where reverse(name) = 'wupeiqi';
- or
select * from tb1 where nid = 1 or email = '[email protected]';
特别的:当or条件中有未建立索引的列才失效,以下会走索引
select * from tb1 where nid = 1 or name = 'seven';
select * from tb1 where nid = 1 or email = '[email protected]' and name = 'alex'
- 类型不一致
如果列是字符串类型,传入条件是必须用引号引起来,不然...
select * from tb1 where name = 999;
- !=
select * from tb1 where name != 'alex'
特别的:如果是主键,则还是会走索引
select * from tb1 where nid != 123
- >
select * from tb1 where name > 'alex'
特别的:如果是主键或索引是整数类型,则还是会走索引
select * from tb1 where nid > 123
select * from tb1 where num > 123
- order by
select email from tb1 order by name desc;
当根据索引排序时候,选择的映射如果不是索引,则不走索引
特别的:如果对主键排序,则还是走索引:
select * from tb1 order by nid desc;

https://www.cnblogs.com/iyouyue/p/9004018.html#_label34
14. 如何开启慢日志查询?

https://www.jianshu.com/p/9f9c9326f8f4
15. 数据库导入导出命令(结构+数据)?
- 导出现有数据库数据:
mysqldump -u用户名 -p密码 数据库名称 >导出文件路径 # 结构+数据
mysqldump -u用户名 -p密码 -d 数据库名称 >导出文件路径 # 结构 
- 导入现有数据库数据:
mysqldump -uroot -p密码 数据库名称 < 文件路径
16. 数据库优化方案?
1. 避免全表扫描,首先应考虑在 where 及 orderby 涉及的列上建立索引。
2. 避免在 where 子句中对字段进行 null 值判断,导致引擎放弃使用索引而进行全表扫描 
3. 避免在 where 子句中使用 != 或>操作符,引擎将放弃使用索引而进行全表扫描。
4. 避免在 where 子句中使用or 来连接条件
5. 慎用in 和 not, 可以用 exists 代替 in
6. 慎用 like 'XXX%',要提高效率,可以全文检索。
7. 应尽量避免在 where 子句中对字段进行表达式操作,如:
select id from t where num/2=100
应改为select id from t where num=100*2
8. 避免在where子句中对字段进行函数操作
select id from t where substring(name,1,3)='abc' 
改为:
select id from t where name like 'abc%'
9. 在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。(索引的最左前缀原则) 
10. 并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
11. 索引不是越多越好,索引可以提高select 的效率,同时也降低 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引。
12. 任何地方都不要使用 select * from t ,用具体的字段列表代替“*”
13. 避免频繁创建和删除临时表,以减少系统表资源的消耗。
14. 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
15. 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
数据库中的数据在未进行分库分表的情况下,随着时间和业务的发展,库中的表会越来越多,表中的数据量也会越来越大,相应地,数据操作,增删改查的开销也会越来越大
16. 读写分离。通过数据库配置设置, mysql复制时,产生了多个数据副本(备库),为减少服务器压力,备库用于处理读操作,主库可同时处理读写。备库的复制是异步的,无法实时同步,读写分离的主要难点也在于备库上的脏数据。通常如果使用备库进行读,一般对数据的实时性要求不能太高。
17. 分库、分表。
分库:当数据库中的表太多,可以考虑将表分到不同的数据库
分表
水平分表:将一些列分到另一张表
垂直分表:将历史信息分到另一张表中,很久之前的记录少有查询
18. 利用缓存存储经常被查询的数据。利用redis、memcache
17. char和varchar的区别?
区别一,定长和变长
char 表示定长,长度固定,varchar表示变长,即长度可变。当所插入的字符串超出它们的长度时,视情况来处理,如果是严格模式,则会拒绝插入并提示错误信息,如果是宽松模式,则会截取然后插入。如果插入的字符串长度小于定义长度时,则会以不同的方式来处理,如char(10),表示存储的是10个字符,无论你插入的是多少,都是10个,如果少于10个,则用空格填满。而varchar(10),小于10个的话,则插入多少个字符就存多少个。
varchar怎么知道所存储字符串的长度呢?实际上,对于varchar字段来说,需要使用一个(如果字符串长度小于255)或两个字节(长度大于255)来存储字符串的长度。但是因为他需要有一个prefix来表示他具体bytes数是多少(因为varchar是变长的,没有这个长度值他不知道如何读取数据)。

区别之二,存储的容量不同
对 char 来说,最多能存放的字符个数 255,和编码无关。
而 varchar 呢,最多能存放 65532 个字符。VARCHAR 的最大有效长度由最大行大小和使用的字符集确定。整体最大长度是 65,532字节
18. 简述MySQL的执行计划?
EXPLAIN命令是查看优化器如何决定执行查询的主要方法。可以帮助我们深入了解MySQL的基于开销的优化器,还可以获得很多可能被优化器考虑到的访问策略的细节,以及当运行SQL语句时哪种策略预计会被优化器采用。
http://www.cnblogs.com/clsn/p/8087501.html#auto_id_20
19. 在对name做了唯一索引前提下,简述limit 1 作用
select * from tb where name = ‘Oldboy-Wupeiqi’ 
select * from tb where name = ‘Oldboy-Wupeiqi’ 
是这样的的,用where条件过滤出符合条件的数据的同时,进行计数,比如limit 1,那么在where过滤出第1条数据后,他就会直接把结果select出来返回给你,整个过程就结束了。
20. 1000w条数据,使用limit offset 分页时,为什么越往后翻越慢?如何解决?
答案一:
先查主键,在分页。
select * from tb where id in (
select id from tb where limit 10 offset 30
)
答案二:
按照也无需求是否可以设置只让用户看200页

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

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

如果用户自己修改页码,也可能导致慢;此时对url种的页码进行加密(rest framework )
21. 什么是索引合并?
1、索引合并是把几个索引的范围扫描合并成一个索引。
2、索引合并的时候,会对索引进行并集,交集或者先交集再并集操作,以便合并成一个索引。
3、这些需要合并的索引只能是一个表的。不能对多表进行索引合并。

简单的说,索引合并,让一条sql可以使用多个索引。对这些索引取交集,并集,或者先取交集再取并集。从而减少从数据表中取数据的次数,提高查询效率。
22. 什么是覆盖索引?

http://www.cnblogs.com/chenpingzhao/p/4776981.html
23. 简述数据库读写分离?
读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
https://blog.csdn.net/xybelieve1990/article/details/50830908
24. 简述数据库分库分表?(水平、垂直)
见23问题链接
https://blog.csdn.net/xlgen157387/article/details/53976153
25. redis和memcached比较?
使用redis有哪些好处? 
(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1) 
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash 
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行 
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

redis相比memcached有哪些优势?   
(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 
(2) redis的速度比memcached快很多 (3) redis可以持久化其数据

Memcache与Redis的区别都有哪些? 
1)、存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,这样能保证数据的持久性。 
2)、数据支持类型 Memcache对数据类型支持相对简单。 Redis有复杂的数据类型。 
3)、使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。 Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

redis与 memcached相比,redis支持key-value数据类型,同时支持list、set、hash等数据结构的存储。
redis支持数据的备份,即master-slave模式的数据备份。
redis支持数据的持久化。
redis在很多方面支持数据库的特性,可以这样说他就是一个数据库系统,而memcached只是简单地K/V缓存。
它们在性能方面差别不是很大,读取方面尤其是针对批量读取性能方面memcached占据优势。当然redis也有他的优点,如持久性、支持更多的数据结构。
所以在选择方面如果有持久方面的需求或对数据类型和处理有要求的应该选择redis。
如果简单的key/value 存储应该选择memcached。
26. redis中数据库默认是多少个db 及作用?
Redis默认支持16个数据库(可以通过配置文件支持更多,无上限),可以通过配置databases来修改这一数字。客户端与Redis建立连接后会自动选择0号数据库,不过可以随时使用SELECT命令更换数据库
Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。
27. python操作redis的模块?

https://www.cnblogs.com/Eva-J/p/5152841.html
28. 如果redis中的某个列表中的数据量非常大,如果实现循环显示每一个值?
通过scan_iter分片取,减少内存压力
scan_iter(match=None, count=None)增量式迭代获取redis里匹配的的值
# match,匹配指定key
# count,每次分片最少获取个数
r = redis.Redis(connection_pool=pool)
for key in r.scan_iter(match='PREFIX_*', count=100000):
print(key)
29. redis如何实现主从复制?以及数据同步机制?

https://blog.csdn.net/zhangguanghui002/article/details/78524533
30. redis中的sentinel的作用?
帮助我们自动在主从之间进行切换
检测主从中 主是否挂掉,且超过一半的sentinel检测到挂了之后才进行进行切换。
如果主修复好了,再次启动时候,会变成从。

启动主redis:
redis-server /etc/redis-6379.conf 启动主redis
redis-server /etc/redis-6380.conf 启动从redis

在linux中:
找到 /etc/redis-sentinel-8001.conf 配置文件,在内部:
- 哨兵的端口 port = 8001
- 主redis的IP,哨兵个数的一半/1

找到 /etc/redis-sentinel-8002.conf 配置文件,在内部:
- 哨兵的端口 port = 8002
- 主redis的IP, 1 

启动两个哨兵 
31. 如何实现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 实例中。
32. redis中默认有多少个哈希槽?

16384
33. 简述redis的有哪几种持久化策略及比较?
RDB:每隔一段时间对redis进行一次持久化。
- 缺点:数据不完整
- 优点:速度快
AOF:把所有命令保存起来,如果想到重新生成到redis,那么就要把命令重新执行一次。
- 缺点:速度慢,文件比较大
- 优点:数据完整
34. 列举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(驱逐):禁止驱逐数据
35. 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(驱逐):禁止驱逐数据
36. 写代码,基于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
37. 如何基于redis实现消息队列?
不要使用redis去做消息队列,这不是redis的设计目标。
但实在太多人使用redis去做去消息队列,redis的作者看不下去,另外基于redis的核心代码,另外实现了一个消息队列disque:https://github.com/antirez/disque
38. 如何基于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)
39. 什么是codis及作用?

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

是 Twtter 开源的一个 Redis 和 Memcache 代理服务器,主要用于管理 Redis 和 Memcached 集群,减少与Cache 服务器直接连接的数量。
41. 写代码实现redis事务操作。
import redis

pool = redis.ConnectionPool(host='10.211.55.4', port=6379)

conn = redis.Redis(connection_pool=pool)

# pipe = r.pipeline(transaction=False)
pipe = conn.pipeline(transaction=True)
# 开始事务
pipe.multi()

pipe.set('name', 'bendere')
pipe.set('role', 'sb')

# 提交
pipe.execute()

注意:咨询是否当前分布式redis是否支持事务
42. redis中的watch的命令的作用?
在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()
- 数据库的锁 
43. 基于redis如何实现商城商品数量计数器?
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们
称之为“user_scores”,我们只需要像下面一样执行即可:
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到
44. 简述redis分布式锁和redlock的实现机制。
在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段。 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可以获得更好的可靠性。 这篇文章的目的就是尝试提出一种官方权威的用Redis实现分布式锁管理器的算法,我们把这个算法称为RedLock。
https://www.cnblogs.com/ironPhoenix/p/6048467.html
https://blog.csdn.net/junli_chen/article/details/79228282
45. 什么是一致性哈希?Python中是否有相应模块?

Python模块--hash_ring,即Python中的一致性hash
46. 如何高效的找到redis中所有以aaa开头的key?
redis 有一个keys命令。
语法:KEYS pattern
说明:返回与指定模式相匹配的所用的keys。
该命令所支持的匹配模式如下:
(1)?:用于匹配单个字符。例如,h?llo可以匹配hello、hallo和hxllo等;
(2)*:用于匹配零个或者多个字符。例如,h*llo可以匹配hllo和heeeello等;
(3)[]:可以用来指定模式的选择区间。例如h[ae]llo可以匹配hello和hallo,但是不能匹配hillo。
同时,可以使用“/”符号来转义特殊的字符

猜你喜欢

转载自www.cnblogs.com/andy0816/p/12228500.html