Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Modeler   Code  
»áÔ±   
 
   
 
 
     
   
 ¶©ÔÄ
  ¾èÖú
edisÄÚ²¿Êý¾Ý½á¹¹Ïê½âÖ®×Öµä(dict)
 
×÷Õߣºxkey À´Ô´£ºCSDN ·¢²¼ÓÚ£º2015-04-07
  2787  次浏览      27
 

±¾ÎÄËùÒýÓõÄÔ´ÂëÈ«²¿À´×ÔRedis2.8.2°æ±¾¡£

RedisÖÐ×ÖµädictÊý¾Ý½á¹¹ÓëAPIÏà¹ØÎļþÊÇ£ºdict.h, dict.c¡£

±¾ÎĽ²½âµÄ²»ÊǺÜÏêϸ£¬¿ÉÒÔͬʱ²Î¿¼RedisʵÏÖÓëÉè¼ÆÒ»ÊéÖÐ×ֵ䲿·Ö£¬±¾ÎĹØÓÚ×ÖµäµÄºËÐÄ´úÂëµÄ×¢ÊÍ¿ÉÒԲο¼¡£

×ֵ䣬¼òµ¥Ëµ¾ÍÊÇ´æ´¢key-value¼üÖµÊý¾Ý£¬µ±È»value=NULLÄÇô¾ÍÊǼ¯ºÏÁË¡£×ÖµäͨË×À´Ëµ¾ÍÊÇC++ STLÖеÄmap£¬STLÖеÄmapÊÇÓÃred-black treeʵÏֵģ¬ÒòΪmap²»½öÄܹ»±£Ö¤key²»Öظ´£¬¶øÇÒkey»¹Êǰ´ÕÕ×ÖµäÐò´æ´¢µÄ£¬¶øRedisÖеÄ×ֵ䲢²»ÒªÇóÓÐÐò£¬Òò´ËΪÁ˽µµÍ±àÂëµÄÄѶÈʹÓùþÏ£±í×÷Ϊ×ÖµäµÄµ×²ãʵÏÖ¡£RedisµÄ×ÖµäÊÇʹÓÃÒ»¸öͰbucket£¬Í¨¹ý¶Ôkey½øÐÐhashµÃµ½µÄË÷ÒýÖµindex£¬È»ºó½«key-valueµÄÊý¾Ý´æÔÚͰµÄindexλÖã¬Redis´¦ÀíhashÅöײµÄ·½Ê½ÊÇÁ´±í£¬Á½¸ö²»Í¬µÄkey hashµÃµ½ÏàͬµÄË÷ÒýÖµ£¬ÄÇô¾ÍʹÓÃÁ´±í½â¾ö³åÍ»¡£Ê¹ÓÃÁ´±í×ÔÈ»µ±´æ´¢µÄÊý¾Ý¾Þ´óµÄʱºò£¬×ֵ䲻Ãâ»áÍË»¯³É¶à¸öÁ´±í£¬Ð§ÂÊ´ó´ó½µµÍ£¬Redis²ÉÓÃrehashµÄ·½Ê½¶ÔͰ½øÐÐÀ©ÈÝÀ´½â¾öÕâÖÖÍË»¯¡£

RedisʹÓõÄhashËã·¨ÓÐÒÔÏÂÁ½ÖÖ£º

1. MurmurHash2 32 bit Ëã·¨£ºÕâÖÖËã·¨µÄ·Ö²¼ÂʺÍËٶȶ¼·Ç³£ºÃ£¬¾ßÌåÐÅÏ¢Çë²Î¿¼ MurmurHash µÄÖ÷Ò³£ºhttp://code.google.com/p/smhasher/ ¡£

2. »ù ÓÚ djb Ëã ·¨ ʵ ÏÖ µÄ Ò» ¸ö ´ó С д ÎÞ ¹Ø É¢ ÁÐ Ëã ·¨£º ¾ß Ìå ЊϢ Çë ²Î ¿¼
http://www.cse.yorku.ca/~oz/hash.html ¡£

×ÖµäÊý¾Ý½á¹¹

