Home >Backend Development >PHP Tutorial >The use of cache, cache use_PHP tutorial
Caching is a powerful tool to improve access speed. The main ones used in the work are memcache and redis. They are B/S software, similar to Apache installed on the computer during practice. It also listens to the port and can be directly typed on the client (such as operating Memcache through telnet on cmd). Use the corresponding command to access the value. During self-study, you can familiarize yourself with the native command in this way and see the effect. They reside in the memory, write the data into the memory after getting it (it will occupy a memory area after installing the software), set the expiration time of the data, and read it directly from the memory when used. Because it is memory, the access speed is improved by orders of magnitude. . lz Zeng Jin wrote a very simple backend, which does not require caching at all. When I saw the speed, I couldn’t help but roar: How can it be called a web page if it is so slow? ! But I was young and ignorant at that time, and I didn’t know how to use caching. Of course, it’s not much better now. I hope the front-end girl didn’t secretly curse in her heart at that time>38c8e323d422a5c75b9e39036a32421a3connect('127.0.0.1', 11211); or Use the addserver method: $mem->addServer('127.0.0.1', 11211); The latter is established by adding a Memcache server connection to the existing connection pool, with the same effect.
Redis, a powerful caching tool, has been continuously improved thanks to its wide use in domestic portals. It now has a stable version (it looks like the latest 64-bit release 2.8 version on Microsoft github, in my case It can't run on the working machine...), there are many more methods than memcache. As for development, it is mainly used for data access, while other things, communication protocols, and clusters are generally developed in the background, not for speed. Even if you pursue it to the extreme, it is still basically useless. Redis is divided into the following types according to operations:
1. String: It is also a key-value pair. It mainly performs get, set, length, auto-increment and other operations on the element corresponding to its key. Redis will store all added types as strings, even pure numbers;
2. Hash: Hash table (hash table) is a dictionary in the data structure, using a specific hash algorithm. For example, there are hundreds of people in the mobile phone address book, and the first letter of the last name is used as the key code to sort. , so that we can quickly find contacts by linking a letter to a name through a specific mapping method. In redis, when creating a hash table, first specify the table name, which table you want to store it in, and then specify a field (field, actually a variable) and its corresponding value. The cmd operation is as follows (in The hash table hash1 stores the value Jack corresponding to a domain username, and redis will automatically establish a mapping from username to Jack). Multiple domains and corresponding variables can be stored in a hash table;
3. List: List is similar to a linked list equivalent to a data structure. It can also implement stacks and queues. The previous string (the name is very weird) stores a one-to-one corresponding single key-value pair, and it stores Multiple values (without keys), the point is that as a list structure, it can insert and pop up variable values from the left and right (head and tail), find the length, and a series of operations, which is why it is called a list, below lpush . The rpush command inserts the variables hello and world from the left and right sides of the table;
4. Set: Set, as the name suggests, can also store multiple variable values (without keys), but it only conforms to the characteristics of mathematically defined sets. For example, the elements in the set are not repeated, so the key names of each data pair in the set are not Similarly, it can also perform operations such as union, intersection, and difference (the following command adds a value var to the set mySet);
5. SortedSet: An ordered set, which can be regarded as a special way of Set. In each SortedSet type set, when adding a value (without a key), a variable value called score must be assigned to them. Ordered, there must be a field to determine whether it is in order or not. This is it. The following command creates an ordered set stset, adds a value google.com, and its score is 10. In fact, you can guess that since it is called an ordered set and since the score is given, it is basically certain that this can be used to perform certain sorting operations, and this is also the case;
6. Key: Operate the key alone, of course it also affects the corresponding element value, such as viewing the data type of the element corresponding to the key, the survival time of the element corresponding to the key, resetting the survival time, returning all keys, regular form View related key names, delete keys (elements are also gone), etc. It is not a data type. In fact, Redis refers to Hash table names, List table names, and Set names as keys, and the entire set or table is its value. The following is the type command to see the type of value it stores. It stores a List list.
For the above commands, you can find a reference document and go through them, and you will be familiar with them quickly. I will only talk about them in general here. So how is cache usually used in actual projects? Let’s not talk about the big picture, such as Weibo’s cache design. This may involve things at the architectural level. Just say that as a backend with a certain amount of visits, you want to use caching to improve speed.
An important principle (which I encounter many times now) is to use the parameters passed in from to spell the key name , and use this key name to store and retrieve the value . For example, there is a method in the Model class: getUserInfoByUid($params), which passes in an array containing the uid field to query the user's information. Here, cache is used to get the cached data first. If it cannot be retrieved, check the database and then re-add it to the cache. , and finally return the result data, which is also a common process for using cache.
Taking Memcache as an example, first look at the code and define a Cache class:
<span>/*</span><span>* * Memcache缓存类 </span><span>*/</span> <span>define</span>('CACHE_HOST', '127.0.0.1'<span>); </span><span>define</span>('CACHE_PORT', 11211<span>); </span><span>class</span><span> Cache{ </span><span>private</span> <span>static</span> <span>$instance</span> = <span>null</span><span>; </span><span>private</span> <span>$_cache</span> = <span>null</span>; <span>//</span><span> 缓存操作对象</span> <span>const</span> prefix = 'APP|'; <span>//</span><span> 缓存键值的前缀,为该项目名称 </span> <span>private</span> <span>function</span><span> __construct(){ </span><span>if</span>(<span>$this</span>->_cache === <span>null</span><span>){ </span><span>try</span><span>{ </span><span>$this</span>->_cache = <span>new</span><span> Memcache; </span><span>if</span>(!<span>$this</span>->_cache->connect(CACHE_HOST,<span> CACHE_PORT)){ </span><span>exit</span>('connect failed'<span>); } } </span><span>catch</span>(<span>Exception</span> <span>$e</span><span>){ </span><span>echo</span> 'ERROR: '.<span>$e</span>-><span>getMessage(); } } } </span><span>/*</span><span>* * 返回单例 </span><span>*/</span> <span>public</span> <span>static</span> <span>function</span><span> getInstance(){ </span><span>if</span>(self::<span>$instance</span> === <span>null</span><span>){ self</span>::<span>$instance</span> = <span>new</span><span> self(); } </span><span>return</span> self::<span>$instance</span><span>; } </span><span>/*</span><span>* * 生成键名 </span><span>*/</span> <span>private</span> <span>function</span> genKey(<span>$key</span><span>){ </span><span>return</span> Cache::prefix.<span>$key</span><span>; } </span><span>public</span> <span>function</span><span> __destruct(){ </span><span>if</span>(<span>$this</span>->_cache !== <span>null</span><span>){ </span><span>return</span> <span>$this</span>->_cache-><span>close(); } } </span><span>/*</span><span>* * 添加元素,设置过期时间 </span><span>*/</span> <span>public</span> <span>function</span> add(<span>$key</span>, <span>$val</span>, <span>$expire</span> = 3600<span>){ </span><span>return</span> <span>$this</span>->_cache->add(<span>$this</span>->genKey(<span>$key</span>), <span>$val</span>, 0, <span>$expire</span><span>); } </span><span>/*</span><span>* * 重置元素 </span><span>*/</span> <span>public</span> <span>function</span> set(<span>$key</span>, <span>$val</span>, <span>$expire</span> = 3600<span>){ </span><span>echo</span> 'cache key: '.<span>$this</span>->genKey(<span>$key</span>).'<br/>'; <span>//</span><span> test</span> <span>return</span> <span>$this</span>->_cache->set(<span>$this</span>->genKey(<span>$key</span>), <span>$val</span>, 0, <span>$expire</span><span>); } </span><span>/*</span><span>* * 获取元素 </span><span>*/</span> <span>public</span> <span>function</span> get(<span>$key</span><span>){ </span><span>return</span> <span>$this</span>->_cache->get(<span>$this</span>->genKey(<span>$key</span><span>)); } </span><span>/*</span><span>* * 自增 </span><span>*/</span> <span>public</span> <span>function</span> increment(<span>$key</span>, <span>$val</span> = 1<span>){ </span><span>return</span> <span>$this</span>->_cache->increment(<span>$this</span>->genKey(<span>$key</span>), <span>$val</span><span>); } </span><span>/*</span><span>* * 自减 </span><span>*/</span> <span>public</span> <span>function</span> decrement(<span>$key</span>, <span>$val</span> = 1<span>){ </span><span>return</span> <span>$this</span>->_cache->decrement(<span>$this</span>->genKey(<span>$key</span>), <span>$val</span><span>); } </span><span>/*</span><span>* * 删除元素 </span><span>*/</span> <span>public</span> <span>function</span> delete(<span>$key</span>, <span>$timeout</span> = 0<span>){ </span><span>return</span> <span>$this</span>->_cache->delete(<span>$this</span>->genKey(<span>$key</span>), <span>$timeout</span><span>); } </span><span>/*</span><span>* * 删除所有元素 </span><span>*/</span> <span>public</span> <span>function</span> <span>flush</span><span>(){ </span><span>return</span> <span>$this</span>->_cache-><span>flush</span><span>(); } }</span>
Then define an operation database operation class:
<span>/*</span><span>* * 数据库操作 </span><span>*/</span> <span>define</span>('DB_NAME', 'test'<span>); </span><span>define</span>('DB_HOST', '127.0.0.1'<span>); </span><span>define</span>('DB_PORT', 3306<span>); </span><span>define</span>('DB_USER', 'root'<span>); </span><span>define</span>('DB_PASS', '1234'<span>); </span><span>class</span><span> Dal{ </span><span>private</span> <span>static</span> <span>$instance</span> = <span>null</span><span>; </span><span>public</span> <span>$pdo</span> = <span>null</span><span>; </span><span>private</span> <span>function</span><span> __construct(){ </span><span>try</span><span>{ </span><span>$dsn</span> = 'mysql:dbname='.DB_NAME.';host='.DB_HOST.':'.<span>DB_PORT; </span><span>$this</span>->pdo = <span>new</span> PDO(<span>$dsn</span>, DB_USER,<span> DB_PASS); } </span><span>catch</span>(PDOException <span>$e</span><span>){ </span><span>echo</span> 'Error: '.<span>$e</span>-><span>getMessage(); </span><span>return</span> <span>false</span><span>; } } </span><span>//</span><span>获取实例</span> <span>public</span> <span>static</span> <span>function</span><span> getInstance(){ </span><span>if</span>(self::<span>$instance</span> === <span>null</span><span>){ self</span>::<span>$instance</span> = <span>new</span><span> self(); } </span><span>return</span> self::<span>$instance</span><span>; } </span><span>//</span><span> 获取用户信息</span> <span>public</span> <span>function</span> getUserInfoByUid(<span>$uid</span><span>){ </span><span>$sql</span> = <span>sprintf</span>('select * from tab1 where uid = %s limit 1', <span>$uid</span><span>); </span><span>$stmt</span> = <span>$this</span>->pdo->query(<span>$sql</span><span>); </span><span>return</span> <span>$stmt</span>->fetch(PDO::<span>FETCH_ASSOC); } }</span>
Generally speaking, data is usually read and written in the Model model class, so define another UserModel class, try to be as simple as possible, and no longer write possible Model public base classes.
<span>include</span> 'Dal.php'<span>; </span><span>include</span> 'Cache.php'<span>; </span><span>class</span><span> UserModel{ </span><span>private</span> <span>static</span> <span>$instance</span> = <span>null</span><span>; </span><span>public</span> <span>$cache</span> = <span>null</span><span>; </span><span>public</span> <span>$db</span> = <span>null</span><span>; </span><span>//</span><span> 缓存键名部分,通过函数名称及参数拼接</span> <span>const</span> cache_get_userinfo_uid = 'GET|USER|INFO|BY|UID|%s'<span>; </span><span>private</span> <span>function</span><span> __construct(){ </span><span>//</span><span> 获取对应类实例</span> <span>$this</span>->cache = Cache::<span>getInstance(); </span><span>$this</span>->db = Dal::<span>getInstance(); } </span><span>public</span> <span>static</span> <span>function</span><span> getInstance(){ </span><span>if</span>(self::<span>$instance</span> === <span>null</span><span>){ self</span>::<span>$instance</span> = <span>new</span><span> self(); } </span><span>return</span> self::<span>$instance</span><span>; } </span><span>/*</span><span>* 根据uid获取用户信息 </span><span>*/</span> <span>public</span> <span>function</span> getUserInfoByUid(<span>$params</span>, <span>$timeout</span> = 3600<span>){ </span><span>//</span><span> 缺少必要参数uid,返回</span> <span>if</span>(<span>empty</span>(<span>$params</span>['uid'<span>])){ </span><span>return</span> <span>null</span><span>; } </span><span>//</span><span> 拼接缓存键名</span> <span>$key</span> = <span>sprintf</span>(self::cache_get_userinfo_uid, <span>$params</span>['uid'<span>]); </span><span>//</span><span> 获取缓存数据</span> <span>$data</span> = json_decode(<span>$this</span>->cache->get(<span>$key</span>), <span>true</span><span>); </span><span>echo</span> 'cache=><pre class="brush:php;toolbar:false">'<span>; </span><span>var_dump</span>(<span>$data</span><span>); </span><span>//</span><span> 缓存为空</span> <span>if</span>(!<span>$data</span><span>){ </span><span>$data</span> = <span>$this</span>->db->getUserInfoByUid(<span>$params</span>['uid'<span>]); </span><span>if</span>(<span>empty</span>(<span>$data</span><span>)){ </span><span>return</span> <span>null</span><span>; } </span><span>//</span><span> 在数据库中获取到数据后,重新写入缓存</span> <span>$this</span>->cache->set(<span>$key</span>, json_encode(<span>$data</span>), <span>$timeout</span><span>); } </span><span>else</span><span>{ } </span><span>return</span> <span>$data</span><span>; } } </span><span>//</span><span> 测试</span> <span>$data</span> = UserModel::getInstance()->getUserInfoByUid(<span>array</span>('uid'=>17653), 5<span>); </span><span>echo</span> 'UserModel=><pre class="brush:php;toolbar:false">'<span>; </span><span>var_dump</span>(<span>$data</span>);
Here, we want to obtain a user's information through the user's uid. The query condition is uid, and the necessary parameter passed is uid. Originally, we want to cache each user's data in order to make repeated queries faster. , so you can consider using uid when defining cache keys. But just using this uid is not enough, because if there are queries based on uid in other tables, there will be duplicate key names, causing data confusion. So we use the getUserInfoByUid method, and you can also spell the method name in, so in In the UserModel class, a constant cache_get_userinfo_uid is defined, and the %s at the end is to splice the parameter uid. However, this may still not work. Now the company has started another project, which also needs to use this table. The method written by someone has exactly the same name as this method. After all, the function name is not patented, and operation and maintenance usually uses the server separately as a function. When using cache, several projects share a server, and it is normal for data to be written in a cache pool. At this time, you can consider adding a prefix---the project name (or project). Therefore, in the Cache class, it is defined A constant prefix is used to call this project APP, which basically guarantees that there will be no conflicts. Of course, if this is a sub-project of a group, and this group has other projects, and the group is called Star, you can add the project group name in front.
Look at the effect. The first time (left) the read cache is empty, but you can see the printed cache key name,
The second time (on the right) there is data in the cache, because I put the place where the cache key name is printed in the set method of the Cache class. The second time I read the cache data directly without connecting to the database, so naturally there is no Reset the cache, so the cache key is not printed.
It took me a long time to ensure that the key names do not conflict. It also included how to use cache in a project. There are several methods for reference:
1. The current parameters can be spliced together. Here is only one uid. If there are names, ages, etc., they can be placed at the back. How to find out specifically depends on personal preferences or code specifications;
2. The current method name, as close as possible to the current method name, is spelled in the middle of the cache key;
3. Use the current project name as a prefix (optional), which is safer;
4. The name of the project group is spelled in as a prefix again (generally not used).
Then, in the Model class, get the cache first, and if it cannot be retrieved, read the database. After reading, don’t forget to write to the cache, and set the expiration time and other details. It’s just such a process.
Of course, you can do it more carefully. For example, after reading the cached data, check its expiration time and find that it will expire in 5 seconds, so we reset it to 3000 seconds, like this It can be obtained during the first access, so that the database will not be read again next time. Frequently connecting to the database is very time-consuming.
However, I still think Redis is easy to use. The Hash table in Redis is the best>-_-<, but it happens to be not available on the machine.
Hmm...it's late, go to bed~=_=