当前位置: 首页 > 编程日记 > 正文

Memcached深度分析

Memcached是danga.com(运营LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载, 提升性能。关于这个东西,相信很多人都用过,本文意在通过对memcached的实现及代码分析,获得对这个出色的开源软件更深入的了解,并可以根据我们 的需要对其进行更进一步的优化。末了将通过对BSM_Memcache扩展的分析,加深对memcached的使用方式理解。 本文的部分内容可能需要比较好的数学基础作为辅助。 ◎Memcached是什么 在阐述这个问题之前,我们首先要清楚它“不是什么”。很多人把它当作和SharedMemory那种形式的存储载体来使用,虽然memcached使用了 同样的“Key=>Value”方式组织数据,但是它和共享内存、APC等本地缓存有非常大的区别。Memcached是分布式的,也就是说它不是 本地的。它基于网络连接(当然它也可以使用localhost)方式完成服务,本身它是一个独立于应用的程序或守护进程(Daemon方式)。 Memcached使用libevent库实现网络连接服务,理论上可以处理无限多的连接,但是它和Apache不同,它更多的时候是面向稳定的持续连接 的,所以它实际的并发能力是有限制的。在保守情况下memcached的最大同时连接数为200,这和Linux线程能力有关系,这个数值是可以调整的。 关于libevent可以参考相关文档。 Memcached内存使用方式也和APC不同。APC是基于共享内存和MMAP的,memcachd有自己的内存分配算法和管理方式,它和共享内存没有 关系,也没有共享内存的限制,通常情况下,每个memcached进程可以管理2GB的内存空间,如果需要更多的空间,可以增加进程数。 ◎Memcached适合什么场合 在很多时候,memcached都被滥用了,这当然少不了对它的抱怨。我经常在论坛上看见有人发贴,类似于“如何提高效率”,回复是“用memcached”,至于怎么用,用在哪里,用来干什么一句没有。memcached不是万能的,它也不是适用在所有场合。 Memcached是“分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不需要共享的,或者干脆规模小到只有一台服务器的应用, memcached不会带来任何好处,相反还会拖慢系统效率,因为网络连接同样需要资源,即使是UNIX本地连接也一样。 在我之前的测试数据中显示,memcached本地读写速度要比直接PHP内存数组慢几十倍,而APC、共享内存方式都和直接数组差不多。可见,如果只是 本地级缓存,使用memcached是非常不划算的。 Memcached在很多时候都是作为数据库前端cache使用的。因为它比数据库少了很多SQL解析、磁盘操作等开销,而且它是使用内存来管理数据的, 所以它可以提供比直接读取数据库更好的性能,在大型系统中,访问同样的数据是很频繁的,memcached可以大大降低数据库压力,使系统执行效率提升。 另外,memcached也经常作为服务器之间数据共享的存储媒介,例如在SSO系统中保存系统单点登陆状态的数据就可以保存在memcached中,被 多个应用共享。 需要注意的是,memcached使用内存管理数据,所以它是易失的,当服务器重启,或者memcached进程中止,数据便会丢失,所以 memcached不能用来持久保存数据。很多人的错误理解,memcached的性能非常好,好到了内存和硬盘的对比程度,其实memcached使用 内存并不会得到成百上千的读写速度提高,它的实际瓶颈在于网络连接,它和使用磁盘的数据库系统相比,好处在于它本身非常“轻”,因为没有过多的开销和直接 的读写方式,它可以轻松应付非常大的数据交换量,所以经常会出现两条千兆网络带宽都满负荷了,memcached进程本身并不占用多少CPU资源的情况。 ◎Memcached的工作方式 以下的部分中,读者最好能准备一份memcached的源代码。 Memcached是传统的网络服务程序,如果启动的时候使用了-d参数,它会以守护进程的方式执行。创建守护进程由daemon.c完成,这个程序只有一个daemon函数,这个函数很简单(如无特殊说明,代码以1.2.1为准):
CODE:
#include <fcntl.h> #include <stdlib.h> #include <unistd.h> int daemon(nochdir, noclose) int nochdir, noclose; { int fd; switch (fork()) { case -1: return (-1); case 0: break; default: _exit(0); } if (setsid() == -1) return (-1); if (!nochdir) (void)chdir("/"); if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > STDERR_FILENO) (void)close(fd); } return (0); }
这个函数 fork 了整个进程之后,父进程就退出,接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设备, daemon 就建立成功了。 Memcached 本身的启动过程,在 memcached.c 的 main 函数中顺序如下: 1 、调用 settings_init() 设定初始化参数 2 、从启动命令中读取参数来设置 setting 值 3 、设定 LIMIT 参数 4 、开始网络 socket 监听(如果非 socketpath 存在)( 1.2 之后支持 UDP 方式) 5 、检查用户身份( Memcached 不允许 root 身份启动) 6 、如果有 socketpath 存在,开启 UNIX 本地连接(Sock 管道) 7 、如果以 -d 方式启动,创建守护进程(如上调用 daemon 函数) 8 、初始化 item 、 event 、状态信息、 hash 、连接、 slab 9 、如设置中 managed 生效,创建 bucket 数组 10 、检查是否需要锁定内存页 11 、初始化信号、连接、删除队列 12 、如果 daemon 方式,处理进程 ID 13 、event 开始,启动过程结束, main 函数进入循环。 在 daemon 方式中,因为 stderr 已经被定向到黑洞,所以不会反馈执行中的可见错误信息。 memcached.c 的主循环函数是 drive_machine ,传入参数是指向当前的连接的结构指针,根据 state 成员的状态来决定动作。 Memcached 使用一套自定义的协议完成数据交换,它的 protocol 文档可以参考: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt 在API中,换行符号统一为/r/n ◎Memcached的内存管理方式 Memcached有一个很有特色的内存管理方式,为了提高效率,它使用预申请和分组的方式管理内存空间,而并不是每次需要写入数据的时候去malloc,删除数据的时候free一个指针。Memcached使用slab->chunk的组织方式管理内存。 1.1和1.2的slabs.c中的slab空间划分算法有一些不同,后面会分别介绍。 Slab可以理解为一个内存块,一个slab是memcached一次申请内存的最小单位,在memcached中,一个slab的大小默认为 1048576字节(1MB),所以memcached都是整MB的使用内存。每一个slab被划分为若干个chunk,每个chunk里保存一个 item,每个item同时包含了item结构体、key和value(注意在memcached中的value是只有字符串的)。slab按照自己的 id分别组成链表,这些链表又按id挂在一个slabclass数组上,整个结构看起来有点像二维数组。slabclass的长度在1.1中是21,在 1.2中是200。 slab有一个初始chunk大小,1.1中是1字节,1.2中是80字节,1.2中有一个factor值,默认为1.25 在1.1中,chunk大小表示为初始大小*2^n,n为classid,即:id为0的slab,每chunk大小1字节,id为1的slab,每 chunk大小2字节,id为2的slab,每chunk大小4字节……id为20的slab,每chunk大小为1MB,就是说id为20的slab里 只有一个chunk:
CODE:
void slabs_init(size_t limit) { int i; int size=1; mem_limit = limit; for(i=0; i<=POWER_LARGEST; i++, size*=2) { slabclass[i].size = size; slabclass[i].perslab = POWER_BLOCK / size; slabclass[i].slots = 0; slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0; slabclass[i].end_page_ptr = 0; slabclass[i].end_page_free = 0; slabclass[i].slab_list = 0; slabclass[i].list_size = 0; slabclass[i].killing = 0; } /* for the test suite: faking of how much we've already malloc'd */ { char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC"); if (t_initial_malloc) { mem_malloced = atol(getenv("T_MEMD_INITIAL_MALLOC")); } } /* pre-allocate slabs by default, unless the environment variable for testing is set to something non-zero */ { char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC"); if (!pre_alloc || atoi(pre_alloc)) { slabs_preallocate(limit / POWER_BLOCK); } } }
在1.2 中,chunk大小表示为初始大小*f^n,f为factor,在memcached.c中定义,n为classid,同时,201个头不是全部都要初始 化的,因为factor可变,初始化只循环到计算出的大小达到slab大小的一半为止,而且它是从id1开始的,即:id为1的slab,每chunk大 小80字节,id为2的slab,每chunk大小80*f,id为3的slab,每chunk大小80*f^2,初始化大小有一个修正值 CHUNK_ALIGN_BYTES,用来保证n-byte排列 (保证结果是CHUNK_ALIGN_BYTES的整倍数)。这样,在标准情况下,memcached1.2会初始化到id40,这个slab中每个 chunk大小为504692,每个slab中有两个chunk。最后,slab_init函数会在最后补足一个id41,它是整块的,也就是这个 slab中只有一个1MB大的chunk:
CODE:
void slabs_init(size_t limit, double factor) { int i = POWER_SMALLEST - 1; unsigned int size = sizeof(item) + settings.chunk_size; /* Factor of 2.0 means use the default memcached behavior */ if (factor == 2.0 && size < 128) size = 128; mem_limit = limit; memset(slabclass, 0, sizeof(slabclass)); while (++i < POWER_LARGEST && size <= POWER_BLOCK / 2) { /* Make sure items are always n-byte aligned */ if (size % CHUNK_ALIGN_BYTES) size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); slabclass[i].size = size; slabclass[i].perslab = POWER_BLOCK / slabclass[i].size; size *= factor; if (settings.verbose > 1) { fprintf(stderr, "slab class %3d: chunk size %6d perslab %5d/n", i, slabclass[i].size, slabclass[i].perslab); } } power_largest = i; slabclass[power_largest].size = POWER_BLOCK; slabclass[power_largest].perslab = 1; /* for the test suite: faking of how much we've already malloc'd */ { char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC"); if (t_initial_malloc) { mem_malloced = atol(getenv("T_MEMD_INITIAL_MALLOC")); } } #ifndef DONT_PREALLOC_SLABS { char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC"); if (!pre_alloc || atoi(pre_alloc)) { slabs_preallocate(limit / POWER_BLOCK); } } #endif }
由上可以看出,memcached的内存分配是有冗余的,当一个slab不能被它所拥有的chunk大小整除时,slab尾部剩余的空间就被丢弃了,如id40中,两个chunk占用了1009384字节,这个slab一共有1MB,那么就有39192字节被浪费了。 Memcached使用这种方式来分配内存,是为了可以快速的通过item长度定位出slab的classid,有一点类似hash,因为item的长度 是可以计算的,比如一个item的长度是300字节,在1.2中就可以得到它应该保存在id7的slab中,因为按照上面的计算方法,id6的chunk 大小是252字节,id7的chunk大小是316字节,id8的chunk大小是396字节,表示所有252到316字节的item都应该保存在id7 中。同理,在1.1中,也可以计算得到它出于256和512之间,应该放在chunk_size为512的id9中(32位系统)。 Memcached初始化的时候,会初始化slab(前面可以看到,在main函数中调用了slabs_init())。它会在slabs_init() 中检查一个常量DONT_PREALLOC_SLABS,如果这个没有被定义,说明使用预分配内存方式初始化slab,这样在所有已经定义过的 slabclass中,每一个id创建一个slab。这样就表示,1.2在默认的环境中启动进程后要分配41MB的slab空间,在这个过程里, memcached的第二个内存冗余发生了,因为有可能一个id根本没有被使用过,但是它也默认申请了一个slab,每个slab会用掉1MB内存 当一个slab用光后,又有新的item要插入这个id,那么它就会重新申请新的slab,申请新的slab时,对应id的slab链表就要增长,这个链表是成倍增长的,在函数grow_slab_list函数中,这个链的长度从1变成2,从2变成4,从4变成8……:
CODE:
static int grow_slab_list (unsigned int id) { slabclass_t *p = &slabclass[id]; if (p->slabs == p->list_size) { size_t new_size = p->list_size ? p->list_size * 2 : 16; void *new_list = realloc(p->slab_list, new_size*sizeof(void*)); if (new_list == 0) return 0; p->list_size = new_size; p->slab_list = new_list; } return 1; }
在 定位item时,都是使用slabs_clsid函数,传入参数为item大小,返回值为classid,由这个过程可以看出,memcached的第三 个内存冗余发生在保存item的过程中,item总是小于或等于chunk大小的,当item小于chunk大小时,就又发生了空间浪费。 ◎Memcached的NewHash算法 Memcached的item保存基于一个大的hash表,它的实际地址就是slab中的chunk偏移,但是它的定位是依靠对key做hash的结果, 在primary_hashtable中找到的。在assoc.c和items.c中定义了所有的hash和item操作。 Memcached使用了一个叫做NewHash的算法,它的效果很好,效率也很高。1.1和1.2的NewHash有一些不同,主要的实现方式还是一样的,1.2的hash函数是经过整理优化的,适应性更好一些。 NewHash的原型参考:http://burtleburtle.net/bob/hash/evahash.html。数学家总是有点奇怪,呵呵~ 为了变换方便,定义了u4和u1两种数据类型,u4就是无符号的长整形,u1就是无符号char(0-255)。 具体代码可以参考1.1和1.2源码包。 注意这里的hashtable长度,1.1和1.2也是有区别的,1.1中定义了HASHPOWER常量为20,hashtable表长为 hashsize(HASHPOWER),就是4MB(hashsize是一个宏,表示1右移n位),1.2中是变量16,即hashtable表长 65536:
CODE:
typedef unsigned long int ub4; /* unsigned 4-byte quantities */ typedef unsigned char ub1; /* unsigned 1-byte quantities */ #define hashsize(n) ((ub4)1<<(n)) #define hashmask(n) (hashsize(n)-1)
在assoc_init ()中,会对primary_hashtable做初始化,对应的hash操作包括:assoc_find()、assoc_expand()、 assoc_move_next_bucket()、assoc_insert()、assoc_delete(),对应于item的读写操作。其中 assoc_find()是根据key和key长寻找对应的item地址的函数(注意在C中,很多时候都是同时直接传入字符串和字符串长度,而不是在函数 内部做strlen),返回的是item结构指针,它的数据地址在slab中的某个chunk上。 items.c是数据项的操作程序,每一个完整的item包括几个部分,在item_make_header()中定义为: key:键 nkey:键长 flags:用户定义的flag(其实这个flag在memcached中没有启用) nbytes:值长(包括换行符号/r/n) suffix:后缀Buffer nsuffix:后缀长 一个完整的item长度是键长+值长+后缀长+item结构大小(32字节),item操作就是根据这个长度来计算slab的classid的。 hashtable中的每一个桶上挂着一个双链表,item_init()的时候已经初始化了heads、tails、sizes三个数组为0,这三个数 组的大小都为常量LARGEST_ID(默认为255,这个值需要配合factor来修改),在每次item_assoc()的时候,它会首先尝试从 slab中获取一块空闲的chunk,如果没有可用的chunk,会在链表中扫描50次,以得到一个被LRU踢掉的item,将它unlink,然后将需 要插入的item插入链表中。 注意item的refcount成员。item被unlink之后只是从链表上摘掉,不是立刻就被free的,只是将它放到删除队列中(item_unlink_q()函数)。 item对应一些读写操作,包括remove、update、replace,当然最重要的就是alloc操作。 item还有一个特性就是它有过期时间,这是memcached的一个很有用的特性,很多应用都是依赖于memcached的item过期,比如 session存储、操作锁等。item_flush_expired()函数就是扫描表中的item,对过期的item执行unlink操作,当然这只 是一个回收动作,实际上在get的时候还要进行时间判断:
CODE:
/* expires items that are more recent than the oldest_live setting. */ void item_flush_expired() { int i; item *iter, *next; if (! settings.oldest_live) return; for (i = 0; i < LARGEST_ID; i++) { /* The LRU is sorted in decreasing time order, and an item's timestamp * is never newer than its last access time, so we only need to walk * back until we hit an item older than the oldest_live time. * The oldest_live checking will auto-expire the remaining items. */ for (iter = heads[i]; iter != NULL; iter = next) { if (iter->time >= settings.oldest_live) { next = iter->next; if ((iter->it_flags & ITEM_SLABBED) == 0) { item_unlink(iter); } } else { /* We've hit the first old item. Continue to the next queue. */ break; } } } }
CODE:
/* wrapper around assoc_find which does the lazy expiration/deletion logic */ item *get_item_notedeleted(char *key, size_t nkey, int *delete_locked) { item *it = assoc_find(key, nkey); if (delete_locked) *delete_locked = 0; if (it && (it->it_flags & ITEM_DELETED)) { /* it's flagged as delete-locked. let's see if that condition is past due, and the 5-second delete_timer just hasn't gotten to it yet... */ if (! item_delete_lock_over(it)) { if (delete_locked) *delete_locked = 1; it = 0; } } if (it && settings.oldest_live && settings.oldest_live <= current_time && it->time <= settings.oldest_live) { item_unlink(it); it = 0; } if (it && it->exptime && it->exptime <= current_time) { item_unlink(it); it = 0; } return it; }
Memcached的内存管理方式是非常精巧和高效的,它很大程度上减少了直接alloc系统内存的次数,降低函数开销和内存碎片产生几率,虽然这种方式会造成一些冗余浪费,但是这种浪费在大型系统应用中是微不足道的。