typedef struct dictEntry {//×ÖµäµÄ½Úµã  
void *key;
union {//ʹÓõÄÁªºÏÌå
void *val;
uint64_t u64;//ÕâÁ½¸ö²ÎÊýºÜÓÐÓÃ
int64_t s64;
} v;
struct dictEntry *next;//ÏÂÒ»¸ö½ÚµãÖ¸Õë
} dictEntry;

typedef struct dictType {
unsigned int (*hashFunction)(const void *key); //hashº¯ÊýÖ¸Õë
void *(*keyDup)(void *privdata, const void *key); //¼ü¸´Öƺ¯ÊýÖ¸Õë
void *(*valDup)(void *privdata, const void *obj); //Öµ¸´Öƺ¯ÊýÖ¸Õë
int (*keyCompare)(void *privdata, const void *key1, const void *key2); //¼ü±È½Ïº¯ÊýÖ¸Õë
void (*keyDestructor)(void *privdata, void *key); //¼ü¹¹Ô캯ÊýÖ¸Õë
void (*valDestructor)(void *privdata, void *obj); //Öµ¹¹Ô캯ÊýÖ¸Õë
} dictType;

/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht { //×Öµähash table
dictEntry **table;//¿ÉÒÔ¿´×ö×ÖµäÊý×飬Ë׳ÆÍ°bucket
unsigned long size; //Ö¸ÕëÊý×éµÄ´óС£¬¼´Í°µÄ²ãÊý
unsigned long sizemask;
unsigned long used; //×ÖµäÖе±Ç°µÄ½ÚµãÊýÄ¿
} dictht;

typedef struct dict {
dictType *type;
void *privdata; //˽ÓÐÊý¾Ý
dictht ht[2]; //Á½¸öhash table
int rehashidx; /* rehashing not in progress if rehashidx == -1 */ //rehash Ë÷Òý
int iterators; /* number of iterators currently running */ //µ±Ç°¸Ã×Öµäµü´úÆ÷¸öÊý
} dict;

dictÊý¾Ý½á¹¹ÖÐÉùÃ÷ÁËÁ½¸ö×Öµähashtable½á¹¹dictht£¬ht[1]ÔÚrehashʱºòʹÓ㬺óÃæ¾ßÌå·ÖÎö¡£

ÏÂͼ¸ø³öÕû¸ö×Öµä½á¹¹£¬Í¼Æ¬À´×ÔRedisÉè¼ÆÓëʵÏÖÒ»Ê飺

ÉÏͼht[1]Ϊ¿Õ£¬ËµÃ÷µ±È»×ÖµäûÔÚRehash״̬¡£

×ÖµäµÄAPIº¯Êý

´´½¨ÐÂ×Öµä

ͨ¹ýdictCreateº¯Êý´´½¨Ò»¸öÐÂ×Öµädict *dictCreate(dictType *type, void *privDataPtr)£¬Ò»¸ö¿Õ×ÖµäµÄʾÒâͼÈçÏÂ(ͼƬÀ´×ÔRedisÉè¼ÆÓëʵÏÖÒ»Êé):

ÉÏÃæÒѾ­ÌáÆð¹ý£¬ht[1]Ö»ÔÚRehashʱʹÓá£

×ÖµäÌí¼ÓÔªËØ

¸ù¾Ý×ֵ䵱ǰµÄ״̬£¬½«Ò»¸ökey-valueÔªËØÌí¼Óµ½×ÖµäÖпÉÄÜ»áÒýÆðһϵÁи´ÖƵIJÙ×÷£º

Èç¹û×Öµäδ³õʼ»¯£¨¼´×ÖµäµÄ0ºÅ¹þÏ£±íht[0]µÄtableΪ¿Õ£©£¬ÄÇôÐèÒªµ÷ÓÃdictExpandº¯Êý¶ÔËü³õʼ»¯£»

Èç¹û²åÈëµÄÔªËØkeyÒѾ­´æÔÚ£¬ÄÇôÌí¼ÓÔªËØÊ§°Ü£»

