网站首页大小,wordpress 关闭工具栏,怎样登陆wordpress,下载并安装app菜菜#xff0c;咱们网站现在有多少PV和UV了#xff1f;Y总#xff0c;咱们没有统计pv和uv的系统#xff0c;预估大约有一千万uv吧写一个统计uv和pv的系统吧网上有现成的#xff0c;直接接入一个不行吗#xff1f;别人的不太放心#xff0c;毕竟自己写的#xff0c;自己… 菜菜咱们网站现在有多少PV和UV了Y总咱们没有统计pv和uv的系统预估大约有一千万uv吧写一个统计uv和pv的系统吧网上有现成的直接接入一个不行吗别人的不太放心毕竟自己写的自己拥有主动权。给你两天时间系统性能不要太差呀好吧~~~定义PV是page view的缩写即页面浏览量通常是衡量一个网络新闻频道或网站甚至一条网络新闻的主要指标。网页浏览数是评价网站流量最常用的指标之一简称为PVUV是unique visitor的简写是指通过互联网访问、浏览这个网页的自然人。通过以上的概念可以清晰的看出pv是比较好设计的网站的每一次被访问pv都会增加但是uv就不一定会增加了uv本质上记录的是按照某个标准划分的自然人这个标准其实我们可以自己去定义比如可以定义同一个IP的访问者为同一个UV这也是最常见的uv定义之一另外还有根据cookie定义等等。无论是pv还是uv都需要一个时间段来加以描述平时我们所说的pvuv数量指的都是24小时之内一个自然日的数据。pv相比较uv来说技术上比较容易一些今天咱们就来说一说uv的统计为什么说uv的统计相对来说比较难呢因为uv涉及到同一个标准下的自然人的去重尤其是一个uv千万级别的网站设计一个好的uv统计系统也许并非想象的那么容易。那我们就来设计一个以一个自然日为时间段的uv统计系统一个自然人uv的定义为同一个来源IP当然你也可以自定义其他标准数据量级别假设为每日千万uv的量级。注意今天我们讨论的重点是获取到自然人定义的信息之后如何设计uv统计系统并非是如何获取自然人的定义。uv系统的设计并非想象的那么简单因为uv可能随着网站的营销策略会出现瞬间大流量比如网站举办了一个秒杀活动。基于DB方案服务端编程有一句名言曰没有一个表解决不了的功能如果有那就两个表三个表。一个uv统计系统确实可以基于数据库来实现而且也不复杂uv统计的记录表可以类似如下不要太纠结以下表设计是否合理字段类型描述IPvarchar(30)客户端来源ipDayIDint时间的简写例如 20190629其他字段int其他字段描述当一个请求到达服务器服务端每次需要查询一次数据库是否有当前IP和当前时间的访问记录如果有则说明是同一个uv如果没有则说明是新的uv记录插入数据库。当然以上两步也可以写到一个sql语句中if exists( select 1 from table where ipip and dayiddayid ) Begin return 0 Endelse Begin insert into table ....... End1 from table where ipip and dayiddayid ) Begin return 0 Endelse Begin insert into table ....... End所有基于数据库的解决方案在数据量大的情况下几乎都更容易出现瓶颈。面对每天千万级别的uv统计基于数据库的这种方案也许并不是最优的。优化方案面对每一个系统的设计我们都应该沉下心来思考具体的业务。至于uv统计这个业务有几个特点1. 每次请求都需要判断是否已经存在相同的uv记录2. 持久化uv数据不能影响正常的业务3. uv数据的准确性可以忍受一定程度的误差哈希表基于数据库的方案中在大数据量的情况下性能的瓶颈引发原因之一就是判断是否已经存在相同记录所以要优化这个系统肯定首先是要优化这个步骤。根据菜菜以前的文章是否可以想到解决这个问题的数据结构对就是哈希表。哈希表根据key来查找value的时间复杂度为O1常数级别可以完美的解决查找相同记录的操作瓶颈。也许在uv数据量比较小的时候哈希表也许是个不错的选择但是面对千万级别的uv数据量哈希表的哈希冲突和扩容以及哈希表占用的内存也许并不是好的选择了。假设哈希表的每个key和value 占用10字节1千万的uv数据大约占用 100M对于现代计算机来说100M其实不算大但是有没有更好的方案呢优化哈希表基于哈希表的方案在千万级别数据量的情况下只能算是勉强应付如果是10亿的数据量呢那有没有更好的办法搞定10亿级数据量的uv统计呢这里抛开持久化数据因为持久化设计到数据库的分表分库等优化策略了咱们以后再谈。有没有更好的办法去快速判断在10亿级别的uv中某条记录是否存在呢为了尽量缩小使用的内存我们可以这样设计可以预先分配bit类型的数组数组的大小是统计的最大数据量的一个倍数这个倍数可以自定义调整。现在假设系统的uv最大数据量为1千万系统可以预先分配一个长度为5千万的bit数组bit占用的内存最小只占用一位。按照一个哈希冲突比较小的哈希函数计算每一个数据的哈希值并设置bit数组相应哈希值位置的值为1。由于哈希函数都有冲突有可能不同的数据会出现相同的哈希值出现误判但是我们可以用多个不同的哈希函数来计算同一个数据来产生不同的哈希值同时把这多个哈希值的数组位置都设置为1从而大大减少了误判率刚才新建的数组为最大数据量的一个倍数也是为了减小冲突的一种方式容量越大冲突越小。当一个1千万的uv数据量级5千万的bit数组占用内存才几十M而已比哈希表要小很多在10亿级别下内存占用差距将会更大。以下为代码示例class BloomFilter { BitArray container null; public BloomFilter(int length) { container new BitArray(length); } public void Set(string key) { var h1 Hash1(key); var h2 Hash2(key); var h3 Hash3(key); var h4 Hash4(key); container[h1] true; container[h2] true; container[h3] true; container[h4] true; } public bool Get(string key) { var h1 Hash1(key); var h2 Hash2(key); var h3 Hash3(key); var h4 Hash4(key); return container[h1] container[h2] container[h3] container[h4]; } //模拟哈希函数1 int Hash1(string key) { int hash 5381; int i; int count; char[] bitarray key.ToCharArray(); count bitarray.Length; while (count 0) { hash (hash 5) (bitarray[bitarray.Length - count]); count--; } return (hash 0x7FFFFFFF) % container.Length; } int Hash2(string key) { int seed 131; // 31 131 1313 13131 131313 etc.. int hash 0; int count; char[] bitarray (keykey2).ToCharArray(); count bitarray.Length; while (count 0) { hash hash * seed (bitarray[bitarray.Length - count]); count--; } return (hash 0x7FFFFFFF)% container.Length; } int Hash3(string key) { int hash 0; int i; int count; char[] bitarray (key keykey3).ToCharArray(); count bitarray.Length; for (i 0; i count; i) { if ((i 1) 0) { hash ^ ((hash 7) ^ (bitarray[i]) ^ (hash 3)); } else { hash ^ (~((hash 11) ^ (bitarray[i]) ^ (hash 5))); } count--; } return (hash 0x7FFFFFFF) % container.Length; } int Hash4(string key) { int hash 5381; int i; int count; char[] bitarray (key keykeyke4).ToCharArray(); count bitarray.Length; while (count 0) { hash (hash 5) (bitarray[bitarray.Length - count]); count--; } return (hash 0x7FFFFFFF) % container.Length; } }BloomFilter { BitArray container null; public BloomFilter(int length) { container new BitArray(length); } public void Set(string key) { var h1 Hash1(key); var h2 Hash2(key); var h3 Hash3(key); var h4 Hash4(key); container[h1] true; container[h2] true; container[h3] true; container[h4] true; } public bool Get(string key) { var h1 Hash1(key); var h2 Hash2(key); var h3 Hash3(key); var h4 Hash4(key); return container[h1] container[h2] container[h3] container[h4]; } //模拟哈希函数1 int Hash1(string key) { int hash 5381; int i; int count; char[] bitarray key.ToCharArray(); count bitarray.Length; while (count 0) { hash (hash 5) (bitarray[bitarray.Length - count]); count--; } return (hash 0x7FFFFFFF) % container.Length; } int Hash2(string key) { int seed 131; // 31 131 1313 13131 131313 etc.. int hash 0; int count; char[] bitarray (keykey2).ToCharArray(); count bitarray.Length; while (count 0) { hash hash * seed (bitarray[bitarray.Length - count]); count--; } return (hash 0x7FFFFFFF)% container.Length; } int Hash3(string key) { int hash 0; int i; int count; char[] bitarray (key keykey3).ToCharArray(); count bitarray.Length; for (i 0; i count; i) { if ((i 1) 0) { hash ^ ((hash 7) ^ (bitarray[i]) ^ (hash 3)); } else { hash ^ (~((hash 11) ^ (bitarray[i]) ^ (hash 5))); } count--; } return (hash 0x7FFFFFFF) % container.Length; } int Hash4(string key) { int hash 5381; int i; int count; char[] bitarray (key keykeyke4).ToCharArray(); count bitarray.Length; while (count 0) { hash (hash 5) (bitarray[bitarray.Length - count]); count--; } return (hash 0x7FFFFFFF) % container.Length; } }测试程序为BloomFilter bf new BloomFilter(200000000); int exsitNumber 0; int noExsitNumber 0; for (int i0;i 10000000; i) { string key $ip_{i}; var isExsit bf.Get(key); if (isExsit) { exsitNumber 1; } else { bf.Set(key); noExsitNumber 1; } } Console.WriteLine($判断存在的数据量{exsitNumber}); Console.WriteLine($判断不存在的数据量{noExsitNumber});200000000); int exsitNumber 0; int noExsitNumber 0; for (int i0;i 10000000; i) { string key $ip_{i}; var isExsit bf.Get(key); if (isExsit) { exsitNumber 1; } else { bf.Set(key); noExsitNumber 1; } } Console.WriteLine($判断存在的数据量{exsitNumber}); Console.WriteLine($判断不存在的数据量{noExsitNumber}); 测试结果判断存在的数据量7017判断不存在的数据量9992983占用内存40M误判率不到 千分之1在这个业务场景下在可接受范围之内。在真正的业务当中系统并不会在启动之初就分配这么大的bit数组而是随着冲突增多慢慢扩容到一定容量的。异步优化当判断一个数据是否已经存在这个过程解决之后下一个步骤就是把数据持久化到DB如果数据量较大或者瞬间数据量较大可以考虑使用mq或者读写IO比较大的NOSql来代替直接插入关系型数据库。思路一转整个的uv流程其实也都可以异步化而且也推荐这么做。完●程序员过关斩将--你为什么还在用存储过程●程序员过关斩将--小小的分页引发的加班血案●程序员修神之路--问世间异步为何物●程序员修神之路--提高网站的吞吐量?●程序员修神之路--?分布式高并发下Actor模型如此优秀?●程序员过关斩将--论商品促销代码的优雅性●程序员过关斩将--你的面向接口编程一定对吗●程序员修神之路--高并发下为什么更喜欢进程内缓存●程序员修神之路--高并发优雅的做限流