相关文章:

【分享】 IT囧事

导读&#xff1a;企业的业务发展离不开信息化建设&#xff0c;信息系统的稳定运行更离不开IT运维的支持&#xff0c;许多生命期短暂或者使用效果不太好的IT系统&#xff0c;都是因为后期的维护和支持不到位&#xff0c;才导致前期投入的资金和人力付之东流&#xff0c;让人扼腕…

为什么掌握Linux对程序员这么重要……

人工智能、物联网、大数据时代&#xff0c;Linux正有着一统天下的趋势&#xff0c;几乎每个软件工程师岗位&#xff0c;都要求掌握Linux。可以说&#xff0c;打开 Linux 操作系统这扇门&#xff0c;你才是合格的软件工程师。如果不能熟练地操作 Linux&#xff0c;你基本上等于少…

Java并发编程有多难?这几个核心技术你掌握了吗?

本文主要内容索引 1、Java线程 2、线程模型 3、Java线程池 4、Future(各种Future) 5、Fork/Join框架 6、volatile 7、CAS&#xff08;原子操作&#xff09; 8、AQS&#xff08;并发同步框架&#xff09; 9、synchronized&#xff08;同步锁&#xff09; 10、并发队列&#xff0…

这届 AI 预测欧洲杯冠军,通通被打脸