Èç¹û²åÈëÔªËØÊ±£¬ÒýÆðÅöײ£¬ÐèҪʹÓÃÁ´±íÀ´´¦ÀíÅöײ£»

Èç¹û²åÈëÔªËØÊ±£¬ÒýÆð³ÌÐòÂú×ãRehashµÄÌõ¼þʱ£¬Ïȵ÷ÓÃdictExpandº¯ÊýÀ©Õ¹¹þÏ£±íµÄsize£¬È»ºó×¼±¸½¥½øÊ½Rehash²Ù×÷¡£

×ÖµäÌí¼ÓÔªËØµÄÁ÷³Ìͼ£¬À´×ÔRedisÉè¼ÆÓëʵÏÖÒ»Êé

/* Expand or create the hash table */  
int dictExpand(dict *d, unsigned long size)
{
dictht n; /* the new hash table */
unsigned long realsize = _dictNextPower(size); //µÃµ½ÐèÒªÀ©Õ¹µ½µÄsize

/* the size is invalid if it is smaller than the number of
* elements already inside the hash table */
if (dictIsRehashing(d) || d->ht[0].used > size)
return DICT_ERR;

/* Allocate the new hash table and initialize all pointers to NULL */
n.size = realsize;
n.sizemask = realsize-1;
n.table = zcalloc(realsize * sizeof(dictEntry*));
n.used = 0;

/* Is this the first initialization? If so it's not really a rehashing
* we just set the first hash table so that it can accept keys. */
if (d->ht[0].table == NULL) {
d->ht[0] = n;
return DICT_OK;
}

/* Prepare a second hash table for incremental rehashing */
//×¼±¸½¥½øÊ½rehash£¬rehashµÄ×ÖµätableΪ0ºÅ
d->ht[1] = n;
d->rehashidx = 0;
return DICT_OK;
}

/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *d)
{
/* Incremental rehashing already in progress. Return. */
if (dictIsRehashing(d)) return DICT_OK;

// Èç¹û¹þÏ£±íΪ¿Õ£¬ÄÇô½«ËüÀ©Õ¹Îª³õʼ´óС
if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);

