iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >MySQL动态hash结构常用的实现方式
  • 160
分享到

MySQL动态hash结构常用的实现方式

2024-04-02 19:04:59 160人浏览 八月长安
摘要

这篇文章主要介绍“Mysql动态hash结构常用的实现方式”,在日常操作中,相信很多人在mysql动态hash结构常用的实现方式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”

这篇文章主要介绍“Mysql动态hash结构常用的实现方式”,在日常操作中,相信很多人在mysql动态hash结构常用的实现方式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Mysql动态hash结构常用的实现方式”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

MySQL动态hash结构
1.常用的实现方式
      前一段时间一直在研究mysql中的hash结构,大概搞清楚了这种no empty slot的hash结构,读了几篇关于mysql中的hash结构文章,发现很多文章对于这种动态hash的关键点解释不够清楚,特此把这些天看mysql中hash的这段代码的体会写一下。
      mysql中的hash结构不同于一般的那种用链表解决冲突的hash结构,链表解决冲突的hash结构用在memcached,jdk中,最常见的hash结构如下图:

      这种hash结构实现起来十分简单,事先分配好一个2^n大小的一个数组,然后对键值对2^n取余数,然后把数据根据余数放到相应的数组下标中,如果恰好这个位置元素已经被其他元素占据了,那么就通过一个单链表,来解决键值冲突,如果hash结构中键值的个数超过一定的阈值,就认为这个结构中数据元素太多了,很高的概率hash后必须通过遍历冲突链表来找到键值,这时再把hash数组的规模以2的倍数增长,然后rehash每个元素即可。
      还有一种hash结构也是预先分配好一个数组,如果元素冲突,不采取链表解决冲突,采取取下一个空闲位置作为元素的真实存储地址。
      不管怎样,上面的这两种hash结构的优点就是好理解,好实现,缺点就是浪费空间,可以发现,hash数组是预先分配好的,那么元素的空间都是已经定的了,例子:如果按照上面的结构,如果4位置永远没有元素的话,那么这个位置占有的空间永远就是浪费的。

2.无空闲空间的动态hash结构

      mysql中的hash结构的特点就是没有浪费的空闲空间,数组是动态分配的,任何时刻,这个数组所开辟的空间总是和当前hash结构中元素的个数相同。
      实现的重点就在于对一个元素求hash值然后通过一个计算掩码的公式求得这个元素真实的hash数组的位置,在之前那两中hash结构中,这个公式一般是:hash mod 2^n,但是这个动态hash结构的计算掩码的公式是:
代码:

  1.  

  2. 182 static uint my_hash_mask(my_hash_value_type hashnr, size_t buffmax,

  3. 183                          size_t maxlength)              

  4. 184 {  

  5. 185   if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1)); 

  6. 186   return (hashnr & ((buffmax >> 1) -1));

  7. 187 }  

       这里hashnr是一个字符串所代表的hash值,buffmax是2^n,maxlength是当前数组中记录的个数(它就是当前数组的长度,分配的空间),这里通过代码可以看到maxlength介于buffmax/2到buffmax之间。上面这段代码的意思是,按照原有的那种取余数的方式计算掩码,对hashnr除以buffmax取余数,这里会出现一种情况就是有可能求余得到的键值会大于当前等于当前record的数量,按照原来的方式来说只要对buffmax求余数,那么从对应的hash数组的范围就是[0,buffmax-1],在这区间都是分配好的内存,但是动态hash结构中,不会分配超过records的数组,也就是从(records,buffmax-1]是没有分配内存的,数组的大小就是records,而不是buffmax,这里处理方法就是把buffmax除以2后,再去取得余数,得到对应的hash键值。
       这里除以2,为什么不除以3,是另有玄机的,可以知道对于一个数除以a,和这个数除以2/a得到的余数之差就是2/a,这个是必然的,例如39/8=7,那么39/4=3,7和3之间差的是4,就是4=8/2,那么就说明了如果hash值对buffmax求余的话,如果大于等于records,那么就会折半再去取余数,这个余数和真实余数之间差buffmax/2。
       可以看出这个动态hash表在求余数大于等于records的情况下,选择了一种折中的办法,就是把这个hash值通过buffmax/2求得一个临时的hash掩码。
       这个动态hash表,每插入一个元素records就会加1,如果records==buffmax时,buffmax就会再次增大两倍。这个规则我们会发现有问题,先假设上次我们成功插入了元素,掩码落在了[0,records-1]之内,这时由于成功插入了新的元素records就会加1,这时如果原来的掩码通过buffmax计算出来的掩码比records大,就落在[0,records)之内,现在records增加了一位,那么原来存放上一个记录的位置就出现了问题。他应该在当前records的位置。
       所以这种动态hash结构的特点就是在插入新元素之前,试着恢复原来本该属于当前新开辟数组的位置元素,那么属于这个新地方的元素的下标计算方法:

  1. 386 halfbuff= info->blength >> 1;

  2. 387

  3. 388 idx=first_index=info->records-halfbuff;

      这段代码的意思就是先把blength(records位于[blength/2,blength]之间)除以2,然后当前records减去halfbuff即可,就是能计算出上一步本该属于当前位置的元素的下标,这个过程就是前面讲到求余数的逆过程,举例:如果当前records=5 blength=8,那么如果一个元素hash值是13那么通过求掩码知道,去余数是5,但是这时records=5,那么通过那种折中的办法,13/4=1,那么1就是最终的掩码的位置。那么上一步插入了数据之后,records=6,那么原来上一步插入数据13就应该是掩码=5,那么当前records=6,6-5=1,这个1位置的元素就是上一步有可能掩码是5的元素,由于records的限制,被放到了1的位置,这是就需要把他重新放大掩码为5的位置上来。
      如何知道当前hash值是否是由于records的限制被放到1的位置,还是通过直接计算掩码得到本该属于他的位置1的地方。通过位操作符&就可以得到结果
398 if (!(hash_nr & halfbuff))
      这句代码的意思就是判断这个hash值是否属于低位(本该属于低位还是被records限制放到的低位,低位以records为界),还是刚才的例子13&4>0,那么就说明13的hash值属于高位,不属于原来掩码为1的位置;9&4=0,那么就说明9这个元素就是属于掩码位置为1的位置。
      通过上面的一段分析,动态hash结构,每次插入新的元素就要分配一个元素的位置,首先要去移动上一步被放到低位的元素,恢复到原来属于它的位置。这里只需要处理records-halfbuff位置的元素,因为在这个元素之前都已经处理过了,比这个元素大的处理移动不了,因为records的大小还没有到达能够移动这些大掩码的数据。画个图来解释一下前面讲到的知识。

      如图所示,hash结构已经存储了5个元素,现在records=5,blength=8,蓝色的空间(index=[0,4])代表已经分配的空间,每个分配的空间都被数据占用,没有空闲的,index=5的绿色空间是新分配的,用于新插入新的数据,红色空间,index=[6,7]是不存在的,为了显示blength才画出来。那么在插入新数据之前,因为新分配的空间可能原先属于hash掩码是5的元素,那么在插入新元素之前首先需要找到这个元素,把它放到5的地方,空闲出来的地方才作为没有被利用的位置,插入新的元素。要知道原本属于index=5的元素一定被hash到了index=1的地方(因为对blength=8求余为5,那么对4求余那么一定是1),那么看看index=1的元素hash值到底是多少,如果是1,那么index=1的元素就不用移动了,否则这个index=1的元素调整到5的位置。
      也就是说这个动态hash结构,每次插入一个元素之前都要调整一下原来的结构,把原来被插入到其他index的元素重新移动到属于它本来的index上,这就是动态hash结构的精髓。
