春季总结(6)

春季总结即将完结ing

目录

1

2

3

4

5

6

7

8​​​​​​​

9


1

索引的类型

普通索引
这是最基本的索引类型,而且它没有唯一性之类的限制。普通索引可以通过以下几种方式创建:
创建索引,例如CREATE INDEX <索引的名字> ON tablename (列的列表);
修改表,例如ALTER TABLE tablename ADD INDEX [索引的名字] (列的列表);
创建表的时候指定索引,例如CREATE TABLE tablename ( […], INDEX [索引的名字] (列的列表) );

唯一性索引
这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一。唯一性索引可以用以下几种方式创建:
创建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表);
修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表);
创建表的时候指定索引,例如CREATE TABLE tablename ( […], UNIQUE [索引的名字] (列的列表) );

主键
主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。如果你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键一般在创建表的时候指定,例如“CREATE TABLE tablename ( […], PRIMARY KEY (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主键。

全文索引
MySQL从3.23.23版开始支持全文索引和全文检索。在MySQL中,全文索引的索引类型为FULLTEXT。全文索引可以在VARCHAR或者TEXT类型的列上创建。
 

单列索引与多列索引
索引可以是单列索引,也可以是多列索引。下面我们通过具体的例子来说明这两种索引的区别

1.多个单列索引:
定义:即是在表中在需要索引的字段上为每个字段设计一个索引;
特点:简单,索引个数多

2.多列索引:
定义:即是在表中根据查询需求在多个字段上设计一个索引;
特点:稍微复杂,需要考虑索引顺序;

索引的缺点
到目前为止,我们讨论的都是索引的优点。事实上,索引也是有缺点的。

首先,索引要占用磁盘空间。通常情况下,这个问题不是很突出。但是,如果你创建每一种可能列组合的索引,索引文件体积的增长速度将远远超过数据文件。如果你有一个很大的表,索引文件的大小可能达到操作系统允许的最大文件限制。

第二,对于需要写入数据的操作,比如DELETE、UPDATE以及INSERT操作,索引会降低它们的速度。这是因为MySQL不仅要把改动数据写入数据文件,而且它还要把这些改动写入索引文件。

( 1 )为经常出现在关键字 orderby, group by, distinct 后面的字段,建立索引 ;   经常用于where子句或者作为连接条件的列。
( 2 )在 union 等集合操作的结果集字段上建立索引,其建立索引的目的同上 ;
( 3 )为经常用作查询选择的字段,建立索引 ;
( 4 )在经常用做表链接的属性上,建立索引 ;
————————————————

2

进程间通信的方式有:

一、管道

管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。

特点:

  • 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

  • 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

  • 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

二、FIFO

FIFO,也称为命名管道,它是一种文件类型。

1、特点

  • FIFO可以在无关的进程之间交换数据,与无名管道不同。

  • FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

三、消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。

特点

  • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。

  • 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。

  • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

四、信号量

信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

特点

  • 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。

  • 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。

  • 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。

  • 支持信号量组。

五、共享内存

3

命名管道

命名管道(FIFO)不同于无名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据

命名管道(FIFO)和无名管道(pipe)有一些特点是相同的,不一样的地方在于:

1、FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在内存中。

2、当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。

3、FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。

命名管道的默认操作
后期的操作,把这个命名管道当做普通文件一样进行操作:open()、write()、read()、close()。但是,和无名管道一样,操作命名管道肯定要考虑默认情况下其阻塞特性。

下面验证的是默认情况下的特点,即 open() 的时候没有指定非阻塞标志( O_NONBLOCK )。

1)

open() 以只读方式打开 FIFO 时,要阻塞到某个进程为写而打开此 FIFO
open() 以只写方式打开 FIFO 时,要阻塞到某个进程为读而打开此 FIFO。

简单一句话,只读等着只写,只写等着只读,只有两个都执行到,才会往下执行。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main(int argc, char *argv[])
{
	int fd;
	int ret;
	
	ret = mkfifo("my_fifo", 0666); //创建命名管道
	if(ret != 0)
	{
		perror("mkfifo");
	}
	
	printf("before open\n");
	fd = open("my_fifo", O_RDONLY);//等着只写
	if(fd < 0)
	{
		perror("open fifo");
	}
	printf("after open\n");
	
	printf("before read\n");
	char recv[100] = {0};
	
	//读数据,命名管道没数据时会阻塞,有数据时就取出来
	read(fd, recv, sizeof(recv)); 
	printf("read from my_fifo buf=[%s]\n", recv);
	
	return 0;
}


#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main(int argc, char *argv[])
{
	int fd;
	int ret;
	
	ret = mkfifo("my_fifo", 0666);//创建命名管道
	if(ret != 0)
	{
		perror("mkfifo");
	}
	
	printf("before open\n");
	fd = open("my_fifo", O_WRONLY); //等着只读
	if(fd < 0)
	{
		perror("open fifo");
	}
	printf("after open\n");
	
	printf("before write\n");
	// 5s后才往命名管道写数据,没数据前,读端read()阻塞
	sleep(5);
	char send[100] = "Hello Mike";
	write(fd, send, strlen(send));
	printf("write to my_fifo buf=%s\n", send);
	
	return 0;
}

4

 通过线程2的加锁操作, 避免了这样的问题. 这也解释了为什么pthread_cond_wait函数在进入以后要进行解锁操作, 如果起不解锁, 那么线程2在进行条件置为true的操作就没有办法执行, 因为线程1在进入等待之前已经对这个变量加锁了. 这样线程1会一直等待, 而线程2也会等待, 导致死锁.

线程1                                       线程2
pthread_mutex_lock(&mutex);           pthread_mutex_lock(&mutex);
while (condition == FALSE) {          condition = TRUE;
 pthread_cond_wait(&cond, &mutex);    pthread_cond_signal(&cond);
}                                     pthread_mutex_unlock(&mutex);
pthread_mutex_unlock(&mutex);


条件变量的使用例子
下面的链接以redis 3.2.3的代码中的BIO模块为例子, 给出实际系统中的条件变量使用的方法. 可以发现, redis的BIO模块就是用上面介绍的模型实现的.

Redis BIO系统

总结
锁的基本使用包括了锁初始化, 加锁, 解锁三个步骤. 使用默认的锁性质时, 一个锁变量只能由一个线程获得, 在这个线程释放锁之前, 其他线程如果尝试获得锁, 就会进入阻塞的状态. 这样, 加锁和解锁之间的这段代码只有一个线程执行, 从而能够保证并发访问的正确性.

对于条件变量, 其基本的使用场景是, 某些线程对条件进行判断, 如果不满足条件, 就进入等待状态. 在进行条件判断之前, 先进行加锁操作. 另外一些线程则是负责对条件赋值为真, 然后通知等待的线程继续执行, 线程被唤醒后, 继续进入判断的环节以及后续的操作.

以上面例子来看, 也就是可以分为以下两部分:

A类线程:

加锁
检查(条件不成立则等待,知道成立再次进入检查阶段)
执行
解锁
B类线程:

加锁
条件置为真
通知
解锁

自旋锁:线程获取锁的时候,如果锁被其他线程持有,则当前线程将循环等待,直到获取到锁。

自旋锁等待期间,线程的状态不会改变,线程一直是用户态并且是活动的(active)。

自旋锁如果持有锁的时间太长,则会导致其它等待获取锁的线程耗尽CPU。

自旋锁本身无法保证公平性,同时也无法保证可重入性。

基于自旋锁,可以实现具备公平性和可重入性质的锁
————————————————
 

5

获取被管理对象的指针
使用get()·函数获取管理对象的指针。

Task *p1 = taskPtr.get();
1
重置 unique_ptr 对象
在 unique_ptr 对象上调用reset()函数将重置它,即它将释放delete关联的原始指针并使unique_ptr 对象为空。

taskPtr.reset();
1
unique_ptr 对象不可复制
由于 unique_ptr 不可复制,只能移动。因此,我们无法通过复制构造函数或赋值运算符创建unique_ptr对象的副本。

// 编译错误 : unique_ptr 不能复制
std::unique_ptr<Task> taskPtr3 = taskPtr2; // Compile error

// 编译错误 : unique_ptr 不能复制
taskPtr = taskPtr2; //compile error


转移 unique_ptr 对象的所有权
我们无法复制 unique_ptr 对象,但我们可以转移它们。这意味着 unique_ptr 对象可以将关联的原始指针的所有权转移到另一个 unique_ptr 对象。让我们通过一个例子来理解:

// 通过原始指针创建 taskPtr2
std::unique_ptr<Task> taskPtr2(new Task(55));
// 把taskPtr2中关联指针的所有权转移给taskPtr4
std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
// 现在taskPtr2关联的指针为空
if(taskPtr2 == nullptr)
    std::cout<<"taskPtr2 is  empty"<<std::endl;

// taskPtr2关联指针的所有权现在转移到了taskPtr4中
if(taskPtr4 != nullptr)
    std::cout<<"taskPtr4 is not empty"<<std::endl;

// 会输出55
std::cout<< taskPtr4->mId << std::endl;



std::move() 将把 taskPtr2 转换为一个右值引用。因此,调用 unique_ptr 的移动构造函数,并将关联的原始指针传输到 taskPtr4。在转移完原始指针的所有权后, taskPtr2将变为空。

释放关联的原始指针
在 unique_ptr 对象上调用 release()将释放其关联的原始指针的所有权,并返回原始指针。这里是释放所有权,并没有delete原始指针,reset()会delete原始指针。

std::unique_ptr<Task> taskPtr5(new Task(55));
// 不为空
if(taskPtr5 != nullptr)
    std::cout<<"taskPtr5 is not empty"<<std::endl;
// 释放关联指针的所有权
Task * ptr = taskPtr5.release();
// 现在为空
if(taskPtr5 == nullptr)
    std::cout<<"taskPtr5 is empty"<<std::endl;

unique_ptr不能直接复制,必须使用std::move()转移其管理的指针,转移后原 unique_ptr 为空。std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);