/*Èç¹û¹þÏ£±íµÄÒÑÓýڵãÊý >= ¹þÏ£±íµÄ´óС£¬²¢ÇÒÒÔÏÂÌõ¼þÈÎÒ»¸öÎªÕæ£º
1) dict_can_resize ÎªÕæ
2) ÒÑÓýڵãÊý³ýÒÔ¹þÏ£±í´óС֮±È´óÓÚ dict_force_resize_ratio
ÄÇôµ÷Óà dictExpand ¶Ô¹þÏ£±í½øÐÐÀ©Õ¹,À©Õ¹µÄÌå»ýÖÁÉÙΪÒÑʹÓýڵãÊýµÄÁ½±¶
*/
if (d->ht[0].used >= d->ht[0].size &&
(dict_can_resize ||
d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
{
return dictExpand(d, d->ht[0].used*2);
}
return DICT_OK;
}

static int _dictKeyIndex(dict *d, const void *key)
{
unsigned int h, idx, table;
dictEntry *he;

/* Expand the hash table if needed */
if (_dictExpandIfNeeded(d) == DICT_ERR)
return -1;
/* Compute the key hash value */
h = dictHashKey(d, key);//ͨ¹ýhashº¯ÊýµÃµ½keyËùÔÚµÄbucketË÷ÒýλÖÃ
//²éÕÒÔÚÏÖÓÐ×ÖµäÖÐÊÇ·ñ³öÏÖÁ˸Ãkey
for (table = 0; table <= 1; table++) {
idx = h & d->ht[table].sizemask;
/* Search if this slot does not already contain the given key */
he = d->ht[table].table[idx];
while(he) {
if (dictCompareKeys(d, key, he->key))
return -1;
he = he->next;
}
//Èç¹ûϵͳûÔÚrehashÔò²»ÐèÒª²éÕÒht[1]
if (!dictIsRehashing(d)) break;
}
return idx;
}

dictEntry *dictAddRaw(dict *d, void *key)
{
int index;
dictEntry *entry;
dictht *ht;

if (dictIsRehashing(d)) _dictRehashStep(d);// ³¢ÊÔ½¥½øÊ½µØ rehash ͰÖÐÒ»×éÔªËØ

/* Get the index of the new element, or -1 if
* the element already exists. */
// ²éÕÒ¿ÉÈÝÄÉÐÂÔªËØµÄË÷ÒýλÖÃ,Èç¹ûÔªËØÒÑ´æÔÚ£¬ index Ϊ -1
if ((index = _dictKeyIndex(d, key)) == -1)
return NULL;

/* Allocate the memory and store the new entry */
ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
// ¾ö¶¨¸Ã°ÑÐÂÔªËØ·ÅÔÚÄǸö¹þÏ£±í
entry = zmalloc(sizeof(*entry));
//Í·²å·¨£¬²åÈë½Úµã
entry->next = ht->table[index];
ht->table[index] = entry;
ht->used++;

/* Set the hash entry fields. */
dictSetKey(d, entry, key);//¹ØÁªÆðkey
return entry;
}

/* Add an element to the target hash table */
//Ìí¼ÓÒ»¸öÔªËØ
int dictAdd(dict *d, void *key, void *val)
{
dictEntry *entry = dictAddRaw(d,key);

if (!entry) return DICT_ERR;
dictSetVal(d, entry, val);//¹ØÁªÆðvalue
return DICT_OK;
}

×ÖµäRehash½âÎö

RehashµÄ´¥·¢»úÖÆ£ºµ±Ã¿´ÎÌí¼ÓÐÂÔªËØÊ±£¬¶¼»á¶Ô¹¤×÷¹þÏ£±íht[0]½øÐмì²é£¬Èç¹ûused£¨¹þÏ£±íÖÐÔªËØµÄÊýÄ¿£©Óësize£¨Í°µÄ´óС£©±ÈÂÊratioÂú×ãÒÔÏÂÈÎÒ»Ìõ¼þ£¬½«¼¤»î×ÖµäµÄRehash»úÖÆ£ºratio=used / size£¬ ratio >= 1²¢ÇÒdict_can_resize ÎªÕæ£»ratio ´ó ÓÚ ±ä Á¿ dict_force_resize_ratio ¡£

RehashÖ´Ðйý³Ì£º

´´½¨Ò»¸ö±Èht[0].usedÖÁÉÙÁ½±¶µÄht[1].table£»½«Ô­ht[0].tableÖÐËùÓÐÔªËØÇ¨ÒÆµ½ht[1].table£»Çå¿ÕÔ­À´ht[0]£¬½«ht[1]Ìæ»»³Éht[0]
½¥½øÊ½RehashÖ÷ÒªÓÉÁ½¸öº¯ÊýÀ´½øÐУº

_dictRehashStep:µ±¶Ô×ֵ佸ÐÐÌí¼Ó¡¢²éÕÒ¡¢É¾³ý¡¢Ëæ»ú»ñÈ¡ÔªËØ¶¼»áÖ´ÐÐÒ»´Î£¬Æäÿ´ÎÔÚ¿ªÊ¼Rehashºó£¬½«ht[0].tableµÄµÚÒ»¸ö²»Îª¿ÕµÄË÷ÒýÉϵÄËùÓнڵãÈ«²¿Ç¨ÒƵ½ht[1].table;

dictRehashMilliseconds:ÓÉRedis·þÎñÆ÷³£¹æÈÎÎñ³ÌÐò(serverCron)Ö´ÐУ¬ÒÔºÁÃëΪµ¥Î»£¬ÔÚÒ»¶¨Ê±¼äÄÚ£¬ÒÔÿ´ÎÖ´ÐÐ100²½rehash²Ù×÷¡£

Rehash²Ù×÷ºËÐĺ¯Êý£º

int dictRehash(dict *d, int n) {  
if (!dictIsRehashing(d)) return 0;

while(n--) {
dictEntry *de, *nextde;

/* Check if we already rehashed the whole table... */
if (d->ht[0].used == 0) {//ÒѾ­Íê³É
zfree(d->ht[0].table);//ÊÍ·Åht[0].table
d->ht[0] = d->ht[1]; //ÕâÀïht[0]Óëht[1]¶¼²»ÊÇÖ¸Õ룬ֱ½Ó¸³Öµ¾ÍÌæ»»ÁË
_dictReset(&d->ht[1]);//½«ht[1].tableÉèÖÃΪnull
d->rehashidx = -1;
return 0;
}

/* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned)d->rehashidx);
//ÕÒµ½µÚÒ»¸ö²»Îª¿ÕµÄÊý×é
while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;
//Ö¸Ïò¸ÃÁ´±íÍ·
de = d->ht[0].table[d->rehashidx];
/* Move all the keys in this bucket from the old to the new hash HT */
while(de) {//±éÀúÁ´±í
unsigned int h;

nextde = de->next;
/* Get the index in the new hash table */
//µÃµ½ÔÚht[1]ÖеÄË÷ÒýºÅ£¬Í¨¹ýÏàÓ¦µÄhashº¯Êý
h = dictHashKey(d, de->key) & d->ht[1].sizemask;

// Ìí¼Ó½Úµãµ½ ht[1] £¬µ÷ÕûÖ¸Õ룬²ÉÓõÄÊÇÍ·²å·¨
de->next = d->ht[1].table[h];
d->ht[1].table[h] = de;
d->ht[0].used--;
d->ht[1].used++;
de = nextde;
}
d->ht[0].table[d->rehashidx] = NULL;//ÉèÖÃΪ¿Õ
d->rehashidx++;
}
return 1;
}

С½á

RedisÖеÄ×ÖµäÊý¾Ý½á¹¹Ê¹ÓùþÏ£±íÀ´ÊµÏÖ£¬ÓÃÀ´´æ´¢key-value¼üÖµÔªËØ£»

×ÖµäʹÓÃÁ½¸ö¹þÏ£±í£¬Ò»°ãֻʹÓÃht[0]£¬Ö»Óе±Rehashʱºò²ÅʹÓÃht[0]£»

¹þÏ£±í²ÉÓÃÁ´±íµÄ·½Ê½½â¾ö¼üÅöײÎÊÌ⣻

RedisµÄRehash²Ù×÷Êǽ¥½øÊ½µÄ£¬·þÎñÆ÷³ÌÐò»áÖ÷¶¯Rehash£¬ÔÚ²éÕÒ¡¢Ìí¼Ó¡¢É¾³ýÔªËØµÈ²Ù×÷ʱҲ»áÔÚRehash½øÐÐʱִÐÐÒ»´Îrehash²Ù×÷¡£

×ÖµäµÄÄÚÈÝʵÔÚÌ«¶à£¬²Ù×÷±È½Ï·±Ëö£¬Ó¦¸ÃÊÇRedisÖÐ×ÔӵĵײãÊý¾Ý½á¹¹ÁË£¬±¾ÎÄ·ÖÎöµÄ¾ø¶Ô²»¹»ÉîÈ룬ϣÍûÒÔºóÓÐʱ¼äÔÙÐ޸İɣ¬ÔÝʱÏÈÕâÑù¡£µ½Ä¿Ç°ÎªÖ¹£¬RedisÁùÖÖÄÚ²¿Êý¾Ý½á¹¹£¬Í¬Ê±Ò²Êǵײã²Ù×÷µÄʵÏÖ½²½âÈ«²¿½áÊø£¬ºóÃæµÄÎÄÕ½«½øÈëÎåÖÖ»ù±¾Êý¾ÝÀàÐÍÖ¸ÁîµÄʵÏÖ£¬×Ö·û´®£¨String£©¡¢¹þÏ£±í£¨Hash£©¡¢ÁÐ±í£¨List£©¡¢¼¯ºÏ£¨Set£©¡¢ÓÐÐò¼¯ºÏ£¨Sorted Set£©µÄ¸÷ÖÖÖ¸ÁîµÄʵÏÖ¡£

ÎÒ×Ô¼º¶ÔRedis2.8.2Ô´ÂëµÄ×¢ÊÍ£¬ÓÐʱ¼äÕÒ¸ö»ú»á·Å³öÀ´¡£

×îºó¸Ðл»Æ½¡ºê£¨huangz1990£©µÄRedisÉè¼ÆÓëʵÏÖ¼°ÆäËû¶ÔRedis2.6Ô´ÂëµÄÏà¹Ø×¢ÊͶÔÎÒÔÚÑо¿Redis2.8Ô´Âë·½ÃæµÄ°ïÖú¡£

   
2787 ´Îä¯ÀÀ       27
Ïà¹ØÎÄÕÂ

»ùÓÚEAµÄÊý¾Ý¿â½¨Ä£
Êý¾ÝÁ÷½¨Ä££¨EAÖ¸ÄÏ£©
¡°Êý¾Ýºþ¡±£º¸ÅÄî¡¢ÌØÕ÷¡¢¼Ü¹¹Óë°¸Àý
ÔÚÏßÉ̳ÇÊý¾Ý¿âϵͳÉè¼Æ ˼·+Ч¹û
 
Ïà¹ØÎĵµ

GreenplumÊý¾Ý¿â»ù´¡Åàѵ
MySQL5.1ÐÔÄÜÓÅ»¯·½°¸
ijµçÉÌÊý¾ÝÖÐ̨¼Ü¹¹Êµ¼ù
MySQL¸ßÀ©Õ¹¼Ü¹¹Éè¼Æ
Ïà¹Ø¿Î³Ì

Êý¾ÝÖÎÀí¡¢Êý¾Ý¼Ü¹¹¼°Êý¾Ý±ê×¼
MongoDBʵս¿Î³Ì
²¢·¢¡¢´óÈÝÁ¿¡¢¸ßÐÔÄÜÊý¾Ý¿âÉè¼ÆÓëÓÅ»¯
PostgreSQLÊý¾Ý¿âʵսÅàѵ
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]