3.元素的移动
      通过上面的分析,在新插入数据之前,需要调整已有元素的位置,目标就是重新恢复高位元素的链表,同时修正低位元素的链表,因为当前链表就是低位链表,这个链表含有高位元素,要把恢复到起点是records元素高位链表中,当前链表起点就是records-blength/2元素,如果这个元素的hash掩码等于records,那么说明这个元素属于index=records,那么需要移动这个元素到这个位置,同时这个元素的移动会导致其他节点的next指针的混乱(因为这个元素的位置发生了移动),所以元素移动的目的就是把属于高位的元素回复到原来的位置,同时恢复低位和高位元素的next指针。
      移动元素的逻辑参照源代码,需要分清low和high,low表示有元素本来就属于当前的hash掩码,high表示这个元素不属于当前hash掩码值,真正的掩码值是再加上blength/2,在同一个hash掩码的情况下,几个重要的表示位,我说下我理解的意义:(可能有偏差)

  1. LowFind:表示有元素属于当前的hash掩码

  2. LowUsed:低位元素是否还占据了老的空闲位置(false代表上一个低位元素占据了新的空闲位置,true代表使用的还是老的位置)

  3. HighFind:表示有元素不属于当前的hash掩码,等于当前掩码+blength/2

  4. HighUsed:高位元素是否占据了老的空闲位置(false代表上一个高位元素占据了新的空闲位置,true代表使用的还是老的位置)

重要的变量:

  1. empty:表示hash结构中空闲的index

  2. gpos:表示属于低位(当前掩码)的上一个元素的index

  3. ptr_to_rec:表示属于当前掩码的上一个元素的data

  4. gpos2:表示属于当前掩码+blength/2的上一个元素的index

  5. ptr_to_rec2:表示属于高位(当前掩码+blength/2)的上一个元素的data

      对于元素的移动,是从当前records-blength/2的元素开始,开始调整具有相同hash掩码元素的位置(原因参看前面的解释,由于属于当前位置的元素按照2/blength被重新计算掩码,这个位置一定是records-blength/2),大体上分为两种情况,一种是当前位置的元素的重新按照新的records计算hash掩码还属于原来的掩码,就认为这个是低位,另一种是当前位置的元素重新按照records计算hash掩码属于records位置,认为这个是高位。
     元素位置的调整和next指针的变化代码:

  1.  

  2. 385   data=dynamic_element(&info->array,0,HASH_LINK*); //计算hash数组第一个元素的位置

  3. 386   halfbuff= info->blength >> 1;       //为了得到元素可能属于records位置的index

  4. 387 

  5. 388   idx=first_index=info->records-halfbuff; //减去halfbuff得到可能属于records位置的index

  6.       //还不满就需要恢复那些放错位置的index上的数据

  7. 389   if (idx != info->records)                            

  8. 390   {

  9. 391     do

  10. 392     {

  11. 393       pos=data+idx;             //得到第一个index,低位

  12. 394       hash_nr=rec_hashnr(info,pos->data);   //重新计算下hash值

  13. 395       if (flag == 0)                            

  14. 396         if (my_hash_mask(hash_nr, info->blength, info->records) != first_index)

  15. 397           break;

  16. 398       if (!(hash_nr & halfbuff))//做与操作,根据hash值判断是否是真正属于records位置的

  17. 399       {                                        

  18.        //与操作的结果等于0说明这个hash值的元素就是属于当前位置的,进入case1

  19. 400         if (!(flag & LOWFIND))//如果元素属于低位没有出现过,进入case1-a

  20. 401         {

  21. 402           if (flag & HIGHFIND)

  22.       //如果这个元素属于低位,但是这个属于高位的元素已经找到,那么当前元素肯定是由于位置冲突,属于低位,但是被挤到了其他的位置

  23. 403           {                        //进入case1-a-1

  24. 404             flag=LOWFIND | HIGHFIND;

  25. 405            

  26. 406             gpos=empty;             //现在的位置pos变成empty

  27. 407             ptr_to_rec=pos->data;

  28. 408             empty=pos;                          

  29. 409           }

  30. 410           else

  31. 411           {                            //进入case1-a-2

  32. 412             flag=LOWFIND | LOWUSED;            

  33. 413             gpos=pos;

  34. 414             ptr_to_rec=pos->data;

  35. 415           }

  36. 416         }

  37. 417         else

  38. 418         {                             //进入case1-b

  39. 419           if (!(flag & LOWUSED))

  40. 420           {

  41. 421            

  42. 422             gpos->data=ptr_to_rec;

  43. 423             gpos->next= (uint) (pos-data);

  44. 424             flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);

  1.  

  2. 425           }

  3. 426           gpos=pos;

  4. 427           ptr_to_rec=pos->data;

  5. 428         }

  6. 429       }

  7. 430       else                            //进入case2

  8. 431       {                                        

  9. 432         if (!(flag & HIGHFIND))       //进入case2-a

  10. 433         {

  11. 434           flag= (flag & LOWFIND) | HIGHFIND;

  12. 435          

  13. 436           gpos2 = empty; empty=pos;

  14. 437           ptr_to_rec2=pos->data;

  15. 438         }

  16. 439         else

  17. 440         {                             //进入case2-b

  18. 441           if (!(flag & HIGHUSED))

  19. 442           {

  20. 443            

  21. 444             gpos2->data=ptr_to_rec2;

  22. 445             gpos2->next=(uint) (pos-data);

  23. 446             flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);

  24. 447           }

  25. 448           gpos2=pos;

  26. 449           ptr_to_rec2=pos->data;

  27. 450         }

  28. 451       }

  29. 452     }

  30. //递归到属于这个hash掩码冲突链表的最后一个元素

  31. 453     while ((idx=pos->next) != NO_RECORD);

  32. 454 

  33. 455     if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)

  34. 456     {

  35. //如果没有LowUsed,说明当前链表的最后一个元素不是原来的位置,就设置next指针为null

  36. 457       gpos->data=ptr_to_rec;

  37. 458       gpos->next=NO_RECORD;

  38. 459     }

  39. 460     if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)

  40. 461     {

  41. 462       gpos2->data=ptr_to_rec2;

  42. 463       gpos2->next=NO_RECORD;

  43. 464     }

  44.  