持续了一个月的欧洲杯&#xff0c;终于落下帷幕。北京时间 7 月 12 日&#xff08;周一&#xff09;凌晨&#xff0c;本届欧洲杯决赛中&#xff0c;意大利对阵英格兰。两队在 120 分钟时间里 1-1 战平&#xff0c;意大利在欧洲杯中通过点球大战以 3:2击败英格兰夺冠。意大利上次…

资源的正确引用

对资源的引用应该发生在对资源的保护期间。 比如在所保护内hold住资源、local_bh_disable内hold住资源&#xff1b; 否则对资源的使用可能发生不一致的情况。 PS&#xff1a; 代码逻辑应该符合真实世界的合理逻辑。转载于:https://www.cnblogs.com/kernel521/p/4045976.html

给网站管理员的建议:创建可利用的、可抓取的网站

转载自 谷歌中文网站管理员博客 发表者 T.V. Raman&#xff0c;研究学者 原文&#xff1a; Webmaster tips for creating accessible, crawlable sites 发表于&#xff1a;2008年4月14日 上午10:47 Hubbell和我正在我们位于加州的家中度假。欢迎您随时 阅读在此之前我为网站管…

iptables如何开放被动模式的FTP服务

如何开放被动模式的FTP服务? 1.装载FTP追踪时的专用的模块; # modprobe nf_conntrack_ftp # lsmod | grep ftp 2.放行请求报文 命令连接&#xff1a; NEW&#xff0c;ESTABLISHED 数据连接&#xff1a; RELATED&#xff0c;ESTABLISHED #iptables -I INPUT -d 192.168.141.10 …