创建unique_ptr对象有两种方法:

//C++11: 
std::unique_ptr<Task> taskPtr(new Task(23));
//C++14: 
std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34);

C++ 智能指针 unique_ptr 详解与示例_码农小明的博客-CSDN博客_c++ unique_ptr

6

GC对每个对象有个引用计数,所有说只要有变量在引用它,计数器就不为了,一个变量不再引用这个对象,对象的计数器就减一,知道计数器为0时,对象就成为内存垃圾了(没有变量引用它),但是此时垃圾并没有回收。那什么时候回收呢,是在内存占用超过一定限度是,GC才启动,释放垃圾资源,说白了就是delete这些对象,将空间归还给系统。但是这还没完,空间释放后,内存空间就不连续了,所有GC还要赶一件事,就是将空间整理下,将占用的空间连续话,具体说就是将空间向上推,就是想高地值转存,这样空间就连续了,使用也方便了,然后GC就改变应用那些对象的变量里地地址,让他们指向正确的位置,所以说C#中的引用类型就是一种指针,一种动态改变值的指针。即压缩内存。

托管资源:一般是指被CLR控制的内存资源,这些资源由CLR来管理。可以认为是.net 类库中的资源。
非托管资源:不受CLR控制和管理的资源,比如文件流,数据库的连接,网络连接,系统的窗口句柄,打印机资源等,
这类资源一般不存在堆上。可以认为操作系统资源的一组API。
对于托管资源,GC负责垃圾回收。对于非托管资源,GC可以跟踪非托管资源的生存期,但是不知道如何释放它,这时候就要人工进行释放。
2、CLR:公共语言运行库(Common Language Runtime,CLR)是整个.NET框架的核心,
————————————————————————————————