说明:注意元素的移动只是移动data,next指针不移动。
case1:当前元素的hash的掩码属于低位,理论上这部分元素不应该被移动,但是如果键值冲突的元素,应该被移动到原来属于它的位置,同时更新next指针
     1-a:同样的hash掩码,在低位还没有出现过
          1-a-1:在低位没有出现,但是过在高位出现了,那么高位出现的元素,肯定把高位的元素恢复到了records的位 置,这时只需要把这个元素恢复到空闲的位置(高位元素让出的位置),把当前的位置标志为empty。
          1-a-2:表示低位没有出现过,高位也没有出现,那么当前的元素保持当前的位置,低位的元素就是要保持原有的hash掩码的位置。
     1-b:表示当前元素的前一个低位元素占据新的空闲的位置,这时新的空闲位置的next指针还是原来的,需要上一个低位元素的next指针指到当前位置的低位元素。
  举例:如果是当前元素是低位,上一个元素属于低位,同时上个元素由于高位元素让出了位置,更改了低位元素的位置(由于高低位掩码相同,低位被挤到了其他掩码的位置),这时需要重新构造低位元素的next的指针

       假设b元素和a元素拥有相同的hash掩码,都属于低位,a元素的位置不属于当前掩码的位置,需要被调到绿色的位置,同时a原来的位置变成了空闲的位置,a位置需要重设置a指向b的next指针。
代码中有个细节:
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
      这段代码的意思是:flag中变为 LOWFIND | LOWUSED,同时去掉状态HIGHUSED,表示上一个元素不是高位元素了,这是因为设置完当前元素b和上一个元素a的next指针后(a->next=b),如果下一个元素c是高位的话,那么按照原来逻辑b->next=c,那么这样next链表就会出现错误,所以这时候把HIGHUSED设置成false,这样就导致了递归到c元素时会重新设置上一个高位元素到当前元素c的next指针。        

Case2:表示当前元素是高位元素

      2-a:如果之前没有出现过高位元素,那么就把当前元素放到空闲的位置,如果不是第一个高位元素,就不需要移动了,因为后面元素的链表是完整的,第二个元素到后面的高位元素next指针都是对的,只是第一个元素到第二个高位元素的next指针不对,因为第一个高位元素被移动到了新的empty的位置。

      2-b:这种情况类似于1-b,就是设置第一个高位元素的next指针到第二个元素,后面的next指针都正确,不用管,这 种情况会导致后面一个元素如果是低位元素,需要调整上一个低位元素next指针指到下一个低位元素,所以这种情况需要表达式flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED)屏蔽掉状态LOWUSED。