KNN 分类算法原理代码解析

作者 | Charmve来源 | 迈微AI研习社k-最近邻算法是基于实例的学习方法中最基本的&#xff0c;先介绍基x于实例学习的相关概念。基于实例的学习已知一系列的训练样例&#xff0c;很多学习方法为目标函数建立起明确的一般化描述&#xff1b;但与此不同&#xff0c;基于实例的学习…

Roadsend PHP-开源的PHP代码编译器

Roadsend PHP 是一个开源的php compiler, 可以将你的PHP代码编译成原生的二进制代码, 无需分发php源码. Roadsend 可以将你的PHP web项目编译成FastCGI的可执行文件,这样apache,nginx可以通过fastcgi方式和编译后的 程序进行通讯. 看起来,PHP 编写的程序可以和C编写的程序有同…

Android安卓开发中图片缩放讲解

安卓开发中应用到图片的处理时候&#xff0c;我们通常会怎么缩放操作呢&#xff0c;来看下面的两种做法&#xff1a; 方法1&#xff1a;按固定比例进行缩放 在开发一些软件&#xff0c;如新闻客户端&#xff0c;很多时候要显示图片的缩略图&#xff0c;由于手机屏幕限制&#x…

a标签是什么意思 怎么使用?

转自&#xff1a;https://www.imooc.com/qadetail/190881 (1) a标签的作用&#xff1a;超链接&#xff0c;用于跳转到别的网页。 (2) a标签的用法&#xff1a;<a href"网址" target"_blank" >到百度首页</a> 其中target是a标签的一个属性&…