7

心跳就是一个业务包,断开的情况更为普遍:
1.假死,连接还在,但是什么都处理不了了
2.单个线程、进程死了,程序能运行
3.心跳更高级,更独立,TCP更底层。可能底层能运行,上层运行不了了

解决了假死的问题,但是不能解决线程持续被占用的问题。

我们创建的内存表和MySQL内部临时表有所不同:内存表的数据存放在内存中,而内部临时表(我们的query语句产生的)在恰当的时候存放在内存中,当内部临时表变得很大时,MySQL会自动地把它转化为 在磁盘上存储的表,而我们创建的内存表,却不会自动转换。

or  and不能够并列

发送方IP、发送方Port、接收方IP、接收方Port、通信协议(Tcp/Udp),这也被称为五元素。五元素是相对于一个操作系统而言的

结果集做连接查询 非常慢因为没有索引
将结果集插入临时表中,临时表建立索引,则速度变快。有索引之后提速60倍。

每当想到自己已经工作了四年,就仿佛从梦中惊醒一样。大学毕业恍然还在昨天,转眼已经物是人非了。

最成功的事:C#客户端

最失败的事:C#客户端,ETF系统、港股系统

记忆最深刻的事:C#客户端,实习期的毕设

8

Create函数返回一个新的 双缓冲对象,new()括号内制定分配的地址为memobj ,创建的对象是双缓冲对象。这是一个共享内存对象。