4.新元素的插入
        hash结构的records数量增加了1,导致了hash结构重新调整了掩码等于records和records-blength/2(高位元素和低位元素)的元素的位置。这是新元素就可以插入了。计算新元素的掩码,找到相应的位置,如果那个位置和empty指针的位置相同,那么说明这个元素是这个掩码的第一个元素,直接插入即可。不等于empty指针的位置,那么说明有元素占据了属于新元素的位置,可能是hash掩码相同的元素,或者掩码不同的元素。如果是掩码相同,那么就把当前元素放到empty的位置,同时原来位置的元素的next指针指到empty的位置。如果掩码不同,把当前元素放到empty的位置,同时要把等于这个元素掩码的链表的最后一个链接到这个新元素上面,这需要找到这个掩码的最后一个元素。
代码:

  1.  

  2.        //计算这个records的掩码

  3. 468   idx= my_hash_mask(rec_hashnr(info, record), info->blength, info->records + 1);

  4. 469   pos=data+idx;

  5. 470   if (pos == empty)    //如果这个掩码的位置是空闲的,直接插入

  6. 471   {

  7. 472     pos->data=(uchar*) record;

  8. 473     pos->next=NO_RECORD;

  9. 474   }

  10. 475   else

  11. 476   {

  12. 477    

  13. 478     empty[0]=pos[0];

  14.        //计算pos位置元素的掩码

  15. 479     gpos= data + my_hash_rec_mask(info, pos, info->blength, info->records + 1);

  16. 480     if (pos == gpos) //如果相等,直接插入元素到空闲位置,同时把pos位置的next指针指到新元素

  17. 481     {

  18. 482       pos->data=(uchar*) record;

  19. 483       pos->next=(uint) (empty - data);

  20. 484     }

  21. 485     else

  22.         //如果掩码不相同,找到掩码等于gpos的最后一个元素,同时把最后一个元素的next指针指到新的元素

  23. 486     {

  24. 487       pos->data=(uchar*) record;

  25. 488       pos->next=NO_RECORD;

  26. 489       movelink(data,(uint) (pos-data),(uint) (gpos-data),(uint) (empty-data));

  27. 490     }

  28. 491   }

  29. 492   if (++info->records == info->blength)

  30. 493     info->blength+= info->blength;

  31. 494   return(0);

  32.  

到此,关于“MySQL动态hash结构常用的实现方式”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

您可能感兴趣的文档:

--结束END--

本文标题: MySQL动态hash结构常用的实现方式