【Java】Lucene检索引擎详解

基于Java的全文索引/检索引擎——Lucene Lucene不是一个完整的全文索引应用&#xff0c;而是是一个用Java写的全文索引引擎工具包&#xff0c;它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。 Lucene的作者&#xff1a;Lucene的贡献者Doug Cutting是一位资深全…

大手笔 !Julia Computing 获 2400 万美元融资,前 Snowflake CEO 加入董事会

整理 | 梦依丹出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;2021 年 7 月 19 日&#xff0c;由 Julia 高性能编程语言创始人成立的 Julia Computing 公司完成了 2400 万美元的 A 轮融资&#xff0c;这笔融资由 Dorilton Ventures 领投&#xff0c;Menlo Ventures…

(Mirage系列之六)在Mirage里使用Collection

在Mirage中&#xff0c;Collection是包含一个或多个CVD的集合。 Collection的主要作用是简化操作。比如我有一百个终端设备需要分配基础层&#xff0c;如果没有Collection&#xff0c;那么管理员需要逐个点击。这是无趣的重复劳动。有了Collection&#xff0c;只要选择一个Coll…

WAS服务器负载测试软件导读

转帖&#xff1a;出处未知 你的Web服务器和应用到底能够支持多少并发用户访问&#xff1f;在出现大量并发请求的情况下&#xff0c;软件会出现问题吗&#xff1f;这些问题靠通常的测试手段是无法解答的。本文介绍了Microsoft为这个目的而提供的免费工具WAS及其用法。另外&#…