MySQLË÷Òý±³ºóµÄÊý¾Ý½á¹¹
MySQLÐÔÄܵ÷ÓÅÓë¼Ü¹¹Éè¼Æ
SQL ServerÊý¾Ý¿â±¸·ÝÓë»Ö¸´
ÈÃÊý¾Ý¿â·ÉÆðÀ´ 10´óDB2ÓÅ»¯
oracleµÄÁÙʱ±í¿Õ¼äдÂú´ÅÅÌ
Êý¾Ý¿âµÄ¿çƽ̨Éè¼Æ


²¢·¢¡¢´óÈÝÁ¿¡¢¸ßÐÔÄÜÊý¾Ý¿â
¸ß¼¶Êý¾Ý¿â¼Ü¹¹Éè¼ÆÊ¦
HadoopÔ­ÀíÓëʵ¼ù
Oracle Êý¾Ý²Ö¿â
Êý¾Ý²Ö¿âºÍÊý¾ÝÍÚ¾ò
OracleÊý¾Ý¿â¿ª·¢Óë¹ÜÀí


GE Çø¿éÁ´¼¼ÊõÓëʵÏÖÅàѵ
º½Ìì¿Æ¹¤Ä³×Ó¹«Ë¾ Nodejs¸ß¼¶Ó¦Óÿª·¢
ÖÐÊ¢Òæ»ª ׿Խ¹ÜÀíÕß±ØÐë¾ß±¸µÄÎåÏîÄÜÁ¦
ijÐÅÏ¢¼¼Êõ¹«Ë¾ PythonÅàѵ
ij²©²ÊITϵͳ³§ÉÌ Ò×ÓÃÐÔ²âÊÔÓëÆÀ¹À
ÖйúÓÊ´¢ÒøÐÐ ²âÊÔ³ÉÊì¶ÈÄ£Ðͼ¯³É(TMMI)
ÖÐÎïÔº ²úÆ·¾­ÀíÓë²úÆ·¹ÜÀí