本文链接: https://www.lsjlt.com/news/60480.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • MySQL动态hash结构常用的实现方式
    这篇文章主要介绍“MySQL动态hash结构常用的实现方式”,在日常操作中,相信很多人在MySQL动态hash结构常用的实现方式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”...
    99+
    2024-04-02
  • redis删除hash的实现方式
    目录Redis删除hash方式redis之hash类型解读redis中存取hash类型常用命令hash命令小结总结redis删除hash方式 在工作中遇到删除hash类型的缓存时遇到了,怎样也删不掉redis里面的缓存,...
    99+
    2023-01-28
    redis删除hash redis hash redis hash删除
  • SpringBoot2.动态@Value的实现方式
    title: SpringBoot2.动态@Value实现 前言 前面文章有详细描述过各个不同阶段对于bean的扩展接口 所以今天就基于BeanPostProcessor实现Spri...
    99+
    2024-04-02
  • mysql之动态增添字段实现方式
    目录数据库mybatis逆向工程新建springboot项目遇到的问题总结数据库 --用户表 CREATE TABLE `users` ( `id` int(11) NOT N...
    99+
    2023-05-20
    mysql动态增添字段 mysql增添字段 mysql字段增添
  • C#实现异步的常用方式总结
    目录前言.NET异步编程模式C#异步有四种实现方式1、异步方法(Async Method TAP模式)2、任务并行库(TPL, Task Parallel Library&...
    99+
    2023-05-19
    C#实现异步方式 C#实现异步 C# 异步
  • springboot动态调用实现类方式
    目录springboot动态调用实现类springboot手动获取实现类springboot动态调用实现类 定义规则的多种类型 public enum RuleType { ...
    99+
    2024-04-02
  • Vue动态样式方法实例总结
    目录前言Vue动态样式实现方式:style:classv-if/v-else + 复合类名结尾前言 本文主要针对 Vue2.x 来展开vue的动态css样式方法归纳。如果亲爱的读者们...
    99+
    2023-02-07
    Vue动态样式 vue 动态style vue动态样式方法
  • C#多态的三种实现方式(小结)
    C#实现多态主要有3种方法,虚方法,抽象类,接口 1 虚方法 在父类的方法前面加关键字virtual, 子类重写该方法时在方法名前面加上override关键字,例如下面的Perso...
    99+
    2024-04-02
  • Mybatis实现动态排序方式
    目录Mybatis实现动态排序Mybatis动态排序不生效问题造成问题原因解决办法Mybatis实现动态排序 在数据展示时,很有可能碰到,需要动态排序的需求。当数据比较少的时候,还可...
    99+
    2022-11-13
    Mybatis动态排序 Mybatis排序 Mybatis实现动态排序
  • JavaScript实现继承的6种常用方式总结
    目录1.原型链继承2.借用构造函数继承3.组合继承(经典继承)4.原型式继承方法一:借用构造函数方法二:Object.create()5.寄生式继承6.寄生组合式继承7.ES6、Cl...
    99+
    2024-04-02
  • mysql自动填充时间的两种实现方式小结
    目录mysql自动填充时间的两种方式方法一:数据库级别方式二:代码级别mysql自动填充时间的两种方式 mysql建表的时候有两个列,一个是createtime、另一个是updatetime 当插入一条数据,create...
    99+
    2022-11-30
    mysql自动填充时间 自动填充时间 mysql自动填充
  • vue实现动态表单动态渲染组件的方式(2)
    本文实例为大家分享了vue实现动态表单动态渲染组件的方式,供大家参考,具体内容如下 思路 先把所有可能出现的表单/组件写在主页面每个表单/组件的slot 属性值要与后端返回的表单/组...
    99+
    2024-04-02
  • vue实现动态表单动态渲染组件的方式(1)
    vue 实现动态表单/动态渲染组件的方式(一),供大家参考,具体内容如下 思路 先写好各个可能会出现的表单或者自定义的组件,引入。此时后端可能会给到一个对象型数组,每个对象有要渲染组...
    99+
    2024-04-02
  • Java 动态代理的多种实现方式
    目录一、动态代理简介二、动态代理的多种实现 1. 基于JDK的实现 2. 基于cglib的实现 三、为什么要有基于cglib的实现 四、两种方式的适用场景JDK动态代理 优点 缺点 ...
    99+
    2024-04-02
  • MySQL中的常用树形结构设计总结
    目录常用树形结构设计总结1. 递归表2.路径枚举3.数据与关系分开存mysql树形结构(多级菜单)查询设计方案三级查询(层级固定,层级数少)多级查询(层级不固定/层级很深)总结常用树形结构设计总结 开发中,经常会遇到树形...
    99+
    2023-03-03
    MySQL树形结构 常用树形结构 树形结构设计
  • Golang中如何动态调用结构体的所有方法?
    哈喽!今天心血来潮给大家带来了《Golang中如何动态调用结构体的所有方法?》,想必大家应该对Golang都不陌生吧,那么阅读本文就都不会很困难,以下内容主要涉及到,若是你正在学习Golang,千万...
    99+
    2024-04-05
  • golang调用c语言动态库方式实现
    下面我们自己在 Linux 下做一个动态库(.so 文件 - Shared Object),然在用 Go 来使用它。本文所用的操作系统为 Ubuntu18.04, 以 gcc 作为编...
    99+
    2024-04-02
  • Java动态代理常用方式有哪些
    本篇内容介绍了“Java动态代理常用方式有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是动态代理?动态代理就是,在程序运行期,创建...
    99+
    2023-06-25
  • mysql复制表的几种常用方式总结
    目录mysql复制表的几种方式 1、复制表结构及数据到新表2、只复制表结构到新表3、复制旧表的数据到新表(假设两个表结构一样)4、复制旧表的数据到新表(假设两个表结构不一样)5、可以将表1结构复制到表26、可以...
    99+
    2023-04-10
    mysql复制表语句 mysql如何复制表 mysql数据表复制
  • Vue动态绑定Class的常用方式有哪些
    本篇内容主要讲解“Vue动态绑定Class的常用方式有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue动态绑定Class的常用方式有哪些”吧!第一种:基础用法Html部分:<div...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作