7.17 数据库索引+锁+一点点MVCC

leetcode hot100链表最后一题:
        设计一个LRU缓存:要实现增删改查的全部O(1)时间复杂度,并且对于与数据结构实现LRU的业务目标。对于查询来说map可以实现O(1)的查询操作,所以map存储<key,index>的结构方便定位链表节点所在位置。
        而增删改为了方便操作,应当使用双向链表的结构进行处理,其实说起来比较简单,但实际需要考虑的细节部分还有待加强,后面还得练练:

/*defination of struct node*/
#include<iostream>
#include <map>
using namespace std;
struct Node
{
    int key;
    int val;
    Node* next;
    Node* pre;
    Node(int _key,int _val){
        key = _key;
        val = _val;
        next = nullptr;
        pre  = nullptr;
    }
};

class LRUCache {
    Node* v_head = new Node(-1,-1);
    Node* v_tail = new Node(-1,-1);
    Node* head;
    Node* tail;
    int size = 0;
    int capacity;
    //哈希表实现查找的O(1)
    //链表实现增删改的O(1)
    //查找key存储在哪
    map<int,Node*> maplist;
public:
    LRUCache(int capacity) {
        v_head->next = v_tail;
        v_head->pre = nullptr;
        v_tail->next = nullptr;
        v_tail->pre = v_head;
        this->capacity = capacity;
    }
    void delete_node(Node* mid_node)
    {
        maplist.erase(mid_node->key);

        Node* right = mid_node->next;
        Node* left = mid_node->pre;

        mid_node->pre = nullptr;
        mid_node->next = nullptr;

        left->next = right;
        right->pre = left;

        delete(mid_node);
        size-=1;
    }

    void insert_node(int key,int value,bool swap)
    {
        //首先计算容量还够不够
        this->size += 1;

        Node* new_node = new Node(key,value);
        new_node->next = v_head->next;
        new_node->pre = v_head;
        v_head->next->pre = new_node;
        v_head->next = new_node;
        maplist[key] = new_node;

        if(!swap && this->size > this->capacity)
        {
            delete_node(v_tail->pre);
        }
    }

    int get(int key) {
        if(maplist.find(key) == maplist.end())return -1;
        else{
            Node* ans = maplist[key];
            int ans_num = ans->val;
            delete_node(ans);
            //这个时候容量超过了,也不能删除
            insert_node(key,ans_num,true);
            return ans_num;
        }
    }


    void put(int key, int value) {
        if(maplist.find(key) !=  maplist.end())
        {
            //说明了此时在链表里面有该Key值,直接进行更改即可
            delete_node(maplist[key]);
            insert_node(key,value, false);

        }else{
            //此时链表里面不存在key值,在最前面添加该节点
            insert_node(key,value, false);
        }
    }
};



/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */


 InnoDB的索引模型: B+ 平衡多枝树的意思:

主键索引:主键索引的叶子节点存储的是整行的数据,因此也被叫做聚簇索引。
非主键索引:非主键索引存储的是主键值,也就是说使用非主键索引之后,还会再次去主键索引里面查,因此也被成为二级索引。再次回的过程 称之为回表。

索引的维护:通过主键映射到了连续页上,如果插入中间叶子节点的话,会导致存不下需要一个新页,后面的数据也要跟着移动。当然当某些行被删掉,此时利用率不高则会进行页的合并。

自增主键:每次都是追加写入,不涉及页的分裂。

有没有什么场景适合用业务字段直接做主键的呢?还是有的。比如,有些业务的场景需求是这样的:

  1. 只有一个索引;(不占空间)

  2. 该索引必须是唯一索引。(这是主键的要求)

你一定看出来了,这就是典型的 KV 场景。

如何避免二级索引回表?

覆盖索引:

比如ID是主键,其存储信息为ID,name,age,phone等等,下面两句SQL:

1. SELECT * FROM table where age > 7;
2. SELECT ID FROM table where age > 7;

显然,使用了age的索引,但是1由于需要知道其他信息,所以需要回表,而2覆盖了我的查询需求。

 联合索引:

将两个字段进行联合索引,按照字段值的全排列(过滤掉不存在组合)进行hash之后放入B+树,支持最左匹配原则。

索引下推:

举例说明吧,例如索引查询 name = 张%,age = 30
在老版本的MySQL中直接去全扫描满足张姓的人,而在新版本中,可以先根据后面的索引值过滤掉一部分,然后再去回表。

数据库锁:
全局锁: 对于整个数据库进行加锁,典型场景做全库逻辑备份。整个数据库处于只读状态。

  • 如果你在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆;
  • 如果你在从库上备份,那么备份期间从库不能执行主库同步过来的 binlog,会导致主从延迟。

InnoDB具备MVCC功能存储了时间戳,因此可以不停摆,但是并不是每一个数据库都用了InnoDB.

表级锁:

行锁:在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。

这样不是一次全部拿走所有锁的,执行一行拿一个,则会导致死锁。

那么就需要解决:

死锁和死锁检测:

两个策略:

1. 等待释放,第一个被锁的释放,但是等待时间有要求。太长业务不能等得起,太短导致锁还没获取到呢,就没了。

2.死锁检测:

每个新来的被堵住的线程,都要判断会不会由于自己的加入导致了死锁,这是一个时间复杂度是 O(n) 的操作。假设有 1000 个并发线程要同时更新同一行,那么死锁检测操作就是 100 万这个量级的。虽然最终检测的结果是没有死锁,但是这期间要消耗大量的 CPU 资源。因此,你就会看到 CPU 利用率很高,但是每秒却执行不了几个事务。

1. 一种头痛医头的方法,就是如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关掉

2. 控制并发度 

3.减少资源的占有

数据库快照:

回滚日志:(undo log)MVCC中特有,每个数据的不同版本就是undo log

  • 24
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值