python
内存管理
缓存
正则和
哈希
高并发
超卖
跨域问题
设计模式
单例模式
class Singleton:
def __init__(self):
pass
def __new__(cls,*args,**kwargs):
if not hasattr(Singleton,'_instance'):
Singleton._instance = object.__new__(cls)
return Singleton._instance
生产者消费者模式
迭代器
迭代器是访问可迭代对象的一种方式
可以记住遍历的位置的对象
迭代器对象从第一个元素开始访问,直到所有的元素被访问结束
迭代器只能往前不能后退
迭代器有两个基本的方法:__iter()__
和__next__()
把一个类作为一个迭代器需要在类中实现两个方法:__iter__()
和__next__()
__iter__()
:返回一个特殊的迭代器对象,这个迭代器对象实现了__next__()
方法并通过Stopiteration异常标识迭代的完成,在__next__()
方法中我们可以社招在完成指定循环次数后触发Stopiteration异常来结束迭代
__next__()
:返回下一个迭代器对象
生成器
使用了yield的函数叫做生成器
生成器是一个返回迭代器的函数,只能用于迭代操作
再调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield值,并在下一次执行next()方法时从当前位置继续运行
调用一个生成器函数,返回的是一个迭代器对象
python2与python3的区别
python3中迭代器的方法是__next__()
python2中迭代器的方法是next()
mysql
联合索引
最左匹配
最左原则指的就是如果你的SQL语句中用到了联合索引中的最左边的索引。那么这条SQL语句就可以利用这个联合索引去进行匹配,当遇到范围查询(>,<,between,like)就会停止匹配
对a,b字段建立索引,where后的条件为
a = 1 and b = 2
执行
b = 2 and a = 1
也是可以匹配到索引的,因为Mysql有优化器会自动调整a,b的顺序与索引顺序一致
将区分度高的字段放在前面,区分度低的放在后面
SELECT * FROM table WHERE a > 1 and b = 2;
遇到这种范围查询的,给B建立索引,mysql有自动优化机制
SELECT * FROM `table` WHERE a = 1 ORDER BY b;
给a建立索引,这样b是有序的
SELECT * FROM `table` WHERE a > 1 ORDER BY b;
因为a的值是一个范围,所以b是无序的,所以不需要对a,b建立索引
脏读
读取到了未提交的数据(如果事务这时候回滚了,那么第二个事务就读到了脏数据)
原因可能有:
(1)有一个交叉的事务有新的commit,导致了数据的改变;
(2)一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit。
不可重复读
同一个事务中,对于同一数据,执行完全相同的select语句时可能看到不一样的结果。
幻读
当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行;
InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
幻读和不可重复读的区别
不可重复读
重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(一个事务多次读取同一范围内数据时,另外一个发生了事务发生insert操作并提交了)。数据变化。
幻读
重点在于新增或者删除:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(一个事务多次读取同一条数据时,另外一个发生了事务发生update,delete操作并提交了。insert操作一条新数据并不会影响刚才被读的那条数据。)。行数变化。
事务
事务就是被绑定在一起作为一个逻辑工作单元的SQL语句分组,如果任何一个语句操作失败那么整个操作就被失败,以后操作就会回滚到操作前状态,或者是上有个节点。为了确保要么执行,要么不执行,就可以使用事务。要将有组语句作为事务考虑,就需要通过ACID测试,即原子性,一致性,隔离性和持久性。
锁
当数据库有并发事务的时候,可能会产生数据的不一致,这个时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制
mysql事务的隔离级别
事务隔离级别是指在处理同一个数据的多个事务中,一个事务修改数据后,其他事务何时能够看到修改后的结果
串行化(Serializable):一个事务一个事务的执行
可重复读(Repeatable read)(mysql默认):无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响
读取已提交(Read committed):其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值
读取未提交(Read uncommitted):其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值
mysql的锁
共享锁:又叫读锁,当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。一起看房
排他锁:又叫死锁。当用户要进行数据的写入时,对数据加上排他锁。排他锁只能加一个,他和其他的排他锁,共享锁读相斥。只能他在房间
InnoDB实现了行级锁,页级锁,表级锁
InnoDB引擎的行锁是基于索引来完成
死锁
死锁是指两个或多个事务在同一资源上互相占用,并请求锁定对方的资源,从而导致恶行循环的现象
常见的解决死锁的方法:
- 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会
- 在同一个事务中,尽可能做到一次锁定锁所需要的所有资源,焦山死锁产生概率
- 对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁来减少死锁产生的概率
- 如果业务处理不好可以使用分布式事务锁或乐观锁
数据库的并发控制的任务是确保在多个事务中同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。
乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段
悲观锁
查询某条记录时,让数据库为该记录加锁,锁住记录后别人无法操作
悲观锁类似于我们在多线程资源竞争时添加的互斥锁
假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作,在查询数据的时候就把事务锁起来,直到提交事务
实现方式:数据库中的锁机制
悲观锁多云关于多写的场景下
乐观锁
乐观锁不是真实存在的锁
假定不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来
乐观锁常用于写比较少的情况下(多读场景)
在更新的时候判断此时的库存是否是之前查询出的库存,如果相同,表示没人修改,可以更新库存,否则表示别人抢过资源,不执行库存更新
B树
多路自平衡的搜索树
b树允许每个节点有更多的子节点
特点:
- 所有键值分布在整颗树中(索引值和具体data都在每个节点里)
- 任何一个关键字出现且只出现在一个节点中
- 搜索有可能在非叶子节点结束(最好情况O(1)就能找到数据)
- 在关键字全集内做一次查找,性能逼近二分查找
B树是专门为外部存储器设计的,如磁盘,对于读取和写入大块数据有良好的性能,所以一般用在文件系统及数据库中
传统的搜索平衡二叉树查询性能好,但是在数据非常大的时候就无能为力了,原因是数据量足够大时,内存不够用
空间局部性原理:如果一个存储器的某个位置被访问,那么将它附近的位置也会被访问
B树把每个节点给了一点的范围区,区间很多的情况下,搜索也就快了
B+树
B+树是B树的变体,也是一种多路搜索树,不同点
- 所有关键字存储在叶子节点出现,内部节点(非叶子节点并不存储真正的data)
- 为所有叶子节点增加了一个链指针
因为内节点并不存储data,所以一般B+树的叶节点和内节点大小不同,而B树的每个节点大小一般是相同的,为一页
B+树优化:顺序访问B+树
B+树内节点不存储数据,所有data存储在叶节点导致查询时间复杂度固定为logn,而B树查询时间复杂度不固定,与key在树中的位置有关
B+树叶节点两两相连可大大增加区间访问性,可使用在范围查询等
B树每个节点key和data在一起,则无法区间查找
B+树很好的利用局部性原理,
若我们访问节点 key为 50,则 key 为 55、60、62 的节点将来也可能被访问,我们可以利用磁盘预读原理提前将这些数据读入内存,减少了磁盘 IO 的次数
B+树更适合外部存储。由于内节点无 data 域,每个节点能索引的范围更大更精确
由于B树的节点都存了key和data,而B+树只有叶子节点存data,非叶子节点都只是索引值,没有实际的数据,这就时B+树在一次IO里面,能读出的索引值更多。从而减少查询时候需要的IO次数!
redis
为什么使用redis?
- 性能
需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样后面的请求就去缓存中读取,使得请求能够迅速响应 - 并发
在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常,这个时候就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库
使用redis有什么缺点
- 缓存和数据库双写一致性问题
- 缓存雪崩问题
- 缓存击穿问题
- 缓存的并发竞争问题
单线程的redis为什么这么快
- 纯内存操作
- 单线程操作,避免了频繁的上下文切换
- 采用了非阻塞I/O多路复用机制
I/O多路复用机制:1+
传统并发模型:每个I/O流都有一个新的进程管理
I/O多路复用:只有单个线程,通过跟踪每个I/O流的状态,来管理多个I/O流
redis-client在操作的时候,会产生具有不同事件类型的socker。在服务端,有一段I/O多路复用程序,将其置入队列之中,然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中
redsi的数据类型,以及每种数据类型的使用场景
String:做一些复杂的计数功能的缓存
Hash:存放的是结构化的对象
List:简单的消息队列的功能,利用lrange做基于redis的分页功能
set:全局去重功能
zset:
redis如何转存mysql的数据
redis的过期策略以及内存淘汰机制
分析:这个问题其实相当重要,到底redis有没用到家,这个问题就可以看出来。比如你redis只能存5G数据,可是你写了10G,那会删5G的数据。怎么删的,这个问题思考过么?还有,你的数据已经设置了过期时间,但是时间到了,内存占用率还是比较高,有思考过原因么?
回答: redis采用的是定期删除+惰性删除策略。
为什么不用定时删除策略? 定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.
定期删除+惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。 于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。 在redis.conf中有一行配置
maxmemory-policy volatile-lru
该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)
1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。
2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。
3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。
4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐
5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐 6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐 ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。
redis和数据库双写一致性问题
分析:一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。答这个问题,先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。
回答:《分布式之数据库和缓存双写一致性方案解析》给出了详细的分析,在这里简单的说一说。首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。
如何应对缓存穿透和缓存雪崩问题
分析:这两个问题,说句实在话,一般中小型传统软件企业,很难碰到这个问题。如果有大并发的项目,流量有几百万左右。这两个问题一定要深刻考虑。
回答:如下所示
缓存穿透,即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。
解决方案: (一)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试 (二)采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。 (三)提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
缓存雪崩,即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。
解决方案: (一)给缓存的失效时间,加上一个随机值,避免集体失效。 (二)使用互斥锁,但是该方案吞吐量明显下降了。 (三)双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点 - I 从缓存A读数据库,有则直接返回 - II A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。 - III 更新线程同时更新缓存A和缓存B。
如何解决redis的并发竞争key问题
分析:这个问题大致就是,同时有多个子系统去set一个key。这个时候要注意什么呢?大家思考过么。需要说明一下,博主提前百度了一下,发现答案基本都是推荐用redis事务机制。博主不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。
回答:如下所示
(1)如果对这个key操作,不要求顺序 这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。
(2)如果对这个key操作,要求顺序 假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC. 期望按照key1的value值按照 valueA–>valueB–>valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。假设时间戳如下
系统A key 1 {valueA 3:00}
系统B key 1 {valueB 3:05}
系统C key 1 {valueC 3:10}
那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。
其他方法,比如利用队列,将set方法变成串行访问也可以。总之,灵活变通。
网络
HTTPS
操作系统
面试题1
进程线程协程
进程间通信方式
select、poll、epoll之间的区别(搜狗面试)
linux常见调度算法
浅谈操作系统对内存的管理
Vue
Vue组件通讯有哪些方式
1、props 和 e m i t 。 父 组 件 向 子 组 件 传 递 数 据 是 通 过 p r o p s 传 递 的 , 子 组 件 传 递 给 父 组 件 是 通 过 emit。父组件向子组件传递数据是通过props传递的,子组件传递给父组件是通过 emit。父组件向子组件传递数据是通过props传递的,子组件传递给父组件是通过emit触发事件来做到的。
2、$parent 和 $children 获取单签组件的父组件和当前组件的子组件。
3、$attrs 和 l i s t e n e r s A − > B − > C 。 V u e 2.4 开 始 提 供 了 listeners A -> B -> C。Vue2.4开始提供了 listenersA−>B−>C。Vue2.4开始提供了attrs和$listeners来解决这个问题。
4、父组件中通过 provide 来提供变量,然后在子组件中通过 inject 来注入变量。(官方不推荐在实际业务中适用,但是写组件库时很常用。)
5、$refs 获取组件实例。
6、envetBus 兄弟组件数据传递,这种情况下可以使用事件总线的方式。
7、vuex 状态管理。
v-if 和 v-show 的区别
v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。
v-show 会被编译成指令,条件不满足时控制样式将此节点隐藏(display:none)
使用场景
v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景。
v-show 适用于需要非常频繁切换条件的场景。
vue-router 路由钩子函数是什么?执行顺序是什么?
路由钩子的执行流程,钩子函数种类有:全局守卫、路由守卫、组件守卫。
完整的导航解析流程:
1、导航被触发。
2、在失活的组件里调用 beforeRouterLeave 守卫。
3、调用全局的 beforeEach 守卫。
4、在重用的组件调用 beforeRouterUpdate 守卫(2.2+)。
5、在路由配置里面 beforeEnter。
6、解析异步路由组件。
7、在被激活的组件里调用 beforeRouterEnter。
8、调用全局的 beforeResolve 守卫(2.5+)。
9、导航被确认。
10、调用全局的 afterEach 钩子。
11、触发 DOM 更新。
12、调用 beforeRouterEnter 守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。
vuex 的个人理解
主要包括以下几个模块:
State:定义了应用状态的数据结构,可以在这里设置默认的初始化状态。
Getter:允许组件从Store中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步请求。
Module:允许将单一的 Store 拆分更多个 store 且同时保存在单一的状态树中。
算法
买卖股票
交错字符串
爬楼梯
实战
装饰器
闭包
深浅拷贝
斐波拉数列
等比数列求和
上下文管理器
python内存管理
GIL全局解释器锁
二叉树的任意排序
一个正整数数组,求不存在数组中的最小正整数
爬楼梯
celery里面有什么
linux常用命令
dockerfile打包
mysql的索引
redis的持久化
元类
django是什么架构
vue是什么架构
让你实现列表,你怎么实现
列表跟元组的区别
一个整型数组中,任意两个数,求和