深度学习实现场景字符识别模型|代码干货

作者|李秋键出品|AI科技大本营(ID:rgznai100)# 前言 #文字是人从日常交流中语音中演化出来&#xff0c;用来记录信息的重要工具。文字对于人类意义非凡&#xff0c;以中国为例&#xff0c;中国地大物博&#xff0c;各个地方的口音都不统一&#xff0c;但是人们使用同一套书写体…

SQL Server 一些重要视图3

1、 sys.dm_tran_locks; 为每一把锁返回一行、request_session_id 可以与sys.dm_tran_session_transactions \sys.dm_exec_connections相关联。 request_status 查看锁的分配情况 2、 sys.dm_os_waiting_tasks; 为每一个等待的任务返回一行、blocking_session_id标记是因为谁而…

Linux下将Mysql和Apache加入到系统服务里的方法

Apache加入到系统服务里面: cp /安装目录下/apache/bin/apachectl /etc/rc.d/init.d/httpd 修改httpd 在文件头部加入如下内容&#xff1a; ### # Comments to support chkconfig on RedHat Linux # chkconfig: 2345 90 90 # description:http server ### 保存 在打入 #chkconf…

int、bigint、smallint 和 tinyint

int、bigint、smallint 和 tinyint使用整数数据的精确数字数据类型。 bigint 从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 的整型数据&#xff08;所有数字&#xff09;。存储大小为 8 个字节。 int 从 -2^31 (-2,147,483,648) 到 2^31 - 1 (2,147,483,6…

