pika中锁的应用

参考代码:pika-2.4.0
pika作为类redis的存储系统,为了弥补在性能上的不足,在整个系统中大量使用多线程的结构,涉及到多线程编程,势必需要为线程加锁来保证数据访问的一致性和有效性。其中主要用到了三种锁:

简介

1.互斥锁

pika-2.4.0
./include/pika_binlog.h:  slash::Mutex mutex_;  //保证写入binlog的命令顺序性
./include/pika_hub_manager.h:  slash::Mutex hub_stage_protector_;
./include/pika_hub_manager.h:  slash::Mutex sending_window_protector_;
./include/pika_monitor_thread.h:  slash::Mutex monitor_mutex_protector_;
./include/pika_slot.h:    slash::Mutex slotsmgrt_cond_mutex_;
./include/pika_server.h:  slash::Mutex slave_mutex_; // protect slaves_;
./include/pika_server.h:  //slash::Mutex slotsmgrt_protector_;
./include/pika_server.h:  slash::Mutex binlogbg_mutex_; //主从binlog同步
./include/pika_server.h:  slash::Mutex slowlog_mutex_;
./include/pika_server.h:  slash::Mutex ping_thread_protector_; //主从维护心跳

//admin命令中比如bgsave 、dbsync、keyscan之间会有互斥操作
在flushall命令中添加bgsave_protector_、bgsave_protector_是否多余)
./include/pika_server.h:  slash::Mutex bgsave_protector_;
./include/pika_server.h:  slash::Mutex db_sync_protector_;
./include/pika_server.h:  slash::Mutex key_scan_protector_;

2.读写锁

pika2.4.0

./include/pika_binlog.h:  pthread_rwlock_t rwlock_; //dealmsg中可写命令都要记录入binlog中,且都需按序写入
./include/pika_conf.h:  pthread_rwlock_t rwlock_;   //conf中各配置项使用的读写锁
./include/pika_slot.h:    pthread_rwlock_t rwlock_db_;
./include/pika_slot.h:    pthread_rwlock_t rwlock_batch_;
./include/pika_slot.h:    pthread_rwlock_t rwlock_ones_;
./include/pika_server.h:  pthread_rwlock_t rwlock_; //对db保护,flushall、flushdb、ReadonlyCmd获取写锁,其他dealmsg中命令(set、get等)获取读锁
./include/pika_server.h:  pthread_rwlock_t state_protector_; //protect below, use for master-slave mode


pika3.2

./include/pika_binlog.h:  pthread_rwlock_t rwlock_;  
./include/pika_binlog_reader.h:  pthread_rwlock_t rwlock_;
./include/pika_cmd_table_manager.h:  pthread_rwlock_t map_protector_;
./include/pika_conf.h:  pthread_rwlock_t rwlock_;
./include/pika_meta.h:  pthread_rwlock_t rwlock_;
./include/pika_partition.h:  pthread_rwlock_t db_rwlock_;
./include/pika_repl_server.h:  pthread_rwlock_t client_conn_rwlock_;
./include/pika_rm.h:  pthread_rwlock_t partitions_rw_;
./include/pika_server.h:  pthread_rwlock_t tables_rw_;
./include/pika_server.h:  pthread_rwlock_t state_protector_; //protect below, use for master-slave mode
./include/pika_server.h:  pthread_rwlock_t slowlog_protector_;   
./include/pika_table.h:  pthread_rwlock_t partitions_rw_;

3.行锁

./include/pika_server.h:  slash::RecordMutex mutex_record_;//同一时间只有一个线程对一个key进行写操作

锁在命令中应用

下面从命令分类的角度去看不同类型的命令。在pika执行过程中,需要加几道锁。
1.Suspend的admin命令:trySync、BgSave、FlushAll、Flushdb、readonly

   写锁:RWLockWriter(rwlock_)-->互斥锁:slash::Mutex(bgsave_protector_、db_sync_protector_等)-->跟命令本身操作相关的其他锁
   注意:并不是所有的admin命令都会写入binlog中,目前查看的只有flush相关命令会。

2.非Suspend的admin命令 :info、config等

   读锁:RWLockReader(rwlock_)-->跟命令本身操作相关的其他锁

3.读命令:get、hget等

   读锁:RWLockReader(rwlock_)

4.写命令:set、hset等

   pika2.4 读锁:RWLockReader(rwlock_)--->pika行锁slash::RecordMutex(mutex_record_)-->binlog互斥锁g_pika_server->logger_->Lock()
   pika3.2 读锁:RWLockReader(rwlock_)--->pika行锁slash::RecordMutex(mutex_record_)-->blackwidow记录锁:ScopeRecordLock(lock_mgr_)-->binlog互斥锁g_pika_server->logger_->Lock()