主子进程间共用一个内存块。

于是, G_TextLog就是一个对象,存在的地址位于一个共享内存里。

TDoubleBuffer* TDoubleBuffer::Create(void* memobj, size_t memsize, bool isshared)
 
 memobj = mmap(0, memsize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
 
   return new (memobj) TDoubleBuffer(memsize, isshared);

通过mmap()实现共享内存的通信方式有许多特点和要注意的地方。

  • 使用特殊文件提供匿名内存映射:适用于具有亲缘关系的进程之间;由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。 对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可. 

#include <sys/mman.h>    
#include <stdio.h>    
#include <stdlib.h>    
#include <unistd.h>    
    
#define BUF_SIZE 100    
    
int main(int argc, char** argv)    
{    
    char    *p_map;    
    
    /* 匿名映射,创建一块内存供父子进程通信 */    
    p_map = (char *)mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE,    
            MAP_SHARED | MAP_ANONYMOUS, -1, 0);    
    
    if(fork() == 0) {    
        sleep(1);    
        printf("child got a message: %s\n", p_map);    
        sprintf(p_map, "%s", "hi, dad, this is son");    
        munmap(p_map, BUF_SIZE); //实际上,进程终止时,会自动解除映射。    
        exit(0);    
    }    
    
    sprintf(p_map, "%s", "hi, this is father");    
    sleep(2);    
    printf("parent got a message: %s\n", p_map);    
    
    return 0;    
}    

9

1.1 粘包和拆包原因

(1)要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包;

(2)接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包;

(3)要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包;

(4)待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。即TCP报文长度-TCP头部长度>MSS。

1.2 粘包和拆包解决策略

       由于底层的TCP无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,归纳如下:

1 消息定长。发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
2 设置消息边界。服务端从网络流中按消息边界分离出消息内容。在包尾增加回车换行符进行分割,例如FTP协议。
3 将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体长度)的字段。
更复杂的应用层协议。
————————————————
版权声明:本文为CSDN博主「艾伦lee」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ailunlee/article/details/95944377

10

尊敬的领导,由于个人原因,我决定提出离职。

感谢各位领导这几年的教导,感谢各位同事的帮助。转眼我已经在公司工作了四年,蓦然回首,大学毕业仿佛还在昨日。经过慎重考虑,我决定趁着自己还年轻,出去闯一闯;同时由于女朋友在成都,于是决定先前往成都发展。来日方长,一切都后会有期。

再次感谢公司这些年带给我的支持和成长。

此致

敬礼。

欲买桂花同载酒,终不似,少年游

猜你喜欢

转载自blog.csdn.net/baidu_39486224/article/details/124156847