阿里技术文档:Redis+Spring全家桶+Dubbo精选+高性能+高并发

最近花了很长的时间去搜罗整理Java核心技术好文&#xff0c;我把每个Java核心技术的优选文章都整理成了一个又一个的文档。今天就把这些东西分享给老铁们&#xff0c;也能为老铁们省去不少麻烦&#xff0c;想学什么技能了&#xff0c;遇到哪方面的问题了 直接打开文档学一学就好…

SDN:软件定义网络

近期高级网络课的小组任务是在老师给定的范围内自选方向主题研究并做展示报告。我们组选了sdn。原以为这东西会是工业界无人问津的概念化产品&#xff0c;Google了一下却发现事实上sdn挺火的&#xff0c;因为它可能带来的可扩展性&#xff0c;一些大互联网企业也在開始涉足相关…

Linux之文本处理

1 cut&#xff1a;按某种方式对文件进行分割然后输出 选项&#xff1a;-b 按字节选取 -d 自定义分隔符 -f 和-d一起使用&#xff0c;指定哪个区域或字段 [rootlocalhost ~]# cat /etc/passwd | cut -d : -f 1 #以&#xff1a;为分隔符&#xff0c;打印第一个字段 […

Yahoo javascript 开源界面库YUI 和EXT

清清月儿整理 [yui][译]Yahoo!User Interface Libray 介绍 Yahoo! User Interface Library(简称yui) 是一个使用JavaScript编写的工具和控件库。它利用DOM脚本,DHTML和AJAX来构造具有丰富交互功能的Web程序。yui也包含几个核心的CSS文件。yui中的所有组件已经以开源的形式发布…

芯片开发语言:Verilog 在左,Chisel 在右

来源 | 老石谈芯在最近召开的RISC-V中国峰会上&#xff0c;中科院计算所的包云岗研究员团队正式发布了名为“香山”的开源高性能处RISC-V处理器。前不久我有幸和包老师就这个事情做了一次深度的交流&#xff0c;我们聊了关于RISC-V、还有“香山”处理器的前世今生。包老师也分享…

1.1GTK+ 的简单程序HelloWorld

1.1GTK 的简单程序HelloWorld 编译执行如图所看到的&#xff1a;

struts2 validate验证

转自&#xff1a;https://blog.csdn.net/houpengfei111/article/details/9038233 自定义拦截器 要自定义拦截器需要实现com.opensymphony.xwork2.interceptor.Interceptor接口&#xff1a; [java] view plaincopypublic class PermissionInterceptor implements Interceptor { …

PHP中文乱码

页面顶端加 <?php header("content-Type: text/html; charsetgbk"); ?>

收藏 | 提高数据处理效率的 Pandas 函数方法

作者&#xff1a;俊欣来源&#xff1a;关于数据分析与可视化前言大家好&#xff0c;这里是俊欣&#xff0c;今天和大家来分享几个Pandas方法可以有效地帮助我们在数据分析与数据清洗过程当中提高效率&#xff0c;加快工作的进程&#xff0c;希望大家看了之后会有收获。首先导入…

如何让两个div在同一行显示?一个float搞定

最近在学习div和css&#xff0c;遇到了一些问题也解决了很多以前以为很难搞定的问题。比如&#xff1a;如何让两个div显示在同一行呢&#xff1f;&#xff08;不是用table表格&#xff0c;table对SE不太友好&#xff09;其实&#xff0c;<div> 是一个块级元素&#xff0c…

使用sudo进入root权限,以及防止root密码被恶意篡改

一、前言 sudo是允许系统管理员让普通用户执行一些或者全部的root命令的一个工具&#xff0c;减少了root用户的登陆和管理时间&#xff0c;提高了安全性。Sudo不是对shell的一个代替&#xff0c;它是面向每个命令的。 而防止root用户密码被恶意更改&#xff0c;主要是用户基本上…