应用场景

1.挂起指令

在挂起指令的执行中,会添加写锁,以确保,此时没有其他任何指令的执行。其他指令(非suspend的admin命令以及读写命令)会添加读锁,可以并行访问。
其中pika-2.4.0挂起指令有:

trysync
bgsave
flushall
Flushdb
readonly

作用和意义:
保证当前服务器在执行挂起指令时,起到阻写作用。比如flush操作会涉及到创建新db的操作,此时其他命令都不应该执行。

2.行锁

行锁,用于对一个key加锁,保证同一时间只有一个线程对一个key进行操作。
在pika2.4.0中只应用于对key的写操作(写命令)。

作用和意义:
pika中存取的数据都是(key,value)数据,不同key所对应的数据完全独立,所以只需要对key加锁可以保证数据在并发访问时的一致性,行锁相对来说,锁定粒度小,也可以保证数据访问的高效性。

应用场景:
在pika系统中,主要在应用于两个地方,在系统上层指令过程中和在数据引擎层面。对于写指令(如SET,HSET)除了需要更新数据库状态,还涉及到pika的增量同步,需要在binlog中添加所执行的写指令,用于保证master和slave的数据库状态一致。故一条写指令的执行,主要有两个部分:

  1. 更改数据库状态
  2. 将指令添加到binlog中

pika3.2 其加锁情况,如下图(图片来源于网络):


image.png

在图中可以看到,pika对同一个key,加了两次行锁.在实际应用中,pika上所加的锁就已经能够保证数据访问的正确性。如果只是为了pika所需要的业务,层面使用行锁是多余的,但是blackwidow的设计初衷就是通过对rocksdb的改造和封装提供一套完整的类redis数据访问的解决方案,而不仅仅是为pika提供数据库引擎。这种设计思路也是秉承了Unix中的设计原则:Write programs that do one thing and do it well。

这样设计大大降低了pika与blackwidow之间的耦合,也使得blackwidow可以被单独拿出来测试和使用,在pika中的数据迁移工具就是完全使用blackwidow来完成,不必依赖任何pika相关的东西。

具体实现:
在pika系统中,一把行锁就可以维护所有key。在行锁的实现上是将一个key与一把互斥锁相绑定,并将其放入哈希表中维护,来保证每次访问key的线程只有一个,但是不可能也不需要为每一个key保留一把互斥锁,只需要当有多条线程访问同一个key时才需要锁,在所有线程都访问结束之后,就可以销毁这个绑定key的互斥锁,释放资源。具体实现如下:

class RecordLock {
 public:
  RecordLock(port::RecordMutex *mu, const std::string &key)
      : mu_(mu), key_(key) {
        mu_->Lock(key_);
      }
  ~RecordLock() { mu_->Unlock(key_); }

 private:
  port::RecordMutex *const mu_;
  std::string key_;

  // No copying allowed
  RecordLock(const RecordLock&);
  void operator=(const RecordLock&);
};

void RecordMutex::Lock(const std::string &key) {
  mutex_.Lock();
  std::unordered_map<std::string, RefMutex *>::const_iterator it = records_.find(key);

  if (it != records_.end()) {
    //log_info ("tid=(%u) >Lock key=(%s) exist, map_size=%u", pthread_self(), key.c_str(), records_.size());
    RefMutex *ref_mutex = it->second;
    ref_mutex->Ref();
    mutex_.Unlock();

    ref_mutex->Lock();
    //log_info ("tid=(%u) <Lock key=(%s) exist", pthread_self(), key.c_str());
  } else {
    //log_info ("tid=(%u) >Lock key=(%s) new, map_size=%u ++", pthread_self(), key.c_str(), records_.size());
    RefMutex *ref_mutex = new RefMutex();

    records_.insert(std::make_pair(key, ref_mutex));
    ref_mutex->Ref();
    mutex_.Unlock();

    ref_mutex->Lock();
    //log_info ("tid=(%u) <Lock key=(%s) new", pthread_self(), key.c_str());
  }
}
完整代码可参考:slash_mutex.cc slash_mutex.h
参考 https://www.w3cschool.cn/pika/pika-4j1f2dk1.html

3. 互斥锁

应用场景:
上图中在执行add binlog过程中,pika用互斥锁(./include/pika_binlog.h: pthread_rwlock_t rwlock_):保证同一时间只有一个线程往binlog中写入数据,从而保证binlog写入的顺序性。

其次,pika内部有些命令内部的操作是互斥的,比如在做flushall涉及到换新DB,此时是不允许进行bgsave和scan操作的,否则会发生错误。为了保证顺利进行,进行flushall操作之前,就需要先加互斥锁bgsave_protector_、key_scan_protector_。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354