Python 经典面试题汇总之数据库篇
数据库和缓存
1.列举常见的关系型数据库和非关系型都有那些?
关系型数据库(需要有表结构)
mysql、oracle、splserver、postgresql、db2、sybase 非关系型数据库(是以key-value存储的,没有表结构)(NoSQL)
MongoDB
MongoDB 是一个高性能,开源,无模式的文档型数据库,开发语言是C++。它在许多场景下可用于替代传统的关系型数据库或键/值存储方式。
Redis
Redis 是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
2.MySQL常见数据库引擎及比较
InnoDB
支持事务
支持外键
支持表锁、行锁(for update)
表锁:select * from tb for update
行锁:select id,name from tb where id=2 for update myisam
查询速度快
全文索引
支持表锁
表锁:select * from tb for update
3.简述数据库三大范式
# 数据库的三大特性:
'实体':表
'属性':表中的数据(字段)
'关系':表与表之间的关系
----------------------------------------------------
# 数据库设计三大范式:
1:确保每列保持原子性(即数据库表中的所有字段值是不可分解的原子值)
2:确保表中的每列都是和主键相关(表中只能保存一种数据,不可以把多种数据保存在同一张表中)--->完全属于当前表的数据
3:确保每列都和主键直接相关,而不是间接相关(在一个数据库表中保存的数据只能与主键相关)----> 消除传递依赖(间接)
比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。
而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。
# 数据库五大约束'
1.primary KEY:设置主键约束;
2.UNIQUE:设置唯一性约束,不能有重复值;
3.CHECK:检查约束
4.NOT NULL:设置非空约束,该字段不能为空;
5.FOREIGN key:设置外键约束。
4、什么是事务?MySQL如何支持事务?
事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。 一般来说,事务是必须满足4个特性(ACID): Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)。
1、原子性:事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
2、一致性:事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
3、隔离性:当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
4、持久性:持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
最重要的是1,2两个特性。
关闭自动提交:SET AUTOCOMMIT=0; # 此后需要手动提交事务,应用COMMIT或ROLLBACK提交事务或回滚事务
开启自动提交:SET AUTOCOMMIT=1;
如果执行语句:START TRANSACTION(开始一个事务); 那么不论有无设置自动提交,均需要手动提交或回滚。
事务的周期由用户在命令提示符中输入START TRANSACTION指令开始,直至用户输入COMMIT结束。
5.简述数据库设计中一对多和多对多的应用场景?
FK(一对多)
下拉框里面的数据就需要用FK关联另一张表 M2M(多对多)
多选的下拉框,或者checkbox
6.常见SQL
group by 分组对聚合的条件进行筛选需要通过havhing SQL的left join 、right join、inner join之间的区别
left join (左连接) 返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右连接) 返回包括右表中的所有记录1和左表中联结字段相等的记录
inner join(内连接)只返回两个表中联结字段相等的行
7.简述触发器、函数、视图、存储过程
触发器:
对数据库某张表的增加、删除,修改前后定义一些操作。 函数:(触发函数是通过select)
聚合函数:max/sum/min/avg
时间格式化:date_format
字符串拼接:concat 存储过程:
将SQL语句保存到数据库中,并命名,以后在代码调用时,直接调用名称即可
参数类型:
in 只将参数传进去
out 只拿结果
inout 既可以传,可以取 函数与存储过程区别:
本质上没区别。只是函数只能返回一个变量的限制,而存储过程可以返回多个。函数是可以嵌入在sql中使用的,可以在select中调用,而存储过程不可以,它需要执行语句call来调用。 视图:
视图是一个虚拟表,不是真实存在的(一般只能查,不能改)
8.MySQL索引种类
单列
普通索引:加速查找
唯一索引:加速查找 + 约束:不能重复(只能有一个空,不然就重复了)
主键(primay key):加速查找 + 约束:不能重复 + 不能为空
多列
联合索引(多个列创建索引)-----> 相当于单列的普通索引
联合唯一索引 -----> 相当于单列的唯一索引
ps:联合索引的特点:遵循最左前缀的规则
其他词语:
·· - 索引合并,利用多个单例索引查询;(例如在数据库查用户名和密码,分别给用户名和密码建立索引)
- 覆盖索引,在索引表中就能将想要的数据查询到;
9.索引在什么情况下遵循最左前缀的规则?
联合索引
10.主键和外键的区别
主键是能确定一条记录的唯一标示。例如,身份证证号 外键:用于与另一张表的关联,是能确定另一张表记录的字段,用于保持数据的一致性
主键 | 外键 | |
定义 | 唯一标识一条记录,不能有重复的,不允许为空 | 表的外键是另一张表的主键,外键可以有重复的,可以为空 |
作用 | 用来保证数据完整性 | 用来与其他表建立联系的 |
个数 | 主键只能有一个 | 一个表可以有多个外键 |
11.MySQL常见的函数
聚合函数
max/min/sum/avg 时间格式化
date_format 字符串拼接
concat(当拼接了null,则返回null) 截取字符串
substring 返回字节个数
length
12.列举 创建索引但是无法命中索引的8种情况
1. like '%xx'
select * from tb1 where name like '%cn';
2. 使用函数
select * from tb1 where reverse(name) = 'wupeiqi';
3. or
select * from tb1 where nid = 1 or email = 'seven@live.com';
特别的:当or条件中有未建立索引的列才失效,以下会走索引
select * from tb1 where nid = 1 or name = 'seven';
select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'
4. 类型不一致
如果列是字符串类型,传入条件是必须用引号引起来,不然...
select * from tb1 where name = 999;
5. !=
select * from tb1 where name != 'alex'
特别的:如果是主键,则还是会走索引
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.如何开启慢日志查询?
修改配置文件
slow_query_log = OFF 是否开启慢日志记录
long_query_time = 2 时间限制,超过此时间,则记录
slow_query_log_file = /usr/slow.log 日志文件
log_queries_not_using_indexes = OFF 为使用索引的搜索是否记录 下面是开启
slow_query_log = ON
long_query_time = 2
log_queries_not_using_indexes = OFF
log_queries_not_using_indexes = ON 注:查看当前配置信息:
show variables like '%query%'
修改当前配置:
set global 变量名 = 值
14.数据库导入导出命令
备份所有数据库:
mysqldump –u 用户名 –p –h 主机名 --all-databases > 备份文件名.sql
备份整个或多个数据库:
mysqldump –u 用户名 –p –h 主机名 --databases db1 db2 db3 … > 备份文件名.sql
备份某个数据库的某些表:
mysqldump –u 用户名 –p –h 主机名 数据库名 表1 表2 表3… > 备份文件名.sql
通过mysqldump,如果使用了"--all-databases"或"--databases"选项,则在备份文件中包含CREATE DATABASE和USE语句,故并不需要指定一个数据库名去恢复备份文件。
mysql –u 用户名 –p < 备份文件.sql
通过mysqldump,如果没有使用"--databases"选项,则备份文件中不包含CREATE DATABASE和USE语句,那么在恢复的时候必须指定数据库。
mysql –u 用户名 –p 数据库名 < 备份文件.sql
15.数据库优化方案
可以从数据库服务器部署、表设计、表查询等方面考虑。 1、创建数据表时把固定长度的放在前面
2、将固定数据放入内存: 例如:choice字段 (django中有用到,数字1、2、3…… 对应相应内容)
3、char 和 varchar 的区别(char可变, varchar不可变 )
4、联合索引遵循最左前缀(从最左侧开始检索)
5、避免使用 select *
6、读写分离
- 实现:两台服务器同步数据
- 利用数据库的主从分离:主,用于增加、删除、更新;从,用于查询;
7、分库
- 当数据库中的表太多,将某些表分到不同的数据库,例如:1W张表时
- 代价:连表查询
8、分表
- 水平分表:将某些列拆分到另外一张表,例如:博客+博客详情
- 垂直分表:讲些历史信息分到另外一张表中,例如:支付宝账单 9、加缓存
- 利用redis、memcache (常用数据放到缓存里,提高取数据速度) 10、如果只想获取一条数据
- select xxx from tb where name='alex' limit 1;
16.char和varchar的区别
char可变,varchar不可变 。
17.简述MySQL的执行计划
查看有没有命中索引,让数据库帮看看运行速度快不快
explain select * from table;
当type为all时,是为全表索引。
18.在对name做了唯一索引前提下,简述以下区别:
select * from tb where name = ‘Oldboy-Wupeiqi’
select * from tb where name = ‘Oldboy-Wupeiqi’ limit 1
没做唯一索引的话,前者查询会全表扫描,效率低些;
limit 1,只要找到对应一条数据,就不继续往下扫描.
然而 name 字段添加唯一索引了,加不加limit 1,意义都不大。
19.1000w条数据,使用limit offset 分页时,为什么越往后翻越慢?如何解决?
答案一:
先查主键,在分页。
select * from tb where id in (
select id from tb where limit 10 offset 30
); 答案二:
记录当前页ID最大值和最小值
在翻页时,根据条件先进行筛选(子查询);筛选完毕之后,再根据limit offset 查询。 select * from (select * from tb where id > 1000000) limit 10 offset 0; 如果用户自己修改页码,也可能导致慢;此时对url种的页码进行加密(rest framework )
20.什么是索引合并?
简单地说,对同一张表的一条sql可以使用多个索引,对这些索引取交集、并集,从而减少从表中取数据的次数,提高查询效率。
21.什么是覆盖索引?
这个概念很重要!
一个索引涵盖了所有需要查询和返回字段的值,称为"覆盖索引"。
只需扫描索引而无须回表,也就是说只需要通过索引就可以返回查询数据,而不必先查到索引之后再去查询数据。性能不言而喻,速度非常快,很强大!!
在 Explain 的时候,输出的 Extra 信息中如果有 "Using Index" ,就表示这条查询使用了覆盖索引。
22.简述数据库读写分离
- 前提:主备两台服务器同步数据
- 利用数据库的主从分离:主,用于增加、删除、更新;从,用于查询;
例:Django数据库读写分离
步骤一:写配置文件
class Router1:
# 指定到某个数据库【读】数据
def db_for_read(self, model, **hints):
"""
Attempts to read auth models go to auth_db.
"""
if model._meta.model_name == 'usertype':
return 'db1'
else:
return 'default'
# 指定到某个数据库【写】数据
def db_for_write(self, model, **hints):
"""
Attempts to write auth models go to auth_db.
"""
return 'default' 步骤二:配置settings
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'db1': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
DATABASE_ROUTERS = ['db_router.Router1',] 步骤三:视图里用 using 方式可以指定读写数据库
from django.shortcuts import render,HttpResponse
from app01 import models def index(request):
models.UserType.objects.using('db1').create(title='普通用户')
# 手动指定去某个数据库取数据
result = models.UserType.objects.all().using('db1')
print(result)
return HttpResponse('...')
23.简述数据库分库,分表(水平、垂直)?
1、分库
当数据库中的表太多,将某些表分到不同数据库,例如:1W张表时
代价:连表查询跨数据库,代码变多。
2、分表(提高查询性能)
水平分表:表的记录行数庞大,将表按记录行数分成n份,每张表就小了很多。这些表结构一致。
垂直分表:将表的一些内容很长的列拆出来独立成另一张表。
24.redis和memcached比较?
区别:
1:redis不仅支持简单的key_value类型,还支持字符串,HASH,列表,集合。
2:内存使用效率对比:使用简单的key-value存储的话,Memcached的内存利用率更高;
而如果Redis采用HASH结构来做key-value存储,由于其组合式的压缩,Redis的内存利用率更高。
3:性能对比:由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时性能更高;
而在100k以上的数据中,Memcached性能更高。
4:Redis虽然是基于内存的存储系统,但是它本身是支持内存数据的持久化的;而memcached是不支持数据持久化操作的。
5:集群管理不同,Memcached本身并不支持分布式,因此只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。
25.redis中数据库默认是多少个db 及作用?
Redis默认支持16个数据库,可以通过配置databases来修改这一数字。客户端与Redis建立连接后会自动选择0号数据库,不过可以随时使用SELECT命令更换数据库。 Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。
26.python操作redis的模块
连接
- 直接连接:
import redis
r = redis.Redis(host='10.211.55.4', port=6379)
r.set('foo', 'Bar') # 这里的方法与Redis命令类似
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_iter(key, count=3):
start = 0
while True:
result = conn.lrange(key, start, start+count-1)
start += count
if not result:
break
for item in result:
yield item
# 调用
for val in list_iter('num_list'):
print(val)
场景:投票系统
28.redis如何实现主从复制?以及数据同步机制?
优势:
- 高可用
- 分担主压力
注意:
- slave设置只读 从的配置文件添加以下记录,即可:
slaveof 1.1.1.1 3306
29.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 启动两个哨兵
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中默认有多少个哈希槽?
16384
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
36.如何基于redis实现消息队列?
# 通过"发布订阅"模式(P-S)实现消息队列。只要有任务就给所有订阅者每人一份。
# 发布者发布消息到频道了,频道就是一个消息队列。
# 发布者:
import redis
conn = redis.Redis(host='192.168.1.99', port=6379)
conn.publish('104.9MH', "hahahahahaha")
# 订阅者:
import redis
conn = redis.Redis(host='192.168.1.99', port=6379)
pub = conn.pubsub()
pub.subscribe('104.9MH')
while True:
msg= pub.parse_response()
print(msg)
对了,redis 做消息队列不合适。
业务上避免过度复用一个redis,用它做缓存、做计算,还做任务队列,压力太大,不好。
37.什么是codis及作用?
Codis是一个分布式Redis解决方案, 对于上层的应用来说, 连接到Codis Proxy和连接原生的Redis Server没有明显的区别,
上层应用可以像使用单机的Redis一样使用, Codis底层会处理请求的转发, 不停机的数据迁移等工作,
所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的Redis服务。
38.什么是twemproxy及作用?
是 Twtter 开源的一个 Redis 和 Memcache 代理服务器,主要用于管理 Redis 和 Memcached 集群,减少与Cache 服务器直接连接的数量。
39.写代码实现redis事务操作
import redis pool = redis.ConnectionPool(host='192.168.1.99', port=6379)
conn = redis.Redis(connection_pool=pool) # 开始事务
pipe = conn.pipeline(transaction=True)
pipe.multi()
pipe.set('name', 'bendere')
pipe.set('role', 'sb')
# 提交
pipe.execute()
40.redis中的watch的命令的作用?
在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。
假设我们通过WATCH命令在事务执行之前【监视】了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,
EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务执行失败。 面试题:你如何控制剩余的数量不会出问题?
方式一:- 通过redis的watch实现
import redis
conn = redis.Redis(host='192.168.1.99', 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()
方式二 - 数据库的锁
41.谈谈数据库锁
以MySQL为例。InnoDB 存储引擎实现了行锁与表锁。行锁可以以行为单位对数据集进行锁定。表锁可以以表为单位对数据集进行锁定。
行锁、表锁又可分为两种锁:共享锁与排他锁。
- 共享锁:允许一个事务读取一行,阻止其他事务获得相同数据集的排他锁。但允许其他事务获取共享锁。
- 排他锁:允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享与排他锁。但是可以对获取了排他锁的数据集进行单纯的查询访问。
对于 Update、Delete、insert 语句,InnoDB 会自动给涉及的数据集隐式的加上排他锁。对于 select 语句 InnoDB 不会加任何锁。可以通过显式的方式获取共享锁或者排他锁。
- 共享锁:
select * from table where ... lock in share mode
- 排他锁:
select * from table where ... for update
42.基于redis如何实现商城商品数量计数器?
import redis conn = redis.Redis(host='192.168.1.99', port=6379)
conn.set('count',1000)
with conn.pipeline(transaction=True) 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实现分布式锁管理器的算法,我们把这个算法称为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},
]
) # 加锁
my_lock = dlm.lock("my_resource_name", 10000)
if my_lock:
# 进行操作
# 解锁
dlm.unlock(my_lock)
else:
print('获取锁失败')
redis分布式锁?
# 不是单机操作,又多了一/多台机器
# redis内部是单进程、单线程,是数据安全的(只有自己的线程在操作数据)
----------------------------------------------------------------
A、B、C,三个实例(主)
1、来了一个'隔壁老王'要操作,且不想让别人操作,加锁;
加锁:'隔壁老王'自己生成一个随机字符串,设置到A、B、C里(xxx=666)
2、来了一个'邻居老李'要操作A、B、C,一读发现里面有字符串,擦,被加锁了,不能操作了,等着吧~
3、'隔壁老王'解决完问题,不用锁了,把A、B、C里的key:'xxx'删掉;完成解锁
4、'邻居老李'现在可以访问,可以加锁了
# 问题:
1、如果'隔壁老王'加锁后突然挂了,就没人解锁,就死锁了,其他人干看着没法用咋办?
2、如果'隔壁老王'去给A、B、C加锁的过程中,刚加到A,'邻居老李'就去操作C了,加锁成功or失败?
3、如果'隔壁老王'去给A、B、C加锁时,C突然挂了,这次加锁是成功还是失败?
4、如果'隔壁老王'去给A、B、C加锁时,超时时间为5秒,加一个锁耗时3秒,此次加锁能成功吗?
# 解决
1、安全起见,让'隔壁老王'加锁时设置超时时间,超时的话就会自动解锁(删除key:'xxx')
2、加锁程度达到(1/2)+1个就表示加锁成功,即使没有给全部实例加锁;
3、加锁程度达到(1/2)+1个就表示加锁成功,即使没有给全部实例加锁;
4、不能成功,锁还没加完就过期,没有意义了,应该合理设置过期时间
44.什么是一致性哈希?Python中是否有相应模块?
一致性hash算法(DHT)可以通过减少影响范围的方式,解决增减服务器导致的数据散列问题,从而解决了分布式环境下负载均衡问题;
如果存在热点数据,可以通过增添节点的方式,对热点区间进行划分,将压力分配至其他服务器,重新达到负载均衡的状态。
Python模块--hash_ring,即Python中的一致性hash
45.如何高效的找到redis中所有以"w3c"开头的key?
redis 有一个keys命令。
# 语法:KEYS pattern
# 说明:返回与指定模式相匹配的所用的keys。
该命令所支持的匹配模式如下:
1、"?":用于匹配单个字符。例如,h?llo可以匹配hello、hallo和hxllo等;
2、"*":用于匹配零个或者多个字符。例如,h*llo可以匹配hllo和heeeello等;
2、"[]":可以用来指定模式的选择区间。例如h[ae]llo可以匹配hello和hallo,但是不能匹配hillo。同时,可以使用“/”符号来转义特殊的字符
# 例子:
redis 127.0.0.1:6379> KEYS w3c*
1) "w3c1"
2) "w3c123"
3) "w3c12"
# 注意
KEYS 的速度非常快,但如果数据太大,内存可能会崩掉,
如果需要从一个数据集中查找特定的key,最好还是用Redis的集合结构(set)来代替。
Python 经典面试题汇总之数据库篇的更多相关文章
- Python 经典面试题汇总之基础篇
基础篇 1:为什么学习Python 公司建议使用Python,然后自己通过百度和向有学过Python的同学了解了Python.Python这门语言,入门比较简单,它简单易学,生态圈比较强大,涉及的地方 ...
- Python 经典面试题汇总之网络篇
网络篇 1.简述 OSI 七层协议 物理层:定义物理设备标准,如网线的接口类型.光纤的接口类型.各种传输介质. 数据链路层:定义如何传输格式化数据,以及如何访问物理介质. 网络层:定义逻辑网络地址. ...
- Python 经典面试题汇总之框架篇
前端和框架 1.谈谈你对http协议的认识 浏览器本质,socket客户端遵循Http协议 HTTP协议本质:通过\r\n分割的规范,请求响应之后断开链接 ==> 短连接.无状态 具体: Htt ...
- php面试题汇总二(基础篇附答案)
介绍一些php常见面试题及答案,都是平时面试的时候经常会遇到的,小伙伴们仔细了解下吧.接着上一篇php面试题汇总一(基础篇附答案) 1.在PHP中,当前脚本的名称(不包括路径和查询字符串)记录在预定义 ...
- python面试笔试题汇总
Python面试攻略(嗨谈篇) 110道python面试笔试题汇总,你能答对几道? Python 面试问答 Top 25 2018 年最常见的 Python 面试题 & 答案
- Python自动化测试面试题-用例设计篇
目录 Python自动化测试面试题-经验篇 Python自动化测试面试题-用例设计篇 Python自动化测试面试题-Linux篇 Python自动化测试面试题-MySQL篇 Python自动化测试面试 ...
- C/C++ 经典面试题汇总
面试题1:变量的声明和定义有什么区别 ? 为变量分配地址和存储空间的称为定义,不分配地址的称为声明.一个变量可以在多个地方声明,但是只在一个地方定义.加入extern修饰的是变量的声明,说明此变量将在 ...
- 110道python面试笔试题汇总,你能答对几道?
数据分析系列教程插播一篇面试题教程,最近公众号新来了不少朋友,这几天不少粉丝留言说问我有没有python面试题,其实之前分享过一些面试题,今天统一再给大家分享一遍,也希望能帮助此时仍在找工作的同学,尽 ...
- php面试题汇总一(基础篇附答案)
一份不错的php面试题,附答案,有准备换工作的同学可以参考一下. 1:使用五种以上方式获取一个文件的扩展名要求:dir/upload.image.jpg,找出 .jpg 或者 jpg ,必须使用PHP ...
随机推荐
- SAP MM 标准采购组织的分配对于寄售采购订单收货的影响
SAP MM 标准采购组织的分配对于寄售采购订单收货的影响 PO 4100004022 是一个寄售的采购订单, 采购组织是CSAS, 工厂代码SZSP.采购信息记录也是有的, MIGO试图对该采购订单 ...
- gitbook 入门教程之环境要求
gitbook 是基于 node.js 的命令行工具,首先需要安装并配置好 node.js 环境,然后才能安装gitbook 相关工具. 由于安装工具全部都是国外网站,因此速度可能会很慢,也可能需要F ...
- Jmeter接口测试-断言
JMeter接口测试-断言 对应qq群号:616961231 一个http请求发出,怎么判断执行的是否正确呢? 通过检查服务器响应数据,是否符合预期的数据, 如果是,任务成功, 如果否, 任务失败 ...
- Storm入门(十四)Trident API Overview
The core data model in Trident is the "Stream", processed as a series of batches. A stream ...
- 【死磕 Spring】----- IOC 之 注册 BeanDefinition
原文出自:http://cmsblogs.com 获取 Document 对象后,会根据该对象和 Resource 资源对象调用 registerBeanDefinitions() 方法,开始注册 B ...
- 关于.Net mvc 项目在本地vs运行响应时间过长无法访问时,解决方法!
最近可能是刚升级了电脑使用了window10操作系统,总是遇到了一些以前没有遇到过的事情! 今早来到公司本来准备写bug的,但是当我打开vs运行的时候发现今天的电脑响应的时间明显的要比之前打开网页调试 ...
- 了解一下RabbitMQ
RabbitMQ概述 RabbitMQ是遵从AMQP协议的 通信协议都设计到报文交互,换句话说RabbitMQ就是AMQP协议的Erlang的实现. AMQP说到底还是一个通信协议从low-level ...
- Python学习笔记【第一篇】:认识python和基础知识
我的笔记里的python代码运行环境都是在pycharm软件中运行,所以不去记录如何配置环境变量呀什么的. python种类 Cpython: Python的官方版本,使用C语言实现,使用最为广泛,C ...
- 和逛微博、刷朋友圈一样玩转 GitHub
自打毕业之后,可以说每天打开 Github 或Email 看有没有 watch 项目的消息或者自己项目的 issue,然后在Explore 看看社区内项目的走势,紧接着开始写代码搬砖的工作,偶尔也会关 ...
- [JLOI2016] 成绩比较
推石子 首先设\(d[i]=\sum_{t=1}^{U[i]}t^{n-R[i]}(U[i]-t)^{R[i]-1}\),即第\(i\)门课程分数的合法分布方案数: 然后设\(f[i,j]\)表示前\ ...