计算机 ==>> 数据库
数据库
mysql
InnoDB
结构
page 16KB 2-200行记录
索引
索引使用B+树
锁
锁的种类一般分为乐观锁和悲观锁两种,InnoDB存储引擎中使用的就是悲观锁,而按照锁的粒度划分,也可以分成行锁和表锁。
标准的行级锁包括:
共享锁(读锁)——允许事务对一条行数据进行读取;
互斥锁(写锁)——允许事务对一条行数据进行删除或更新;
共享锁之间是兼容的,而互斥锁与其他任意锁都不兼容。
问题点
- 为什么要使用innoDB存储引擎?
答:InnoDB 锁粒度是行锁,而 MyISAM 是表锁;InnoDB 支持事务,MyISAM 不支持。现在一般都是选用InnoDB了,主要是MyISAM全表锁,读写串行问题,并发效率锁表,效率低MyISAM对于读写密集型应用一般是不会去选用的。
- InnoDB四范式?
答:
事务
mongodb
事务
MongoDB 4.0 引入的事务功能,支持多文档ACID特性
redis
命令和数据类型
1.字符串
1 | SET [key] [value] [EX seconds | PX milliseconds] NX|XX |
将键key的值设置为value,SET覆盖旧值(无视数据类型)
EX/PX设置过期时间(秒/毫秒),覆盖TTL
NX/XX检查后设置(不存在才设置/存在才设置)
1 | SETNX [key] [value] |
只在键key不存在的情况下,将键key的值设置为value
等效于 SET [key] [value] NX
1 | SETEX [key] [seconds] [value] |
将键 key 的值设置为 value , 并将键 key 的生存时间设置为 seconds 秒钟。
等效于 SET [Key] [value] EX [seconds]
1 | PSETEX [key] [milliseconds] [value] |
将键 key 的值设置为 value , 并将键 key 的生存时间设置为 milliseconds 秒钟。
等效 SET [Key] [value] PX [seconds]
1 | GET [key] |
返回与键 key 相关联的字符串值
如果键 key 的值并非字符串类型, 那么返回一个错误, 因为 GET 命令只能用于字符串值
1 | GETSET [key] [value] |
将键 key 的值设为 value , 并返回键 key 在被设置之前的旧值
1 | STRLEN [key] |
返回字符串值的长度
1 | APPEND [key] [value] |
如果键 key 已经存在并且它的值是一个字符串, APPEND 命令将把 value 追加到键 key 现有值的末尾。
如果 key 不存在, APPEND 就简单地将键 key 的值设为 value , 就像执行 SET key value 一样。
1 | SETRANGE [key] [offset] [value] |
从偏移量 offset 开始, 用 value 参数覆写(overwrite)键 key 储存的字符串值
那么原字符和偏移量之间的空白将用零字节(zerobytes, “\x00” )进行填充
1 | GETRANGE [key] [start] [end] |
返回键 key 储存的字符串值的指定部分, 字符串的截取范围由 start 和 end 两个偏移量决定 (包括 start 和 end 在内)
1 | INCR key |
为键 key 储存的数字值加上一;如果键 key 不存在, 那么它的值会先被初始化为 0 , 然后再执行 INCR 命令;如果键 key 储存的值不能被解释为数字, 那么 INCR 命令将返回一个错误。
1 | INCRBY [key] [increment] |
为键 key 储存的数字值加上增量 increment ;其它类似上面
1 | INCRBYFLOAT key increment |
为键 key 储存的值加上浮点数增量 increment;其它类似上面
1 | DECR key |
为键 key 储存的数字值减去一;其它类似上面
1 | DECRBY key decrement |
将键 key 储存的整数值减去减量 decrement;其它类似上面
1 | MSET [key] [value] [key value …] |
同时为多个键设置值
MSET 是一个原子性(atomic)操作, 所有给定键都会在同一时间内被设置, 不会出现某些键被设置了但是另一些键没有被设置的情况。
1 | MSETNX [key] [value] [key value …] |
当且仅当所有给定键都不存在时, 为所有给定键设置值
MSETNX 是一个原子性(atomic)操作, 所有给定键要么就全部都被设置, 要么就全部都不设置, 不可能出现第三种状态。
1 | MGET key [key …] |
返回给定的一个或多个字符串键的值
2.哈希表
1 | HSET [key] [field] [value] [field value …] |
将哈希表 key 中域 field 的值设置为 value;返回不存在且设置成功的数量
1 | HSETNX [key] [field] [value] |
当且仅当域 field 尚未存在于哈希表的情况下, 将它的值设置为 value
1 | HGET [key] [field] |
返回哈希表中给定域的值
1 | HEXISTS [key] [field] |
在给定域存在时返回 1 , 在给定域不存在时返回 0
1 | HDEL [key] [field] [field …] |
删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略
1 | HLEN [key] |
返回哈希表 key 中域的数量
1 | HSTRLEN [key] [field] |
返回哈希表 key 中, 与给定域 field 相关联的值的字符串长度(string length)
1 | HINCRBY [key] [field] [increment] |
为哈希表 key 中的域 field 的值加上增量 increment
1 | HINCRBYFLOAT [key] [field] [increment] |
为哈希表 key 中的域 field 加上浮点数增量 increment
1 | HMSET [key] [field] [field value …] |
同时将多个 field-value (域-值)对设置到哈希表 key 中
1 | HMGET [key] [field] [field …] |
返回哈希表 key 中,一个或多个给定域的值
1 | HKEYS [key] |
返回哈希表 key 中的所有域
1 | HVALS [key] |
返回哈希表 key 中所有域的值
1 | HGETALL [key] |
返回哈希表 key 中,所有的域和值;在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
3.列表
底层数据结构为双端链表
1 | LPUSH [key] [value] [value …] |
将一个或多个值 value 插入到列表 key 的表头;如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头
1 | LPUSHX [key] [value] |
将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表
1 | RPUSH [key] [value] [value …] |
将一个或多个值 value 插入到列表 key 的表尾(最右边)
1 | RPUSHX [key] [value] |
将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表
1 | LPOP [key] |
移除并返回列表 key 的头元素
1 | RPOP [key] |
移除并返回列表 key 的尾元素
1 | RPOPLPUSH [source] [destination] |
将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端;将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。
如果 source 和 destination 相同,则列表中的表尾元素被移动到表头,并返回该元素,可以把这种特殊情况视作列表的旋转(rotation)操作。
1 | LREM [key] [count] [value] |
根据参数 count 的值,移除列表中与参数 value 相等的元素。count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count ;count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值;count = 0 : 移除表中所有与 value 相等的值。
1 | LLEN [key] |
返回列表 key 的长度
1 | LINDEX [key] [index] |
返回列表 key 中,下标为 index 的元素
1 | LINSERT [key] [BEFORE|AFTER] [pivot] [value] |
将值 value 插入到列表 key 当中,位于值 pivot 之前或之后
1 | LSET [key] [index] [value] |
将列表 key 下标为 index 的元素的值设置为 value
1 | LRANGE [key] [start] [stop] |
返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定
1 | LTRIM [key] [start] [stop] |
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除
1 | BLPOP key [key …] timeout |
列表的阻塞式(blocking)弹出原语
1 | BRPOP key [key …] timeout |
列表的阻塞式(blocking)弹出原语
1 | BRPOPLPUSH source destination timeout |
当列表 source 为空时, BRPOPLPUSH 命令将阻塞连接,直到等待超时,或有另一个客户端对 source 执行 LPUSH key value [value …] 或 RPUSH key value [value …] 命令为止。 超时参数设为 0 表示阻塞时间可以无限期延长(block indefinitely) 。
4.集合
与Redis对外暴露的其它数据结构类似,set的底层实现,随着元素类型是否是整型以及添加的元素的数目多少,而有所变化。概括来讲,当set中添加的元素都是整型且元素数目较少时,set使用intset作为底层数据结构,否则,set使用dict作为底层数据结构。
1 | SADD [key] [member] [member …] |
将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
1 | SISMEMBER [key] [member] |
判断 member 元素是否集合 key 的成员。
1 | SPOP [key] |
移除并返回集合中的一个随机元素。
1 | SRANDMEMBER [key] [count] |
如果命令执行时,只提供了 key 参数,那么返回集合中的一个随机元素。
如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。
如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
1 | SREM [key] [member] [member …] |
移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
1 | SMOVE [source] [destination] [member] |
将 member 元素从 source 集合移动到 destination 集合。
1 | SCARD [key] |
返回集合 key 的基数(集合中元素的数量)
1 | SMEMBERS [key] |
返回集合 key 中的所有成员。
1 | SINTER [key] [key …] |
返回一个集合的全部成员,该集合是所有给定集合的交集。
1 | SINTERSTORE [destination] [key] [key …] |
集合的交集,并将结果保存到 destination 集合。如果 destination 集合已经存在,则将其覆盖。
1 | SUNION [key] [key …] |
返回一个集合的全部成员,该集合是所有给定集合的并集。
1 | SUNIONSTORE destination key [key …] |
集合的并集,并将结果保存到 destination 集合。如果 destination 集合已经存在,则将其覆盖。
1 | SDIFF [key] [key …] |
返回一个集合的全部成员,该集合是所有给定集合之间的差集。
1 | SDIFFSTORE destination key [key …] |
集合的差集,并将结果保存到 destination 集合。如果 destination 集合已经存在,则将其覆盖。
5.有序集合
使用跳跃表作为有序集合键的的底层实现
1 | ZADD [key] [score] [member] [[score member] [score member] …] |
如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。
1 | ZSCORE [key] [member] |
返回有序集 key 中,成员 member 的 score 值。
1 | ZINCRBY [key] [increment] [member] |
为有序集 key 的成员 member 的 score 值加上增量 increment ;可以通过传递一个负数值 increment ,让 score 减去相应的值。
1 | ZCARD [key] |
返回有序集 key 的基数。
1 | ZCOUNT [key] [min] [max] |
返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。
1 | ZRANGE [key] [start] [stop] [WITHSCORES] |
返回有序集 key 中,指定区间内的成员。其中成员的位置按 score 值递增(从小到大)来排序。具有相同 score 值的成员按字典序来排列。可以通过使用 WITHSCORES 选项,来让成员和它的 score 值一并返回。
1 | ZREVRANGE [key] [start] [stop] [WITHSCORES] |
返回有序集 key 中,指定区间内的成员。其中成员的位置按 score 值递减(从大到小)来排列。具有相同 score 值的成员按字典序的逆序排列。
1 | ZRANGEBYSCORE [key] [min] [max] [WITHSCORES] [LIMIT offset count] |
返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。可选的 LIMIT 参数指定返回结果的数量及区间(就像SQL中的 SELECT LIMIT offset, count,不包含score数量。
1 | ZREVRANGEBYSCORE [key] [max] [min] [WITHSCORES] [LIMIT offset count] |
返回有序集 key 中, score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从大到小)的次序排列。
1 | ZRANK [key] [member] |
返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。
1 | ZREVRANK [key] [member] |
返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。
1 | ZREM [key] [member] [member …] |
移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。
1 | ZREMRANGEBYRANK [key] [start] [stop] |
移除有序集 key 中,指定排名(rank)区间内的所有成员。
1 | ZREMRANGEBYSCORE [key] [min] [max] |
移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。
1 | ZRANGEBYLEX [key] [min] [max] [LIMIT offset count] |
可以返回给定的有序集合键 key 中, 值介于 min 和 max 之间的成员。
1 | ZLEXCOUNT [key] [min] [max] |
对于一个所有成员的分值都相同的有序集合键 key 来说, 这个命令会返回该集合中, 成员介于 min 和 max 范围内的元素数量。
1 | ZREMRANGEBYLEX [key] [min] [max] |
对于一个所有成员的分值都相同的有序集合键 key 来说, 这个命令会移除该集合中, 成员介于 min 和 max 范围内的所有元素。
1 | ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] |
计算给定的一个或多个有序集的并集,其中给定 key 的数量必须以 numkeys 参数指定,并将该并集(结果集)储存到 destination 。默认情况下,结果集中某个成员的 score 值是所有给定集下该成员 score 值之 和 。
1 | ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] |
计算给定的一个或多个有序集的交集,其中给定 key 的数量必须以 numkeys 参数指定,并将该交集(结果集)储存到 destination 。默认情况下,结果集中某个成员的 score 值是所有给定集下该成员 score 值之和.
缓存穿透
查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
缓存雪崩
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
缓存击穿
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
缓存热点
缓存中的某些Key(可能对应用与某个促销商品)对应的value存储在集群中一台机器,使得所有流量涌向同一机器,成为系统的瓶颈,该问题的挑战在于它无法通过增加机器容量来解决。
缓存一致性问题
假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,数据不一致。
假设先淘汰缓存,再写数据库:第一步淘汰缓存成功,第二步写数据库失败。
结论:先淘汰缓存,再写数据库。不管用哪种策略都不能保证原子性。
第一种方案:采用延时双删策略 + 缓存超时设置。
第二种方案:异步更新缓存(基于订阅binlog的同步机制)。
更新缓存的四种设计模式
Cache Aside Pattern:先更新数据库,再使缓存失效(建议为缓存设置过期时间)。并发情况下,写操作完成之后删除缓存在先,读操作在写事务提交之前但是放入缓存又在删除缓存之后导致缓存里面的脏数据,我的解决方法是更新缓存代替删除缓存,然后读操作的放入缓存用putIfAbsent来防止覆盖,这种方式只有在并发写的情况下可能有脏数据,但是并发写的概率更小,可以用锁的方式来控制,推荐使用乐观锁。
Read Through Pattern:在查询操作中更新缓存,也就是说,当缓存失效的时候(过期或LRU换出),Cache Aside是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对应用方是透明的。
Write Through Pattern:在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)。
Write Behind Caching Pattern:在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。
1.订阅-发布
1 | PUBLISH [channel] [message] |
将信息 message 发送到指定的频道 channel 。
1 | SUBSCRIBE [channel] [channel …] |
订阅给定的一个或多个频道的信息。
1 | PSUBSCRIBE [pattern] [pattern …] |
订阅一个或多个符合给定模式的频道。
2.事务
1 | MULTI |
标记一个事务块的开始
1 | EXEC |
执行所有事务块内的命令
1 | DISCARD |
取消事务,放弃执行事务块内的所有命令
1 | WATCH |
监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。